aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2022-06-19 05:52:19 +0200
committerSven Gothel <[email protected]>2022-06-19 05:52:19 +0200
commit4a3f09a9fb5f836f74a1bd083b82caf5edf95fa4 (patch)
treef96545f530c60fb93b823b83baa86c7f937f48ac /include
parentfa440d41eaad783c80a169bc5a1c46c7f0ab05d5 (diff)
Catch2: Update to v3.0.1 Generated: 2022-05-17
Diffstat (limited to 'include')
-rw-r--r--include/catch2/catch_amalgamated.cpp4430
-rw-r--r--include/catch2/catch_amalgamated.hpp4841
2 files changed, 5715 insertions, 3556 deletions
diff --git a/include/catch2/catch_amalgamated.cpp b/include/catch2/catch_amalgamated.cpp
index 3053887..bd8c811 100644
--- a/include/catch2/catch_amalgamated.cpp
+++ b/include/catch2/catch_amalgamated.cpp
@@ -5,8 +5,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.0.0-preview.3
-// Generated: 2020-10-08 13:59:26.616931
+// Catch v3.0.1
+// Generated: 2022-05-17 22:08:47.054486
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -20,6 +20,7 @@
#include <cassert>
+#include <cstddef>
#include <iterator>
#include <random>
@@ -33,8 +34,8 @@ namespace {
using Catch::Benchmark::Detail::sample;
template <typename URng, typename Estimator>
- sample resample(URng& rng, int resamples, std::vector<double>::iterator first, std::vector<double>::iterator last, Estimator& estimator) {
- auto n = last - first;
+ sample resample(URng& rng, unsigned int resamples, std::vector<double>::iterator first, std::vector<double>::iterator last, Estimator& estimator) {
+ auto n = static_cast<size_t>(last - first);
std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
sample out;
@@ -42,7 +43,7 @@ using Catch::Benchmark::Detail::sample;
std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
std::vector<double> resampled;
resampled.reserve(n);
- std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; });
+ std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[static_cast<std::ptrdiff_t>(dist(rng))]; });
return estimator(resampled.begin(), resampled.end());
});
std::sort(out.begin(), out.end());
@@ -127,11 +128,15 @@ using Catch::Benchmark::Detail::sample;
double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
auto m = Catch::Benchmark::Detail::mean(first, last);
- double variance = std::accumulate(first, last, 0., [m](double a, double b) {
- double diff = b - m;
- return a + diff * diff;
- }) / (last - first);
- return std::sqrt(variance);
+ double variance = std::accumulate( first,
+ last,
+ 0.,
+ [m]( double a, double b ) {
+ double diff = b - m;
+ return a + diff * diff;
+ } ) /
+ ( last - first );
+ return std::sqrt( variance );
}
}
@@ -140,6 +145,15 @@ namespace Catch {
namespace Benchmark {
namespace Detail {
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ bool directCompare( double lhs, double rhs ) { return lhs == rhs; }
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic pop
+#endif
+
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
auto count = last - first;
double idx = (count - 1) * k / static_cast<double>(q);
@@ -147,7 +161,9 @@ namespace Catch {
double g = idx - j;
std::nth_element(first, first + j, last);
auto xj = first[j];
- if (g == 0) return xj;
+ if ( directCompare( g, 0 ) ) {
+ return xj;
+ }
auto xj1 = *std::min_element(first + (j + 1), last);
return xj + g * (xj1 - xj);
@@ -179,7 +195,7 @@ namespace Catch {
double sb = stddev.point;
double mn = mean.point / n;
double mg_min = mn / 2.;
- double sg = std::min(mg_min / 4., sb / std::sqrt(n));
+ double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
double sg2 = sg * sg;
double sb2 = sb * sb;
@@ -190,7 +206,7 @@ namespace Catch {
double k0 = -n * nd;
double k1 = sb2 - n * sg2 + nd;
double det = k1 * k1 - 4 * sg2 * k0;
- return (int)(-2. * k0 / (k1 + std::sqrt(det)));
+ return static_cast<int>(-2. * k0 / (k1 + std::sqrt(det)));
};
auto var_out = [n, sb2, sg2](double c) {
@@ -198,11 +214,11 @@ namespace Catch {
return (nc / n) * (sb2 - nc * sg2);
};
- return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2;
+ return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
}
- bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
+ bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
static std::random_device entropy;
@@ -289,25 +305,6 @@ namespace Catch {
} // namespace Catch
-////////////////////////////////////////////////
-// vvv formerly catch_complete_invoke.cpp vvv //
-////////////////////////////////////////////////
-
-
-namespace Catch {
- namespace Benchmark {
- namespace Detail {
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
- const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
- } // namespace Detail
- } // namespace Benchmark
-} // namespace Catch
-
-
-
-
/////////////////////////////////////////////////
// vvv formerly catch_run_for_at_least.cpp vvv //
/////////////////////////////////////////////////
@@ -351,7 +348,7 @@ bool marginComparison(double lhs, double rhs, double margin) {
namespace Catch {
Approx::Approx ( double value )
- : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+ : m_epsilon( std::numeric_limits<float>::epsilon()*100. ),
m_margin( 0.0 ),
m_scale( 0.0 ),
m_value( value )
@@ -495,7 +492,7 @@ namespace Catch {
: expr;
}
- std::string AssertionResult::getMessage() const {
+ StringRef AssertionResult::getMessage() const {
return m_resultData.message;
}
SourceLineInfo AssertionResult::getSourceInfo() const {
@@ -510,13 +507,18 @@ namespace Catch {
-
namespace Catch {
- Config::Config( ConfigData const& data )
- : m_data( data ),
- m_stream( openStream() )
- {
+ bool operator==( ProcessedReporterSpec const& lhs,
+ ProcessedReporterSpec const& rhs ) {
+ return lhs.name == rhs.name &&
+ lhs.outputFilename == rhs.outputFilename &&
+ lhs.colourMode == rhs.colourMode &&
+ lhs.customOptions == rhs.customOptions;
+ }
+
+ Config::Config( ConfigData const& data ):
+ m_data( data ) {
// We need to trim filter specs to avoid trouble with superfluous
// whitespace (esp. important for bdd macros, as those are manually
// aligned with whitespace).
@@ -528,6 +530,7 @@ namespace Catch {
elem = trim(elem);
}
+
TestSpecParser parser(ITagAliasRegistry::get());
if (!m_data.testsOrTags.empty()) {
m_hasTestFilters = true;
@@ -536,25 +539,86 @@ namespace Catch {
}
}
m_testSpec = parser.testSpec();
- }
- Config::~Config() = default;
+ // Insert the default reporter if user hasn't asked for a specfic one
+ if ( m_data.reporterSpecifications.empty() ) {
+ m_data.reporterSpecifications.push_back( {
+#if defined( CATCH_CONFIG_DEFAULT_REPORTER )
+ CATCH_CONFIG_DEFAULT_REPORTER,
+#else
+ "console",
+#endif
+ {}, {}, {}
+ } );
+ }
+
+#if defined( CATCH_CONFIG_BAZEL_SUPPORT )
+ // Register a JUnit reporter for Bazel. Bazel sets an environment
+ // variable with the path to XML output. If this file is written to
+ // during test, Bazel will not generate a default XML output.
+ // This allows the XML output file to contain higher level of detail
+ // than what is possible otherwise.
+# if defined( _MSC_VER )
+ // On Windows getenv throws a warning as there is no input validation,
+ // since the key is hardcoded, this should not be an issue.
+# pragma warning( push )
+# pragma warning( disable : 4996 )
+# endif
+ const auto bazelOutputFilePtr = std::getenv( "XML_OUTPUT_FILE" );
+# if defined( _MSC_VER )
+# pragma warning( pop )
+# endif
+ if ( bazelOutputFilePtr != nullptr ) {
+ m_data.reporterSpecifications.push_back(
+ { "junit", std::string( bazelOutputFilePtr ), {}, {} } );
+ }
+#endif
+
+
+ // We now fixup the reporter specs to handle default output spec,
+ // default colour spec, etc
+ bool defaultOutputUsed = false;
+ for ( auto const& reporterSpec : m_data.reporterSpecifications ) {
+ // We do the default-output check separately, while always
+ // using the default output below to make the code simpler
+ // and avoid superfluous copies.
+ if ( reporterSpec.outputFile().none() ) {
+ CATCH_ENFORCE( !defaultOutputUsed,
+ "Internal error: cannot use default output for "
+ "multiple reporters" );
+ defaultOutputUsed = true;
+ }
- std::string const& Config::getFilename() const {
- return m_data.outputFilename ;
+ m_processedReporterSpecs.push_back( ProcessedReporterSpec{
+ reporterSpec.name(),
+ reporterSpec.outputFile() ? *reporterSpec.outputFile()
+ : data.defaultOutputFilename,
+ reporterSpec.colourMode().valueOr( data.defaultColourMode ),
+ reporterSpec.customOptions() } );
+ }
}
+ Config::~Config() = default;
+
+
bool Config::listTests() const { return m_data.listTests; }
bool Config::listTags() const { return m_data.listTags; }
bool Config::listReporters() const { return m_data.listReporters; }
-
- std::string Config::getProcessName() const { return m_data.processName; }
- std::string const& Config::getReporterName() const { return m_data.reporterName; }
+ bool Config::listListeners() const { return m_data.listListeners; }
std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
+ std::vector<ReporterSpec> const& Config::getReporterSpecs() const {
+ return m_data.reporterSpecifications;
+ }
+
+ std::vector<ProcessedReporterSpec> const&
+ Config::getProcessedReporterSpecs() const {
+ return m_processedReporterSpecs;
+ }
+
TestSpec const& Config::testSpec() const { return m_testSpec; }
bool Config::hasTestFilters() const { return m_hasTestFilters; }
@@ -562,31 +626,34 @@ namespace Catch {
// IConfig interface
bool Config::allowThrows() const { return !m_data.noThrow; }
- std::ostream& Config::stream() const { return m_stream->stream(); }
- std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
- bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); }
- bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); }
+ bool Config::warnAboutMissingAssertions() const {
+ return !!( m_data.warnings & WarnAbout::NoAssertions );
+ }
+ bool Config::warnAboutUnmatchedTestSpecs() const {
+ return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec );
+ }
+ bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; }
ShowDurations Config::showDurations() const { return m_data.showDurations; }
double Config::minDuration() const { return m_data.minDuration; }
TestRunOrder Config::runOrder() const { return m_data.runOrder; }
- unsigned int Config::rngSeed() const { return m_data.rngSeed; }
- UseColour Config::useColour() const { return m_data.useColour; }
+ uint32_t Config::rngSeed() const { return m_data.rngSeed; }
+ unsigned int Config::shardCount() const { return m_data.shardCount; }
+ unsigned int Config::shardIndex() const { return m_data.shardIndex; }
+ ColourMode Config::defaultColourMode() const { return m_data.defaultColourMode; }
bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; }
int Config::abortAfter() const { return m_data.abortAfter; }
bool Config::showInvisibles() const { return m_data.showInvisibles; }
Verbosity Config::verbosity() const { return m_data.verbosity; }
+ bool Config::skipBenchmarks() const { return m_data.skipBenchmarks; }
bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
- int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
+ unsigned int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
- IStream const* Config::openStream() {
- return Catch::makeStream(m_data.outputFilename);
- }
-
} // end namespace Catch
@@ -598,13 +665,6 @@ namespace Catch {
////////////////////////////////////////////////////////////////////////////
- Catch::MessageBuilder::MessageBuilder( StringRef const& macroName,
- SourceLineInfo const& lineInfo,
- ResultWas::OfType type )
- :m_info(macroName, lineInfo, type) {}
-
- ////////////////////////////////////////////////////////////////////////////
-
ScopedMessage::ScopedMessage( MessageBuilder const& builder ):
m_info( builder.m_info ) {
@@ -613,7 +673,7 @@ namespace Catch {
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
- m_info( std::move( old.m_info ) ) {
+ m_info( CATCH_MOVE( old.m_info ) ) {
old.m_moved = true;
}
@@ -729,16 +789,16 @@ namespace Catch {
public: // IMutableRegistryHub
void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override {
- m_reporterRegistry.registerReporter( name, std::move(factory) );
+ m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) );
}
- void registerListener( IReporterFactoryPtr factory ) override {
- m_reporterRegistry.registerListener( std::move(factory) );
+ void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) override {
+ m_reporterRegistry.registerListener( CATCH_MOVE(factory) );
}
void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker ) override {
- m_testCaseRegistry.registerTest( std::move(testInfo), std::move(invoker) );
+ m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) );
}
- void registerTranslator( const IExceptionTranslator* translator ) override {
- m_exceptionTranslatorRegistry.registerTranslator( translator );
+ void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) override {
+ m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) );
}
void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
m_tagAliasRegistry.add( alias, tag, lineInfo );
@@ -786,6 +846,7 @@ namespace Catch {
#include <algorithm>
+#include <cassert>
#include <iomanip>
#include <set>
@@ -794,58 +855,78 @@ namespace Catch {
namespace {
const int MaxExitCode = 255;
- IStreamingReporterPtr createReporter(std::string const& reporterName, IConfig const* config) {
- auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
- CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
+ IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) {
+ auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config));
+ CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << '\'');
return reporter;
}
- IStreamingReporterPtr makeReporter(Config const* config) {
- if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) {
- return createReporter(config->getReporterName(), config);
+ IEventListenerPtr prepareReporters(Config const* config) {
+ if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()
+ && config->getProcessedReporterSpecs().size() == 1) {
+ auto const& spec = config->getProcessedReporterSpecs()[0];
+ return createReporter(
+ spec.name,
+ ReporterConfig( config,
+ makeStream( spec.outputFilename ),
+ spec.colourMode,
+ spec.customOptions ) );
}
- // On older platforms, returning unique_ptr<ListeningReporter>
- // when the return type is unique_ptr<IStreamingReporter>
- // doesn't compile without a std::move call. However, this causes
- // a warning on newer platforms. Thus, we have to work around
- // it a bit and downcast the pointer manually.
- auto ret = Detail::unique_ptr<IStreamingReporter>(new ListeningReporter);
- auto& multi = static_cast<ListeningReporter&>(*ret);
+ auto multi = Detail::make_unique<MultiReporter>(config);
+
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
for (auto const& listener : listeners) {
- multi.addListener(listener->create(Catch::ReporterConfig(config)));
+ multi->addListener(listener->create(config));
}
- multi.addReporter(createReporter(config->getReporterName(), config));
- return ret;
+
+ std::size_t reporterIdx = 0;
+ for ( auto const& reporterSpec : config->getProcessedReporterSpecs() ) {
+ multi->addReporter( createReporter(
+ reporterSpec.name,
+ ReporterConfig( config,
+ makeStream( reporterSpec.outputFilename ),
+ reporterSpec.colourMode,
+ reporterSpec.customOptions ) ) );
+ reporterIdx++;
+ }
+
+ return multi;
}
class TestGroup {
public:
- explicit TestGroup(IStreamingReporterPtr&& reporter, Config const* config):
+ explicit TestGroup(IEventListenerPtr&& reporter, Config const* config):
m_reporter(reporter.get()),
m_config{config},
- m_context{config, std::move(reporter)} {
+ m_context{config, CATCH_MOVE(reporter)} {
- auto const& allTestCases = getAllTestCasesSorted(*m_config);
- m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
- auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
+ assert( m_config->testSpec().getInvalidSpecs().empty() &&
+ "Invalid test specs should be handled before running tests" );
- if (m_matches.empty() && invalidArgs.empty()) {
- for (auto const& test : allTestCases)
- if (!test.getTestCaseInfo().isHidden())
- m_tests.emplace(&test);
+ auto const& allTestCases = getAllTestCasesSorted(*m_config);
+ auto const& testSpec = m_config->testSpec();
+ if ( !testSpec.hasFilters() ) {
+ for ( auto const& test : allTestCases ) {
+ if ( !test.getTestCaseInfo().isHidden() ) {
+ m_tests.emplace( &test );
+ }
+ }
} else {
- for (auto const& match : m_matches)
- m_tests.insert(match.tests.begin(), match.tests.end());
+ m_matches =
+ testSpec.matchesByFilter( allTestCases, *m_config );
+ for ( auto const& match : m_matches ) {
+ m_tests.insert( match.tests.begin(),
+ match.tests.end() );
+ }
}
+
+ m_tests = createShard(m_tests, m_config->shardCount(), m_config->shardIndex());
}
Totals execute() {
- auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
Totals totals;
- m_context.testGroupStarting(m_config->name(), 1, 1);
for (auto const& testCase : m_tests) {
if (!m_context.aborting())
totals += m_context.runTest(*testCase);
@@ -855,28 +936,26 @@ namespace Catch {
for (auto const& match : m_matches) {
if (match.tests.empty()) {
- m_reporter->noMatchingTestCases(match.name);
- totals.error = -1;
+ m_unmatchedTestSpecs = true;
+ m_reporter->noMatchingTestCases( match.name );
}
}
- if (!invalidArgs.empty()) {
- for (auto const& invalidArg: invalidArgs)
- m_reporter->reportInvalidArguments(invalidArg);
- }
-
- m_context.testGroupEnded(m_config->name(), totals, 1, 1);
return totals;
}
- private:
- using Tests = std::set<TestCaseHandle const*>;
+ bool hadUnmatchedTestSpecs() const {
+ return m_unmatchedTestSpecs;
+ }
+
- IStreamingReporter* m_reporter;
+ private:
+ IEventListener* m_reporter;
Config const* m_config;
RunContext m_context;
- Tests m_tests;
+ std::set<TestCaseHandle const*> m_tests;
TestSpec::Matches m_matches;
+ bool m_unmatchedTestSpecs = false;
};
void applyFilenamesAsTags() {
@@ -902,14 +981,17 @@ namespace Catch {
getCurrentMutableContext().setConfig(m_config.get());
m_startupExceptions = true;
- Colour colourGuard( Colour::Red );
- Catch::cerr() << "Errors occurred during startup!" << '\n';
+ auto errStream = makeStream( "%stderr" );
+ auto colourImpl = makeColourImpl(
+ ColourMode::PlatformDefault, errStream.get() );
+ auto guard = colourImpl->guardColour( Colour::Red );
+ errStream->stream() << "Errors occurred during startup!" << '\n';
// iterate over all exceptions and notify user
for ( const auto& ex_ptr : exceptions ) {
try {
std::rethrow_exception(ex_ptr);
} catch ( std::exception const& ex ) {
- Catch::cerr() << TextFlow::Column( ex.what() ).indent(2) << '\n';
+ errStream->stream() << TextFlow::Column( ex.what() ).indent(2) << '\n';
}
}
}
@@ -924,16 +1006,16 @@ namespace Catch {
void Session::showHelp() const {
Catch::cout()
- << "\nCatch v" << libraryVersion() << "\n"
- << m_cli << std::endl
- << "For more detailed usage please see the project docs\n" << std::endl;
+ << "\nCatch2 v" << libraryVersion() << '\n'
+ << m_cli << '\n'
+ << "For more detailed usage please see the project docs\n\n" << std::flush;
}
void Session::libIdentify() {
Catch::cout()
<< std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
<< std::left << std::setw(16) << "category: " << "testframework\n"
- << std::left << std::setw(16) << "framework: " << "Catch Test\n"
- << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
+ << std::left << std::setw(16) << "framework: " << "Catch2\n"
+ << std::left << std::setw(16) << "version: " << libraryVersion() << '\n' << std::flush;
}
int Session::applyCommandLine( int argc, char const * const * argv ) {
@@ -941,15 +1023,19 @@ namespace Catch {
return 1;
auto result = m_cli.parse( Clara::Args( argc, argv ) );
+
if( !result ) {
config();
getCurrentMutableContext().setConfig(m_config.get());
- Catch::cerr()
- << Colour( Colour::Red )
+ auto errStream = makeStream( "%stderr" );
+ auto colour = makeColourImpl( ColourMode::PlatformDefault, errStream.get() );
+
+ errStream->stream()
+ << colour->guardColour( Colour::Red )
<< "\nError(s) in input:\n"
<< TextFlow::Column( result.errorMessage() ).indent( 2 )
<< "\n\n";
- Catch::cerr() << "Run with -? for usage\n" << std::endl;
+ errStream->stream() << "Run with -? for usage\n\n" << std::flush;
return MaxExitCode;
}
@@ -957,6 +1043,7 @@ namespace Catch {
showHelp();
if( m_configData.libIdentify )
libIdentify();
+
m_config.reset();
return 0;
}
@@ -992,12 +1079,12 @@ namespace Catch {
int Session::run() {
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
- Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
+ Catch::cout() << "...waiting for enter/ return before starting\n" << std::flush;
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 << std::endl;
+ Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
static_cast<void>(std::getchar());
}
return exitCode;
@@ -1026,6 +1113,14 @@ namespace Catch {
return 0;
}
+ if ( m_configData.shardIndex >= m_configData.shardCount ) {
+ Catch::cerr() << "The shard count (" << m_configData.shardCount
+ << ") must be greater than the shard index ("
+ << m_configData.shardIndex << ")\n"
+ << std::flush;
+ return 1;
+ }
+
CATCH_TRY {
config(); // Force config to be constructed
@@ -1039,27 +1134,43 @@ namespace Catch {
getCurrentMutableContext().setConfig(m_config.get());
// Create reporter(s) so we can route listings through them
- auto reporter = makeReporter(m_config.get());
+ auto reporter = prepareReporters(m_config.get());
+
+ auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs();
+ if ( !invalidSpecs.empty() ) {
+ for ( auto const& spec : invalidSpecs ) {
+ reporter->reportInvalidTestSpec( spec );
+ }
+ return 1;
+ }
+
// Handle list request
if (list(*reporter, *m_config)) {
return 0;
}
- TestGroup tests { std::move(reporter), m_config.get() };
+ TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
auto const totals = tests.execute();
- if( m_config->warnAboutNoTests() && totals.error == -1 )
+ if ( tests.hadUnmatchedTestSpecs()
+ && m_config->warnAboutUnmatchedTestSpecs() ) {
+ return 3;
+ }
+
+ if ( totals.testCases.total() == 0
+ && !m_config->zeroTestsCountAsSuccess() ) {
return 2;
+ }
// Note that on unices only the lower 8 bits are usually used, clamping
// the return value to 255 prevents false negative when some multiple
// of 256 tests has failed
- return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));
+ return (std::min) (MaxExitCode, static_cast<int>(totals.assertions.failed));
}
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
catch( std::exception& ex ) {
- Catch::cerr() << ex.what() << std::endl;
+ Catch::cerr() << ex.what() << '\n' << std::flush;
return MaxExitCode;
}
#endif
@@ -1117,7 +1228,7 @@ namespace Catch {
else if( tag == "!nonportable"_sr )
return TestCaseProperties::NonPortable;
else if( tag == "!benchmark"_sr )
- return static_cast<TestCaseProperties>(TestCaseProperties::Benchmark | TestCaseProperties::IsHidden );
+ return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden;
else
return TestCaseProperties::None;
}
@@ -1159,16 +1270,25 @@ namespace Catch {
const size_t extras = 3 + 3;
return extractFilenamePart(filepath).size() + extras;
}
+ } // end unnamed namespace
+
+ bool operator<( Tag const& lhs, Tag const& rhs ) {
+ Detail::CaseInsensitiveLess cmp;
+ return cmp( lhs.original, rhs.original );
+ }
+ bool operator==( Tag const& lhs, Tag const& rhs ) {
+ Detail::CaseInsensitiveEqualTo cmp;
+ return cmp( lhs.original, rhs.original );
}
Detail::unique_ptr<TestCaseInfo>
- makeTestCaseInfo(std::string const& _className,
+ makeTestCaseInfo(StringRef _className,
NameAndTags const& nameAndTags,
SourceLineInfo const& _lineInfo ) {
- return Detail::unique_ptr<TestCaseInfo>(new TestCaseInfo(_className, nameAndTags, _lineInfo));
+ return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo);
}
- TestCaseInfo::TestCaseInfo(std::string const& _className,
+ TestCaseInfo::TestCaseInfo(StringRef _className,
NameAndTags const& _nameAndTags,
SourceLineInfo const& _lineInfo):
name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ),
@@ -1180,7 +1300,6 @@ namespace Catch {
// (including optional hidden tag and filename tag)
auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file);
backingTags.reserve(requiredSize);
- backingLCaseTags.reserve(requiredSize);
// We cannot copy the tags directly, as we need to normalize
// some tags, so that [.foo] is copied as [.][foo].
@@ -1204,6 +1323,7 @@ namespace Catch {
// it over to backing storage and actually reference the
// backing storage in the saved tags
StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
+ CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed");
enforceNotReservedTag(tagStr, lineInfo);
properties |= parseSpecialTag(tagStr);
// When copying a tag to the backing storage, we need to
@@ -1225,9 +1345,8 @@ namespace Catch {
}
// Sort and prepare tags
- toLowerInPlace(backingLCaseTags);
- std::sort(begin(tags), end(tags), [](Tag lhs, Tag rhs) { return lhs.lowerCased < rhs.lowerCased; });
- tags.erase(std::unique(begin(tags), end(tags), [](Tag lhs, Tag rhs) {return lhs.lowerCased == rhs.lowerCased; }),
+ std::sort(begin(tags), end(tags));
+ tags.erase(std::unique(begin(tags), end(tags)),
end(tags));
}
@@ -1273,24 +1392,22 @@ namespace Catch {
backingTags += tagStr;
const auto backingEnd = backingTags.size();
backingTags += ']';
- backingLCaseTags += '[';
- // We append the tag to the lower-case backing storage as-is,
- // because we will perform the lower casing later, in bulk
- backingLCaseTags += tagStr;
- backingLCaseTags += ']';
- tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart),
- StringRef(backingLCaseTags.c_str() + backingStart, backingEnd - backingStart));
+ tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart));
}
-
- bool TestCaseHandle::operator == ( TestCaseHandle const& rhs ) const {
- return m_invoker == rhs.m_invoker
- && m_info->name == rhs.m_info->name
- && m_info->className == rhs.m_info->className;
- }
-
- bool TestCaseHandle::operator < ( TestCaseHandle const& rhs ) const {
- return m_info->name < rhs.m_info->name;
+ bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) {
+ // We want to avoid redoing the string comparisons multiple times,
+ // so we store the result of a three-way comparison before using
+ // it in the actual comparison logic.
+ const auto cmpName = lhs.name.compare( rhs.name );
+ if ( cmpName != 0 ) {
+ return cmpName < 0;
+ }
+ const auto cmpClassName = lhs.className.compare( rhs.className );
+ if ( cmpClassName != 0 ) {
+ return cmpClassName < 0;
+ }
+ return lhs.tags < rhs.tags;
}
TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const {
@@ -1330,15 +1447,13 @@ namespace Catch {
TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
: Pattern( filterString )
- , m_tag( toLower( tag ) )
+ , m_tag( tag )
{}
bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
- return std::find_if(begin(testCase.tags),
- end(testCase.tags),
- [&](Tag const& tag) {
- return tag.lowerCased == m_tag;
- }) != end(testCase.tags);
+ return std::find( begin( testCase.tags ),
+ end( testCase.tags ),
+ Tag( m_tag ) ) != end( testCase.tags );
}
bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
@@ -1390,8 +1505,8 @@ namespace Catch {
return matches;
}
- const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
- return (m_invalidArgs);
+ const TestSpec::vectorStrings& TestSpec::getInvalidSpecs() const {
+ return m_invalidSpecs;
}
}
@@ -1400,49 +1515,13 @@ namespace Catch {
#include <chrono>
-static const uint64_t nanosecondsInSecond = 1000000000;
-
namespace Catch {
- auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
- return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();
- }
-
namespace {
- auto estimateClockResolution() -> uint64_t {
- uint64_t sum = 0;
- static const uint64_t iterations = 1000000;
-
- auto startTime = getCurrentNanosecondsSinceEpoch();
-
- for( std::size_t i = 0; i < iterations; ++i ) {
-
- uint64_t ticks;
- uint64_t baseTicks = getCurrentNanosecondsSinceEpoch();
- do {
- ticks = getCurrentNanosecondsSinceEpoch();
- } while( ticks == baseTicks );
-
- auto delta = ticks - baseTicks;
- sum += delta;
-
- // If we have been calibrating for over 3 seconds -- the clock
- // is terrible and we should move on.
- // TBD: How to signal that the measured resolution is probably wrong?
- if (ticks > startTime + 3 * nanosecondsInSecond) {
- return sum / ( i + 1u );
- }
- }
-
- // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers
- // - and potentially do more iterations if there's a high variance.
- return sum/iterations;
+ static auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
- }
- auto getEstimatedClockResolution() -> uint64_t {
- static auto s_resolution = estimateClockResolution();
- return s_resolution;
- }
+ } // end unnamed namespace
void Timer::start() {
m_nanoseconds = getCurrentNanosecondsSinceEpoch();
@@ -1464,12 +1543,6 @@ namespace Catch {
} // namespace Catch
-#if defined(__clang__)
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wexit-time-destructors"
-# pragma clang diagnostic ignored "-Wglobal-constructors"
-#endif
-
#include <cmath>
@@ -1479,8 +1552,6 @@ namespace Catch {
namespace Detail {
- const std::string unprintableString = "{?}";
-
namespace {
const int hexThreshold = 255;
@@ -1517,6 +1588,48 @@ namespace Detail {
}
} // end unnamed namespace
+ std::string convertIntoString(StringRef string, bool escape_invisibles) {
+ 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);
+
+ if (!escape_invisibles) {
+ ret += '"';
+ ret += string;
+ ret += '"';
+ return ret;
+ }
+
+ 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;
+ }
+ }
+ ret += '"';
+
+ return ret;
+ }
+
+ std::string convertIntoString(StringRef string) {
+ return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles());
+ }
+
std::string rawMemoryToString( const void *object, std::size_t size ) {
// Reverse order for little endian architectures
int i = 0, end = static_cast<int>( size ), inc = 1;
@@ -1543,44 +1656,25 @@ namespace Detail {
//// ======================================================= ////
std::string StringMaker<std::string>::convert(const std::string& str) {
- if (!getCurrentContext().getConfig()->showInvisibles()) {
- return '"' + str + '"';
- }
-
- std::string s("\"");
- for (char c : str) {
- switch (c) {
- case '\n':
- s.append("\\n");
- break;
- case '\t':
- s.append("\\t");
- break;
- default:
- s.push_back(c);
- break;
- }
- }
- s.append("\"");
- return s;
+ return Detail::convertIntoString( str );
}
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
std::string StringMaker<std::string_view>::convert(std::string_view str) {
- return ::Catch::Detail::stringify(std::string{ str });
+ return Detail::convertIntoString( StringRef( str.data(), str.size() ) );
}
#endif
std::string StringMaker<char const*>::convert(char const* str) {
if (str) {
- return ::Catch::Detail::stringify(std::string{ str });
+ return Detail::convertIntoString( str );
} else {
return{ "{null string}" };
}
}
std::string StringMaker<char*>::convert(char* str) {
if (str) {
- return ::Catch::Detail::stringify(std::string{ str });
+ return Detail::convertIntoString( str );
} else {
return{ "{null string}" };
}
@@ -1693,11 +1787,6 @@ std::string StringMaker<double>::convert(double value) {
} // end namespace Catch
-#if defined(__clang__)
-# pragma clang diagnostic pop
-#endif
-
-
namespace Catch {
@@ -1717,7 +1806,7 @@ namespace Catch {
return *this;
}
- std::size_t Counts::total() const {
+ std::uint64_t Counts::total() const {
return passed + failed + failedButOk;
}
bool Counts::allPassed() const {
@@ -1784,13 +1873,19 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 3, 0, 0, "preview", 3 );
+ static Version version( 3, 0, 1, "", 0 );
return version;
}
}
+
+
+
+std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); }
+
+
/** \file
* This is a special TU that combines what would otherwise be a very
* small generator-related TUs into one bigger TU.
@@ -1900,16 +1995,6 @@ namespace Catch {
}
-//////////////////////////////////////////////////
-// vvv formerly catch_interfaces_runner.cpp vvv //
-//////////////////////////////////////////////////
-
-
-namespace Catch {
- IRunner::~IRunner() = default;
-}
-
-
////////////////////////////////////////////////////
// vvv formerly catch_interfaces_testcase.cpp vvv //
////////////////////////////////////////////////////
@@ -1921,6 +2006,7 @@ namespace Catch {
}
+
namespace Catch {
IReporterRegistry::~IReporterRegistry() = default;
}
@@ -1929,38 +2015,70 @@ namespace Catch {
namespace Catch {
IReporterFactory::~IReporterFactory() = default;
+ EventListenerFactory::~EventListenerFactory() = default;
}
+#include <string>
+
+namespace Catch {
+ namespace Generators {
+
+ bool GeneratorUntypedBase::countedNext() {
+ auto ret = next();
+ if ( ret ) {
+ m_stringReprCache.clear();
+ ++m_currentElementIndex;
+ }
+ return ret;
+ }
+
+ StringRef GeneratorUntypedBase::currentElementAsString() const {
+ if ( m_stringReprCache.empty() ) {
+ m_stringReprCache = stringifyImpl();
+ }
+ return m_stringReprCache;
+ }
+
+ } // namespace Generators
+} // namespace Catch
+
+
+
#include <algorithm>
+#include <cassert>
#include <iomanip>
namespace Catch {
- ReporterConfig::ReporterConfig( IConfig const* _fullConfig )
- : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+ ReporterConfig::ReporterConfig(
+ IConfig const* _fullConfig,
+ Detail::unique_ptr<IStream> _stream,
+ ColourMode colourMode,
+ std::map<std::string, std::string> customOptions ):
+ m_stream( CATCH_MOVE(_stream) ),
+ m_fullConfig( _fullConfig ),
+ m_colourMode( colourMode ),
+ m_customOptions( CATCH_MOVE( customOptions ) ) {}
- ReporterConfig::ReporterConfig( IConfig const* _fullConfig, std::ostream& _stream )
- : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
-
- std::ostream& ReporterConfig::stream() const { return *m_stream; }
+ Detail::unique_ptr<IStream> ReporterConfig::takeStream() && {
+ assert( m_stream );
+ return CATCH_MOVE( m_stream );
+ }
IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; }
+ ColourMode ReporterConfig::colourMode() const { return m_colourMode; }
+ std::map<std::string, std::string> const&
+ ReporterConfig::customOptions() const {
+ return m_customOptions;
+ }
- TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {}
+ ReporterConfig::~ReporterConfig() = default;
- GroupInfo::GroupInfo( std::string const& _name,
- std::size_t _groupIndex,
- std::size_t _groupsCount )
- : name( _name ),
- groupIndex( _groupIndex ),
- groupsCounts( _groupsCount )
- {}
-
- AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
- std::vector<MessageInfo> const& _infoMessages,
- Totals const& _totals )
+ AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
+ std::vector<MessageInfo> const& _infoMessages,
+ Totals const& _totals )
: assertionResult( _assertionResult ),
infoMessages( _infoMessages ),
totals( _totals )
@@ -2002,20 +2120,6 @@ namespace Catch {
{}
- TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo,
- Totals const& _totals,
- bool _aborting )
- : groupInfo( _groupInfo ),
- totals( _totals ),
- aborting( _aborting )
- {}
-
- TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo )
- : groupInfo( _groupInfo ),
- aborting( false )
- {}
-
-
TestRunStats::TestRunStats( TestRunInfo const& _runInfo,
Totals const& _totals,
bool _aborting )
@@ -2024,84 +2128,7 @@ namespace Catch {
aborting( _aborting )
{}
- void IStreamingReporter::fatalErrorEncountered( StringRef ) {}
-
- void IStreamingReporter::listReporters(std::vector<ReporterDescription> const& descriptions, IConfig const& config) {
- Catch::cout() << "Available reporters:\n";
- const auto maxNameLen = std::max_element(descriptions.begin(), descriptions.end(),
- [](ReporterDescription const& lhs, ReporterDescription const& rhs) { return lhs.name.size() < rhs.name.size(); })
- ->name.size();
-
- for (auto const& desc : descriptions) {
- if (config.verbosity() == Verbosity::Quiet) {
- Catch::cout()
- << TextFlow::Column(desc.name)
- .indent(2)
- .width(5 + maxNameLen) << '\n';
- } else {
- Catch::cout()
- << TextFlow::Column(desc.name + ":")
- .indent(2)
- .width(5 + maxNameLen)
- + TextFlow::Column(desc.description)
- .initialIndent(0)
- .indent(2)
- .width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8)
- << '\n';
- }
- }
- Catch::cout() << std::endl;
- }
-
- void IStreamingReporter::listTests(std::vector<TestCaseHandle> const& tests, IConfig const& config) {
- if (config.hasTestFilters()) {
- Catch::cout() << "Matching test cases:\n";
- } else {
- Catch::cout() << "All available test cases:\n";
- }
-
- for (auto const& test : tests) {
- auto const& testCaseInfo = test.getTestCaseInfo();
- Colour::Code colour = testCaseInfo.isHidden()
- ? Colour::SecondaryText
- : Colour::None;
- Colour colourGuard(colour);
-
- Catch::cout() << TextFlow::Column(testCaseInfo.name).initialIndent(2).indent(4) << '\n';
- if (config.verbosity() >= Verbosity::High) {
- Catch::cout() << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl;
- }
- if (!testCaseInfo.tags.empty() && config.verbosity() > Verbosity::Quiet) {
- Catch::cout() << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n';
- }
- }
-
- if (!config.hasTestFilters()) {
- Catch::cout() << pluralise(tests.size(), "test case") << '\n' << std::endl;
- } else {
- Catch::cout() << pluralise(tests.size(), "matching test case") << '\n' << std::endl;
- }
- }
-
- void IStreamingReporter::listTags(std::vector<TagInfo> const& tags, IConfig const& config) {
- if (config.hasTestFilters()) {
- Catch::cout() << "Tags for matching test cases:\n";
- } else {
- Catch::cout() << "All available tags:\n";
- }
-
- for (auto const& tagCount : tags) {
- ReusableStringStream rss;
- rss << " " << std::setw(2) << tagCount.count << " ";
- auto str = rss.str();
- auto wrapper = TextFlow::Column(tagCount.all())
- .initialIndent(0)
- .indent(str.size())
- .width(CATCH_CONFIG_CONSOLE_WIDTH - 10);
- Catch::cout() << str << wrapper << '\n';
- }
- Catch::cout() << pluralise(tags.size(), "tag") << '\n' << std::endl;
- }
+ IEventListener::~IEventListener() = default;
} // end namespace Catch
@@ -2110,7 +2137,7 @@ namespace Catch {
namespace Catch {
AssertionHandler::AssertionHandler
- ( StringRef const& macroName,
+ ( StringRef macroName,
SourceLineInfo const& lineInfo,
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition )
@@ -2121,7 +2148,7 @@ namespace Catch {
void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
}
- void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
+ void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef message) {
m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
}
@@ -2172,14 +2199,45 @@ namespace Catch {
// This is the overload that takes a string and infers the Equals matcher from it
// The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp
- void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ) {
+ void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) {
handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString );
}
} // namespace Catch
+
+
+#include <algorithm>
+
+namespace Catch {
+ namespace Detail {
+
+ bool CaseInsensitiveLess::operator()( StringRef lhs,
+ StringRef rhs ) const {
+ return std::lexicographical_compare(
+ lhs.begin(), lhs.end(),
+ rhs.begin(), rhs.end(),
+ []( char l, char r ) { return toLower( l ) < toLower( r ); } );
+ }
+
+ bool
+ CaseInsensitiveEqualTo::operator()( StringRef lhs,
+ StringRef rhs ) const {
+ return std::equal(
+ lhs.begin(), lhs.end(),
+ rhs.begin(), rhs.end(),
+ []( char l, char r ) { return toLower( l ) == toLower( r ); } );
+ }
+
+ } // namespace Detail
+} // namespace Catch
+
+
+
+
#include <algorithm>
+#include <ostream>
namespace {
bool isOptPrefix( char c ) {
@@ -2282,7 +2340,7 @@ namespace Catch {
} else {
return ParserResult::runtimeError(
"Expected a boolean value but did not recognise: '" +
- source + "'" );
+ source + '\'' );
}
return ParserResult::ok( ParseResultType::Matched );
}
@@ -2399,7 +2457,7 @@ namespace Catch {
return Detail::InternalParseResult::runtimeError(
"Expected argument following " +
token.token);
- auto result = valueRef->setValue(argToken.token);
+ const auto result = valueRef->setValue(argToken.token);
if (!result)
return Detail::InternalParseResult(result);
if (result.value() ==
@@ -2788,7 +2846,7 @@ Catch::LeakDetector::~LeakDetector() {
namespace Catch {
- MessageInfo::MessageInfo( StringRef const& _macroName,
+ MessageInfo::MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type )
: macroName( _macroName ),
@@ -2813,11 +2871,11 @@ namespace Catch {
auto operator << (std::ostream& os, LazyExpression const& lazyExpr) -> std::ostream& {
if (lazyExpr.m_isNegated)
- os << "!";
+ os << '!';
if (lazyExpr) {
if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression())
- os << "(" << *lazyExpr.m_transientExpression << ")";
+ os << '(' << *lazyExpr.m_transientExpression << ')';
else
os << *lazyExpr.m_transientExpression;
} else {
@@ -2832,7 +2890,7 @@ namespace Catch {
#include <fstream>
-#include <ctime>
+#include <string>
namespace Catch {
@@ -2841,25 +2899,21 @@ namespace Catch {
using namespace Clara;
auto const setWarning = [&]( std::string const& warning ) {
- auto warningSet = [&]() {
- if( warning == "NoAssertions" )
- return WarnAbout::NoAssertions;
-
- if ( warning == "NoTests" )
- return WarnAbout::NoTests;
-
- return WarnAbout::Nothing;
- }();
-
- if (warningSet == WarnAbout::Nothing)
- return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" );
- config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet );
+ if ( warning == "NoAssertions" ) {
+ config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::NoAssertions);
return ParserResult::ok( ParseResultType::Matched );
- };
+ } else if ( warning == "UnmatchedTestSpec" ) {
+ config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::UnmatchedTestSpec);
+ return ParserResult::ok( ParseResultType::Matched );
+ }
+
+ return ParserResult ::runtimeError(
+ "Unrecognised warning option: '" + warning + '\'' );
+ };
auto const loadTestNamesFromFile = [&]( std::string const& filename ) {
std::ifstream f( filename.c_str() );
if( !f.is_open() )
- return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" );
+ return ParserResult::runtimeError( "Unable to load input file: '" + filename + '\'' );
std::string line;
while( std::getline( f, line ) ) {
@@ -2885,28 +2939,53 @@ namespace Catch {
else if( startsWith( "random", order ) )
config.runOrder = TestRunOrder::Randomized;
else
- return ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" );
+ return ParserResult::runtimeError( "Unrecognised ordering: '" + order + '\'' );
return ParserResult::ok( ParseResultType::Matched );
};
auto const setRngSeed = [&]( std::string const& seed ) {
- if( seed != "time" )
- return Clara::Detail::convertInto( seed, config.rngSeed );
- config.rngSeed = static_cast<unsigned int>( std::time(nullptr) );
- return ParserResult::ok( ParseResultType::Matched );
- };
- auto const setColourUsage = [&]( std::string const& useColour ) {
- auto mode = toLower( useColour );
-
- if( mode == "yes" )
- config.useColour = UseColour::Yes;
- else if( mode == "no" )
- config.useColour = UseColour::No;
- else if( mode == "auto" )
- config.useColour = UseColour::Auto;
- else
- return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" );
- return ParserResult::ok( ParseResultType::Matched );
+ if( seed == "time" ) {
+ config.rngSeed = generateRandomSeed(GenerateFrom::Time);
+ return ParserResult::ok(ParseResultType::Matched);
+ } else if (seed == "random-device") {
+ config.rngSeed = generateRandomSeed(GenerateFrom::RandomDevice);
+ return ParserResult::ok(ParseResultType::Matched);
+ }
+
+ CATCH_TRY {
+ std::size_t parsedTo = 0;
+ unsigned long parsedSeed = std::stoul(seed, &parsedTo, 0);
+ if (parsedTo != seed.size()) {
+ return ParserResult::runtimeError("Could not parse '" + seed + "' as seed");
+ }
+
+ // TODO: Ideally we could parse unsigned int directly,
+ // but the stdlib doesn't provide helper for that
+ // type. After this is refactored to use fixed size
+ // type, we should check the parsed value is in range
+ // of the underlying type.
+ config.rngSeed = static_cast<unsigned int>(parsedSeed);
+ return ParserResult::ok(ParseResultType::Matched);
+ } CATCH_CATCH_ANON(std::exception const&) {
+ return ParserResult::runtimeError("Could not parse '" + seed + "' as seed");
+ }
};
+ auto const setDefaultColourMode = [&]( std::string const& colourMode ) {
+ Optional<ColourMode> maybeMode = Catch::Detail::stringToColourMode(toLower( colourMode ));
+ if ( !maybeMode ) {
+ return ParserResult::runtimeError(
+ "colour mode must be one of: default, ansi, win32, "
+ "or none. '" +
+ colourMode + "' is not recognised" );
+ }
+ auto mode = *maybeMode;
+ if ( !isColourImplAvailable( mode ) ) {
+ return ParserResult::runtimeError(
+ "colour mode '" + colourMode +
+ "' is not supported in this binary" );
+ }
+ config.defaultColourMode = mode;
+ return ParserResult::ok( ParseResultType::Matched );
+ };
auto const setWaitForKeypress = [&]( std::string const& keypress ) {
auto keypressLc = toLower( keypress );
if (keypressLc == "never")
@@ -2930,31 +3009,95 @@ namespace Catch {
else if( lcVerbosity == "high" )
config.verbosity = Verbosity::High;
else
- return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" );
+ return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + '\'' );
return ParserResult::ok( ParseResultType::Matched );
};
- auto const setReporter = [&]( std::string const& reporter ) {
- IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+ auto const setReporter = [&]( std::string const& userReporterSpec ) {
+ if ( userReporterSpec.empty() ) {
+ return ParserResult::runtimeError( "Received empty reporter spec." );
+ }
- auto lcReporter = toLower( reporter );
- auto result = factories.find( lcReporter );
+ Optional<ReporterSpec> parsed =
+ parseReporterSpec( userReporterSpec );
+ if ( !parsed ) {
+ return ParserResult::runtimeError(
+ "Could not parse reporter spec '" + userReporterSpec +
+ "'" );
+ }
+
+ auto const& reporterSpec = *parsed;
+
+ IReporterRegistry::FactoryMap const& factories =
+ getRegistryHub().getReporterRegistry().getFactories();
+ auto result = factories.find( reporterSpec.name() );
+
+ if ( result == factories.end() ) {
+ return ParserResult::runtimeError(
+ "Unrecognized reporter, '" + reporterSpec.name() +
+ "'. Check available with --list-reporters" );
+ }
+
+
+ const bool hadOutputFile = reporterSpec.outputFile().some();
+ config.reporterSpecifications.push_back( CATCH_MOVE( *parsed ) );
+ // It would be enough to check this only once at the very end, but
+ // there is not a place where we could call this check, so do it
+ // every time it could fail. For valid inputs, this is still called
+ // at most once.
+ if (!hadOutputFile) {
+ int n_reporters_without_file = 0;
+ for (auto const& spec : config.reporterSpecifications) {
+ if (spec.outputFile().none()) {
+ n_reporters_without_file++;
+ }
+ }
+ if (n_reporters_without_file > 1) {
+ return ParserResult::runtimeError( "Only one reporter may have unspecified output file." );
+ }
+ }
- if( factories.end() != result )
- config.reporterName = lcReporter;
- else
- return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" );
return ParserResult::ok( ParseResultType::Matched );
};
+ auto const setShardCount = [&]( std::string const& shardCount ) {
+ CATCH_TRY{
+ std::size_t parsedTo = 0;
+ int64_t parsedCount = std::stoll(shardCount, &parsedTo, 0);
+ if (parsedTo != shardCount.size()) {
+ return ParserResult::runtimeError("Could not parse '" + shardCount + "' as shard count");
+ }
+ if (parsedCount <= 0) {
+ return ParserResult::runtimeError("Shard count must be a positive number");
+ }
+
+ config.shardCount = static_cast<unsigned int>(parsedCount);
+ return ParserResult::ok(ParseResultType::Matched);
+ } CATCH_CATCH_ANON(std::exception const&) {
+ return ParserResult::runtimeError("Could not parse '" + shardCount + "' as shard count");
+ }
+ };
+
+ auto const setShardIndex = [&](std::string const& shardIndex) {
+ CATCH_TRY{
+ std::size_t parsedTo = 0;
+ int64_t parsedIndex = std::stoll(shardIndex, &parsedTo, 0);
+ if (parsedTo != shardIndex.size()) {
+ return ParserResult::runtimeError("Could not parse '" + shardIndex + "' as shard index");
+ }
+ if (parsedIndex < 0) {
+ return ParserResult::runtimeError("Shard index must be a non-negative number");
+ }
+
+ config.shardIndex = static_cast<unsigned int>(parsedIndex);
+ return ParserResult::ok(ParseResultType::Matched);
+ } CATCH_CATCH_ANON(std::exception const&) {
+ return ParserResult::runtimeError("Could not parse '" + shardIndex + "' as shard index");
+ }
+ };
+
auto cli
= ExeName( config.processName )
| Help( config.showHelp )
- | Opt( config.listTests )
- ["-l"]["--list-tests"]
- ( "list all/matching test cases" )
- | Opt( config.listTags )
- ["-t"]["--list-tags"]
- ( "list all/matching tags" )
| Opt( config.showSuccessfulTests )
["-s"]["--success"]
( "include successful tests in output" )
@@ -2967,10 +3110,10 @@ namespace Catch {
| Opt( config.showInvisibles )
["-i"]["--invisibles"]
( "show invisibles (tabs, newlines)" )
- | Opt( config.outputFilename, "filename" )
+ | Opt( config.defaultOutputFilename, "filename" )
["-o"]["--out"]
- ( "output filename" )
- | Opt( setReporter, "name" )
+ ( "default output filename" )
+ | Opt( accept_many, setReporter, "name[::key=value]*" )
["-r"]["--reporter"]
( "reporter to use (defaults to console)" )
| Opt( config.name, "name" )
@@ -2982,7 +3125,7 @@ namespace Catch {
| Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
["-x"]["--abortx"]
( "abort after x failures" )
- | Opt( setWarning, "warning name" )
+ | Opt( accept_many, setWarning, "warning name" )
["-w"]["--warn"]
( "enable warnings" )
| Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
@@ -3003,24 +3146,36 @@ namespace Catch {
| Opt( setVerbosity, "quiet|normal|high" )
["-v"]["--verbosity"]
( "set output verbosity" )
+ | Opt( config.listTests )
+ ["--list-tests"]
+ ( "list all/matching test cases" )
+ | Opt( config.listTags )
+ ["--list-tags"]
+ ( "list all/matching tags" )
| Opt( config.listReporters )
["--list-reporters"]
- ( "list all reporters" )
+ ( "list all available reporters" )
+ | Opt( config.listListeners )
+ ["--list-listeners"]
+ ( "list all listeners" )
| Opt( setTestOrder, "decl|lex|rand" )
["--order"]
( "test case order (defaults to decl)" )
- | Opt( setRngSeed, "'time'|number" )
+ | Opt( setRngSeed, "'time'|'random-device'|number" )
["--rng-seed"]
( "set a specific seed for random numbers" )
- | Opt( setColourUsage, "yes|no" )
- ["--use-colour"]
- ( "should output be colourised" )
+ | Opt( setDefaultColourMode, "ansi|win32|none|default" )
+ ["--colour-mode"]
+ ( "what color mode should be used as default" )
| Opt( config.libIdentify )
["--libidentify"]
( "report name and version according to libidentify standard" )
| Opt( setWaitForKeypress, "never|start|exit|both" )
["--wait-for-keypress"]
( "waits for a keypress before exiting" )
+ | Opt( config.skipBenchmarks)
+ ["--skip-benchmarks"]
+ ( "disable running benchmarks")
| Opt( config.benchmarkSamples, "samples" )
["--benchmark-samples"]
( "number of samples to collect (default: 100)" )
@@ -3036,6 +3191,15 @@ namespace Catch {
| Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" )
["--benchmark-warmup-time"]
( "amount of time in milliseconds spent on warming up each test (default: 100)" )
+ | Opt( setShardCount, "shard count" )
+ ["--shard-count"]
+ ( "split the tests to execute into this many groups" )
+ | Opt( setShardIndex, "shard index" )
+ ["--shard-index"]
+ ( "index of the group of tests to execute (see --shard-count)" ) |
+ Opt( config.allowZeroTests )
+ ["--allow-running-no-tests"]
+ ( "Treat 'No tests run' as a success" )
| Arg( config.testsOrTags, "test name|pattern|tags" )
( "which test or tests to use" );
@@ -3045,87 +3209,113 @@ namespace Catch {
} // end namespace Catch
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+
-#include <cstring>
+#include <cassert>
#include <ostream>
+#include <utility>
namespace Catch {
- bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
- return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
- }
- bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept {
- // We can assume that the same file will usually have the same pointer.
- // Thus, if the pointers are the same, there is no point in calling the strcmp
- return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0));
- }
+ ColourImpl::~ColourImpl() = default;
- std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
-#ifndef __GNUG__
- os << info.file << '(' << info.line << ')';
-#else
- os << info.file << ':' << info.line;
-#endif
- return os;
+ ColourImpl::ColourGuard ColourImpl::guardColour( Colour::Code colourCode ) {
+ return ColourGuard(colourCode, this );
}
-} // end namespace Catch
+ void ColourImpl::ColourGuard::engageImpl( std::ostream& stream ) {
+ assert( &stream == &m_colourImpl->m_stream->stream() &&
+ "Engaging colour guard for different stream than used by the "
+ "parent colour implementation" );
+ static_cast<void>( stream );
+ m_engaged = true;
+ m_colourImpl->use( m_code );
+ }
-#if defined(__clang__)
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wexit-time-destructors"
-#endif
+ ColourImpl::ColourGuard::ColourGuard( Colour::Code code,
+ ColourImpl const* colour ):
+ m_colourImpl( colour ), m_code( code ) {
+ }
+ ColourImpl::ColourGuard::ColourGuard( ColourGuard&& rhs ) noexcept:
+ m_colourImpl( rhs.m_colourImpl ),
+ m_code( rhs.m_code ),
+ m_engaged( rhs.m_engaged ) {
+ rhs.m_engaged = false;
+ }
+ ColourImpl::ColourGuard&
+ ColourImpl::ColourGuard::operator=( ColourGuard&& rhs ) noexcept {
+ using std::swap;
+ swap( m_colourImpl, rhs.m_colourImpl );
+ swap( m_code, rhs.m_code );
+ swap( m_engaged, rhs.m_engaged );
+ return *this;
+ }
+ ColourImpl::ColourGuard::~ColourGuard() {
+ if ( m_engaged ) {
+ m_colourImpl->use( Colour::None );
+ }
+ }
+ ColourImpl::ColourGuard&
+ ColourImpl::ColourGuard::engage( std::ostream& stream ) & {
+ engageImpl( stream );
+ return *this;
+ }
-#include <ostream>
+ ColourImpl::ColourGuard&&
+ ColourImpl::ColourGuard::engage( std::ostream& stream ) && {
+ engageImpl( stream );
+ return CATCH_MOVE(*this);
+ }
-namespace Catch {
namespace {
+ //! A do-nothing implementation of colour, used as fallback for unknown
+ //! platforms, and when the user asks to deactivate all colours.
+ class NoColourImpl : public ColourImpl {
+ public:
+ NoColourImpl( IStream* stream ): ColourImpl( stream ) {}
- struct IColourImpl {
- virtual ~IColourImpl() = default;
- virtual void use( Colour::Code _colourCode ) = 0;
+ private:
+ void use( Colour::Code ) const override {}
};
+ } // namespace
- struct NoColourImpl : IColourImpl {
- void use( Colour::Code ) override {}
- static IColourImpl* instance() {
- static NoColourImpl s_instance;
- return &s_instance;
- }
- };
-
- } // anon namespace
} // namespace Catch
-#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
-# ifdef CATCH_PLATFORM_WINDOWS
-# define CATCH_CONFIG_COLOUR_WINDOWS
-# else
-# define CATCH_CONFIG_COLOUR_ANSI
-# endif
-#endif
-
-#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+#if defined ( CATCH_CONFIG_COLOUR_WIN32 ) /////////////////////////////////////////
namespace Catch {
namespace {
- class Win32ColourImpl : public IColourImpl {
+ class Win32ColourImpl : public ColourImpl {
public:
- Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
- {
+ Win32ColourImpl(IStream* stream):
+ ColourImpl(stream) {
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
- GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+ GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ),
+ &csbiInfo );
originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
}
- void use( Colour::Code _colourCode ) override {
+ static bool useImplementationForStream(IStream const& stream) {
+ // Win32 text colour APIs can only be used on console streams
+ // We cannot check that the output hasn't been redirected,
+ // so we just check that the original stream is console stream.
+ return stream.isConsole();
+ }
+
+ private:
+ void use( Colour::Code _colourCode ) const override {
switch( _colourCode ) {
case Colour::None: return setTextAttribute( originalForegroundAttributes );
case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
@@ -3149,45 +3339,63 @@ namespace {
}
}
- private:
- void setTextAttribute( WORD _textAttribute ) {
- SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
+ void setTextAttribute( WORD _textAttribute ) const {
+ SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ),
+ _textAttribute |
+ originalBackgroundAttributes );
}
- HANDLE stdoutHandle;
WORD originalForegroundAttributes;
WORD originalBackgroundAttributes;
};
- IColourImpl* platformColourInstance() {
- static Win32ColourImpl s_instance;
-
- auto const* config = getCurrentContext().getConfig();
- UseColour colourMode = config?
- config->useColour() : UseColour::Auto;
- if( colourMode == UseColour::Auto )
- colourMode = UseColour::Yes;
- return colourMode == UseColour::Yes
- ? &s_instance
- : NoColourImpl::instance();
- }
-
} // end anon namespace
} // end namespace Catch
-#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+#endif // Windows/ ANSI/ None
+
-#include <unistd.h>
+#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC )
+# define CATCH_INTERNAL_HAS_ISATTY
+# include <unistd.h>
+#endif
namespace Catch {
namespace {
- // use POSIX/ ANSI console terminal codes
- // Thanks to Adam Strzelecki for original contribution
- // (http://github.com/nanoant)
- // https://github.com/philsquared/Catch/pull/131
- class PosixColourImpl : public IColourImpl {
+ class ANSIColourImpl : public ColourImpl {
public:
- void use( Colour::Code _colourCode ) override {
+ ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {}
+
+ static bool useImplementationForStream(IStream const& stream) {
+ // This is kinda messy due to trying to support a bunch of
+ // different platforms at once.
+ // The basic idea is that if we are asked to do autodetection (as
+ // opposed to being told to use posixy colours outright), then we
+ // only want to use the colours if we are writing to console.
+ // However, console might be redirected, so we make an attempt at
+ // checking for that on platforms where we know how to do that.
+ bool useColour = stream.isConsole();
+#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \
+ !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) )
+ ErrnoGuard _; // for isatty
+ useColour = useColour && isatty( STDOUT_FILENO );
+# endif
+# if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE )
+ useColour = useColour && !isDebuggerActive();
+# endif
+
+ return useColour;
+ }
+
+ private:
+ void use( Colour::Code _colourCode ) const override {
+ auto setColour = [&out =
+ m_stream->stream()]( char const* escapeCode ) {
+ // The escape sequence must be flushed to console, otherwise
+ // if stdin and stderr are intermixed, we'd get accidentally
+ // coloured output.
+ out << '\033' << escapeCode << std::flush;
+ };
switch( _colourCode ) {
case Colour::None:
case Colour::White: return setColour( "[0m" );
@@ -3208,89 +3416,59 @@ namespace {
default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
}
}
- static IColourImpl* instance() {
- static PosixColourImpl s_instance;
- return &s_instance;
- }
-
- private:
- void setColour( const char* _escapeCode ) {
- // The escape sequence must be flushed to console, otherwise if
- // stdin and stderr are intermixed, we'd get accidentally coloured output.
- getCurrentContext().getConfig()->stream()
- << '\033' << _escapeCode << std::flush;
- }
};
- bool useColourOnPlatform() {
- return
-#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
- !isDebuggerActive() &&
-#endif
-#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
- isatty(STDOUT_FILENO)
-#else
- false
-#endif
- ;
- }
- IColourImpl* platformColourInstance() {
- ErrnoGuard guard;
- auto const* config = getCurrentContext().getConfig();
- UseColour colourMode = config
- ? config->useColour()
- : UseColour::Auto;
- if( colourMode == UseColour::Auto )
- colourMode = useColourOnPlatform()
- ? UseColour::Yes
- : UseColour::No;
- return colourMode == UseColour::Yes
- ? PosixColourImpl::instance()
- : NoColourImpl::instance();
- }
-
} // end anon namespace
} // end namespace Catch
-#else // not Windows or ANSI ///////////////////////////////////////////////
-
namespace Catch {
- static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
-
-} // end namespace Catch
-
-#endif // Windows/ ANSI/ None
+ Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode implSelection,
+ IStream* stream ) {
+ if ( implSelection == ColourMode::None ) {
+ return Detail::make_unique<NoColourImpl>( stream );
+ }
+ if ( implSelection == ColourMode::ANSI ) {
+ return Detail::make_unique<ANSIColourImpl>( stream );
+ }
+#if defined( CATCH_CONFIG_COLOUR_WIN32 )
+ if ( implSelection == ColourMode::Win32 ) {
+ return Detail::make_unique<Win32ColourImpl>( stream );
+ }
+#endif
-namespace Catch {
+ // todo: check win32 eligibility under ifdef, otherwise ansi
+ if ( implSelection == ColourMode::PlatformDefault) {
+#if defined (CATCH_CONFIG_COLOUR_WIN32)
+ if ( Win32ColourImpl::useImplementationForStream( *stream ) ) {
+ return Detail::make_unique<Win32ColourImpl>( stream );
+ } else {
+ return Detail::make_unique<NoColourImpl>( stream );
+ }
+#endif
+ if ( ANSIColourImpl::useImplementationForStream( *stream ) ) {
+ return Detail::make_unique<ANSIColourImpl>( stream );
+ }
+ return Detail::make_unique<NoColourImpl>( stream );
+ }
- Colour::Colour( Code _colourCode ) { use( _colourCode ); }
- Colour::Colour( Colour&& other ) noexcept {
- m_moved = other.m_moved;
- other.m_moved = true;
+ CATCH_ERROR( "Could not create colour impl for selection " << static_cast<int>(implSelection) );
}
- Colour& Colour::operator=( Colour&& other ) noexcept {
- m_moved = other.m_moved;
- other.m_moved = true;
- return *this;
- }
-
- Colour::~Colour(){ if( !m_moved ) use( None ); }
- void Colour::use( Code _colourCode ) {
- static IColourImpl* impl = platformColourInstance();
- // Strictly speaking, this cannot possibly happen.
- // However, under some conditions it does happen (see #1626),
- // and this change is small enough that we can let practicality
- // triumph over purity in this case.
- if (impl != nullptr) {
- impl->use( _colourCode );
+ bool isColourImplAvailable( ColourMode colourSelection ) {
+ switch ( colourSelection ) {
+#if defined( CATCH_CONFIG_COLOUR_WIN32 )
+ case ColourMode::Win32:
+#endif
+ case ColourMode::ANSI:
+ case ColourMode::None:
+ case ColourMode::PlatformDefault:
+ return true;
+ default:
+ return false;
}
}
- std::ostream& operator << ( std::ostream& os, Colour const& ) {
- return os;
- }
} // end namespace Catch
@@ -3309,9 +3487,6 @@ namespace Catch {
IResultCapture* getResultCapture() override {
return m_resultCapture;
}
- IRunner* getRunner() override {
- return m_runner;
- }
IConfig const* getConfig() const override {
return m_config;
@@ -3323,9 +3498,6 @@ namespace Catch {
void setResultCapture( IResultCapture* resultCapture ) override {
m_resultCapture = resultCapture;
}
- void setRunner( IRunner* runner ) override {
- m_runner = runner;
- }
void setConfig( IConfig const* config ) override {
m_config = config;
}
@@ -3334,7 +3506,6 @@ namespace Catch {
private:
IConfig const* m_config = nullptr;
- IRunner* m_runner = nullptr;
IResultCapture* m_resultCapture = nullptr;
};
@@ -3354,7 +3525,7 @@ namespace Catch {
Context::~Context() = default;
- SimplePcg32& rng() {
+ SimplePcg32& sharedRng() {
static SimplePcg32 s_rng;
return s_rng;
}
@@ -3363,6 +3534,10 @@ namespace Catch {
+
+
+#include <ostream>
+
#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
#include <android/log.h>
@@ -3436,7 +3611,7 @@ namespace Catch {
size = sizeof(info);
if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) {
- Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+ Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" << std::flush;
return false;
}
@@ -3542,7 +3717,7 @@ namespace Catch {
namespace Catch {
- IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
+ IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default;
namespace Detail {
@@ -3550,7 +3725,7 @@ namespace Catch {
// Extracts the actual name part of an enum instance
// In other words, it returns the Blue part of Bikeshed::Colour::Blue
StringRef extractInstanceName(StringRef enumInstance) {
- // Find last occurence of ":"
+ // Find last occurrence of ":"
size_t name_start = enumInstance.size();
while (name_start > 0 && enumInstance[name_start - 1] != ':') {
--name_start;
@@ -3609,33 +3784,38 @@ namespace Catch {
ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
}
- void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) {
- m_translators.push_back( Detail::unique_ptr<const IExceptionTranslator>( translator ) );
+ void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) {
+ m_translators.push_back( CATCH_MOVE( translator ) );
}
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
std::string ExceptionTranslatorRegistry::translateActiveException() const {
+ // Compiling a mixed mode project with MSVC means that CLR
+ // exceptions will be caught in (...) as well. However, these do
+ // do not fill-in std::current_exception and thus lead to crash
+ // when attempting rethrow.
+ // /EHa switch also causes structured exceptions to be caught
+ // here, but they fill-in current_exception properly, so
+ // at worst the output should be a little weird, instead of
+ // causing a crash.
+ if ( std::current_exception() == nullptr ) {
+ return "Non C++ exception. Possibly a CLR exception.";
+ }
+
+ // First we try user-registered translators. If none of them can
+ // handle the exception, it will be rethrown handled by our defaults.
try {
- // Compiling a mixed mode project with MSVC means that CLR
- // exceptions will be caught in (...) as well. However, these
- // do not fill-in std::current_exception and thus lead to crash
- // when attempting rethrow.
- // /EHa switch also causes structured exceptions to be caught
- // here, but they fill-in current_exception properly, so
- // at worst the output should be a little weird, instead of
- // causing a crash.
- if (std::current_exception() == nullptr) {
- return "Non C++ exception. Possibly a CLR exception.";
- }
return tryTranslators();
}
+ // To avoid having to handle TFE explicitly everywhere, we just
+ // rethrow it so that it goes back up the caller.
catch( TestFailureException& ) {
std::rethrow_exception(std::current_exception());
}
- catch( std::exception& ex ) {
+ catch( std::exception const& ex ) {
return ex.what();
}
- catch( std::string& msg ) {
+ catch( std::string const& msg ) {
return msg;
}
catch( const char* msg ) {
@@ -3669,39 +3849,80 @@ namespace Catch {
+/** \file
+ * This file provides platform specific implementations of FatalConditionHandler
+ *
+ * This means that there is a lot of conditional compilation, and platform
+ * specific code. Currently, Catch2 supports a dummy handler (if no
+ * handler is desired), and 2 platform specific handlers:
+ * * Windows' SEH
+ * * POSIX signals
+ *
+ * Consequently, various pieces of code below are compiled if either of
+ * the platform specific handlers is enabled, or if none of them are
+ * enabled. It is assumed that both cannot be enabled at the same time,
+ * and doing so should cause a compilation error.
+ *
+ * If another platform specific handler is added, the compile guards
+ * below will need to be updated taking these assumptions into account.
+ */
-#if defined(__GNUC__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
+
+
+#include <algorithm>
+
+#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+namespace Catch {
+
+ // If neither SEH nor signal handling is required, the handler impls
+ // do not have to do anything, and can be empty.
+ void FatalConditionHandler::engage_platform() {}
+ void FatalConditionHandler::disengage_platform() noexcept {}
+ FatalConditionHandler::FatalConditionHandler() = default;
+ FatalConditionHandler::~FatalConditionHandler() = default;
+
+} // end namespace Catch
+
+#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
+#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
+#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
namespace {
- // Report the error condition
+ //! Signals fatal error message to the run context
void reportFatal( char const * const message ) {
Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
}
-}
-#endif // signals/SEH handling
+ //! Minimal size Catch2 needs for its own fatal error handling.
+ //! Picked empirically, so it might not be sufficient on all
+ //! platforms, and for all configurations.
+ constexpr std::size_t minStackSizeForErrors = 32 * 1024;
+} // end unnamed namespace
+
+#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch {
+
struct SignalDefs { DWORD id; const char* name; };
// There is no 1-1 mapping between signals and windows exceptions.
// Windows can easily distinguish between SO and SigSegV,
// but SigInt, SigTerm, etc are handled differently.
static SignalDefs signalDefs[] = {
- { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal" },
- { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" },
- { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" },
- { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" },
+ { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
+ { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
};
- LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+ static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name);
@@ -3712,35 +3933,51 @@ namespace Catch {
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() {
- isSet = true;
- // 32k seems enough for Catch to handle stack overflow,
- // but the value was found experimentally, so there is no strong guarantee
- guaranteeSize = 32 * 1024;
- exceptionHandlerHandle = nullptr;
- // Register as first handler in current chain
- exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
- // Pass in guarantee size to be filled
- SetThreadStackGuarantee(&guaranteeSize);
+ ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
+ if (!SetThreadStackGuarantee(&guaranteeSize)) {
+ // We do not want to fully error out, because needing
+ // the stack reserve should be rare enough anyway.
+ Catch::cerr()
+ << "Failed to reserve piece of stack."
+ << " Stack overflows will not be reported successfully.";
+ }
+ }
+
+ // We do not attempt to unset the stack guarantee, because
+ // Windows does not support lowering the stack size guarantee.
+ FatalConditionHandler::~FatalConditionHandler() = default;
+
+
+ void FatalConditionHandler::engage_platform() {
+ // Register as a the top level exception filter.
+ previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter);
}
- void FatalConditionHandler::reset() {
- if (isSet) {
- RemoveVectoredExceptionHandler(exceptionHandlerHandle);
- SetThreadStackGuarantee(&guaranteeSize);
- exceptionHandlerHandle = nullptr;
- isSet = false;
+ void FatalConditionHandler::disengage_platform() noexcept {
+ if (SetUnhandledExceptionFilter(previousTopLevelExceptionFilter) != topLevelExceptionFilter) {
+ Catch::cerr()
+ << "Unexpected SEH unhandled exception filter on disengage."
+ << " The filter was restored, but might be rolled back unexpectedly.";
}
+ previousTopLevelExceptionFilter = nullptr;
}
-bool FatalConditionHandler::isSet = false;
-ULONG FatalConditionHandler::guaranteeSize = 0;
-PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
+} // end namespace Catch
+#endif // CATCH_CONFIG_WINDOWS_SEH
-} // namespace Catch
+#if defined( CATCH_CONFIG_POSIX_SIGNALS )
-#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
+#include <signal.h>
namespace Catch {
@@ -3749,10 +3986,6 @@ namespace Catch {
const char* name;
};
- // 32kb for the alternate stack seems to be sufficient. However, this value
- // is experimentally determined, so that's not guaranteed.
- static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
-
static SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" },
{ SIGILL, "SIGILL - Illegal instruction signal" },
@@ -3762,8 +3995,32 @@ namespace Catch {
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
};
+// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
+// which is zero initialization, but not explicit. We want to avoid
+// that.
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
- void FatalConditionHandler::handleSignal( int sig ) {
+ static char* altStackMem = nullptr;
+ static std::size_t altStackSize = 0;
+ static stack_t oldSigStack{};
+ static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
+
+ static void restorePreviousSignalHandlers() noexcept {
+ // We set signal handlers back to the previous ones. Hopefully
+ // nobody overwrote them in the meantime, and doesn't expect
+ // their signal handlers to live past ours given that they
+ // installed them after ours..
+ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
+ }
+ // Return the old stack
+ sigaltstack(&oldSigStack, nullptr);
+ }
+
+ static void handleSignal( int sig ) {
char const * name = "<unknown signal>";
for (auto const& def : signalDefs) {
if (sig == def.id) {
@@ -3771,16 +4028,33 @@ namespace Catch {
break;
}
}
- reset();
- reportFatal(name);
+ // We need to restore previous signal handlers and let them do
+ // their thing, so that the users can have the debugger break
+ // when a signal is raised, and so on.
+ restorePreviousSignalHandlers();
+ reportFatal( name );
raise( sig );
}
FatalConditionHandler::FatalConditionHandler() {
- isSet = true;
+ assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
+ if (altStackSize == 0) {
+ altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
+ }
+ altStackMem = new char[altStackSize]();
+ }
+
+ FatalConditionHandler::~FatalConditionHandler() {
+ delete[] altStackMem;
+ // We signal that another instance can be constructed by zeroing
+ // out the pointer.
+ altStackMem = nullptr;
+ }
+
+ void FatalConditionHandler::engage_platform() {
stack_t sigStack;
sigStack.ss_sp = altStackMem;
- sigStack.ss_size = sigStackSize;
+ sigStack.ss_size = altStackSize;
sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = { };
@@ -3792,68 +4066,229 @@ namespace Catch {
}
}
- void FatalConditionHandler::reset() {
- if( isSet ) {
- // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
- sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
- }
- // Return the old stack
- sigaltstack(&oldSigStack, nullptr);
- isSet = false;
- }
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
+
+ void FatalConditionHandler::disengage_platform() noexcept {
+ restorePreviousSignalHandlers();
}
- bool FatalConditionHandler::isSet = false;
- struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
- stack_t FatalConditionHandler::oldSigStack = {};
- char FatalConditionHandler::altStackMem[sigStackSize] = {};
+} // end namespace Catch
+#endif // CATCH_CONFIG_POSIX_SIGNALS
-} // namespace Catch
-#endif // signals/SEH handling
-#if defined(__GNUC__)
-# pragma GCC diagnostic pop
-#endif
+
+#include <cstring>
+
+namespace Catch {
+ namespace Detail {
+
+ uint32_t convertToBits(float f) {
+ static_assert(sizeof(float) == sizeof(uint32_t), "Important ULP matcher assumption violated");
+ uint32_t i;
+ std::memcpy(&i, &f, sizeof(f));
+ return i;
+ }
+
+ uint64_t convertToBits(double d) {
+ static_assert(sizeof(double) == sizeof(uint64_t), "Important ULP matcher assumption violated");
+ uint64_t i;
+ std::memcpy(&i, &d, sizeof(d));
+ return i;
+ }
+
+ } // end namespace Detail
+} // end namespace Catch
+
+#include <cstdio>
+#include <fstream>
+#include <sstream>
+#include <vector>
namespace Catch {
+
+ Catch::IStream::~IStream() = default;
+
+namespace Detail {
namespace {
+ template<typename WriterF, std::size_t bufferSize=256>
+ class StreamBufImpl : public std::streambuf {
+ char data[bufferSize];
+ WriterF m_writer;
- void listTests(IStreamingReporter& reporter, IConfig const& config) {
+ public:
+ StreamBufImpl() {
+ setp( data, data + sizeof(data) );
+ }
+
+ ~StreamBufImpl() noexcept override {
+ StreamBufImpl::sync();
+ }
+
+ private:
+ int overflow( int c ) override {
+ sync();
+
+ if( c != EOF ) {
+ if( pbase() == epptr() )
+ m_writer( std::string( 1, static_cast<char>( c ) ) );
+ else
+ sputc( static_cast<char>( c ) );
+ }
+ return 0;
+ }
+
+ int sync() override {
+ if( pbase() != pptr() ) {
+ m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+ setp( pbase(), epptr() );
+ }
+ return 0;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ struct OutputDebugWriter {
+
+ void operator()( std::string const& str ) {
+ if ( !str.empty() ) {
+ writeToDebugConsole( str );
+ }
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class FileStream : public IStream {
+ std::ofstream m_ofs;
+ public:
+ FileStream( std::string const& filename ) {
+ m_ofs.open( filename.c_str() );
+ CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' );
+ }
+ ~FileStream() override = default;
+ public: // IStream
+ std::ostream& stream() override {
+ return m_ofs;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class CoutStream : public IStream {
+ std::ostream m_os;
+ public:
+ // Store the streambuf from cout up-front because
+ // cout may get redirected when running tests
+ CoutStream() : m_os( Catch::cout().rdbuf() ) {}
+ ~CoutStream() override = default;
+
+ public: // IStream
+ std::ostream& stream() override { return m_os; }
+ bool isConsole() const override { return true; }
+ };
+
+ class CerrStream : public IStream {
+ std::ostream m_os;
+
+ public:
+ // Store the streambuf from cerr up-front because
+ // cout may get redirected when running tests
+ CerrStream(): m_os( Catch::cerr().rdbuf() ) {}
+ ~CerrStream() override = default;
+
+ public: // IStream
+ std::ostream& stream() override { return m_os; }
+ bool isConsole() const override { return true; }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class DebugOutStream : public IStream {
+ Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
+ std::ostream m_os;
+ public:
+ DebugOutStream()
+ : m_streamBuf( Detail::make_unique<StreamBufImpl<OutputDebugWriter>>() ),
+ m_os( m_streamBuf.get() )
+ {}
+
+ ~DebugOutStream() override = default;
+
+ public: // IStream
+ std::ostream& stream() override { return m_os; }
+ };
+
+ } // unnamed namespace
+} // namespace Detail
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream> {
+ if ( filename.empty() || filename == "-" ) {
+ return Detail::make_unique<Detail::CoutStream>();
+ }
+ if( filename[0] == '%' ) {
+ if ( filename == "%debug" ) {
+ return Detail::make_unique<Detail::DebugOutStream>();
+ } else if ( filename == "%stderr" ) {
+ return Detail::make_unique<Detail::CerrStream>();
+ } else if ( filename == "%stdout" ) {
+ return Detail::make_unique<Detail::CoutStream>();
+ } else {
+ CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' );
+ }
+ }
+ return Detail::make_unique<Detail::FileStream>( filename );
+ }
+
+}
+
+
+
+
+
+namespace Catch {
+ namespace {
+
+ void listTests(IEventListener& reporter, IConfig const& config) {
auto const& testSpec = config.testSpec();
auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config);
- reporter.listTests(matchedTestCases, config);
+ reporter.listTests(matchedTestCases);
}
- void listTags(IStreamingReporter& reporter, IConfig const& config) {
+ void listTags(IEventListener& reporter, IConfig const& config) {
auto const& testSpec = config.testSpec();
std::vector<TestCaseHandle> matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config);
- std::map<StringRef, TagInfo> tagCounts;
+ std::map<StringRef, TagInfo, Detail::CaseInsensitiveLess> tagCounts;
for (auto const& testCase : matchedTestCases) {
for (auto const& tagName : testCase.getTestCaseInfo().tags) {
- auto it = tagCounts.find(tagName.lowerCased);
+ auto it = tagCounts.find(tagName.original);
if (it == tagCounts.end())
- it = tagCounts.insert(std::make_pair(tagName.lowerCased, TagInfo())).first;
+ it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first;
it->second.add(tagName.original);
}
}
std::vector<TagInfo> infos; infos.reserve(tagCounts.size());
for (auto& tagc : tagCounts) {
- infos.push_back(std::move(tagc.second));
+ infos.push_back(CATCH_MOVE(tagc.second));
}
- reporter.listTags(infos, config);
+ reporter.listTags(infos);
}
- void listReporters(IStreamingReporter& reporter, IConfig const& config) {
+ void listReporters(IEventListener& reporter) {
std::vector<ReporterDescription> descriptions;
IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
@@ -3862,7 +4297,20 @@ namespace Catch {
descriptions.push_back({ fac.first, fac.second->getDescription() });
}
- reporter.listReporters(descriptions, config);
+ reporter.listReporters(descriptions);
+ }
+
+ void listListeners(IEventListener& reporter) {
+ std::vector<ListenerDescription> descriptions;
+
+ auto const& factories =
+ getRegistryHub().getReporterRegistry().getListeners();
+ descriptions.reserve( factories.size() );
+ for ( auto const& fac : factories ) {
+ descriptions.push_back( { fac->getName(), fac->getDescription() } );
+ }
+
+ reporter.listListeners( descriptions );
}
} // end anonymous namespace
@@ -3888,7 +4336,7 @@ namespace Catch {
return out;
}
- bool list( IStreamingReporter& reporter, Config const& config ) {
+ bool list( IEventListener& reporter, Config const& config ) {
bool listed = false;
if (config.listTests()) {
listed = true;
@@ -3900,25 +4348,32 @@ namespace Catch {
}
if (config.listReporters()) {
listed = true;
- listReporters(reporter, config);
+ listReporters(reporter);
+ }
+ if ( config.listListeners() ) {
+ listed = true;
+ listListeners( reporter );
}
return listed;
}
} // end namespace Catch
-#if 0
+
namespace Catch {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
- LeakDetector leakDetector;
+ static LeakDetector leakDetector;
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
}
+// Allow users of amalgamated .cpp file to remove our main and provide their own.
+#if !defined(CATCH_AMALGAMATED_CUSTOM_MAIN)
+
#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
// Standard C/C++ Win32 Unicode wmain entry point
-extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+extern "C" int __cdecl wmain (int argc, wchar_t * argv[], wchar_t * []) {
#else
// Standard C/C++ main entry point
int main (int argc, char * argv[]) {
@@ -3931,7 +4386,8 @@ int main (int argc, char * argv[]) {
return Catch::Session().run( argc, argv );
}
-#endif
+#endif // !defined(CATCH_AMALGAMATED_CUSTOM_MAIN
+
#include <cstdio>
@@ -4137,36 +4593,67 @@ namespace {
+
+#include <ctime>
+#include <random>
+
+namespace Catch {
+
+ std::uint32_t generateRandomSeed( GenerateFrom from ) {
+ switch ( from ) {
+ case GenerateFrom::Time:
+ return static_cast<std::uint32_t>( std::time( nullptr ) );
+
+ case GenerateFrom::Default:
+ case GenerateFrom::RandomDevice:
+ // In theory, a platform could have random_device that returns just
+ // 16 bits. That is still some randomness, so we don't care too much
+ return static_cast<std::uint32_t>( std::random_device{}() );
+
+ default:
+ CATCH_ERROR("Unknown generation method");
+ }
+ }
+
+} // end namespace Catch
+
+
+
+
namespace Catch {
ReporterRegistry::ReporterRegistry() {
// Because it is impossible to move out of initializer list,
// we have to add the elements manually
- m_factories["automake"] = Detail::make_unique<ReporterFactory<AutomakeReporter>>();
+ m_factories["Automake"] = Detail::make_unique<ReporterFactory<AutomakeReporter>>();
m_factories["compact"] = Detail::make_unique<ReporterFactory<CompactReporter>>();
m_factories["console"] = Detail::make_unique<ReporterFactory<ConsoleReporter>>();
- m_factories["junit"] = Detail::make_unique<ReporterFactory<JunitReporter>>();
- m_factories["sonarqube"] = Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
- m_factories["tap"] = Detail::make_unique<ReporterFactory<TAPReporter>>();
- m_factories["teamcity"] = Detail::make_unique<ReporterFactory<TeamCityReporter>>();
- m_factories["xml"] = Detail::make_unique<ReporterFactory<XmlReporter>>();
+ m_factories["JUnit"] = Detail::make_unique<ReporterFactory<JunitReporter>>();
+ m_factories["SonarQube"] = Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
+ m_factories["TAP"] = Detail::make_unique<ReporterFactory<TAPReporter>>();
+ m_factories["TeamCity"] = Detail::make_unique<ReporterFactory<TeamCityReporter>>();
+ m_factories["XML"] = Detail::make_unique<ReporterFactory<XmlReporter>>();
}
ReporterRegistry::~ReporterRegistry() = default;
- IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfig const* config ) const {
+ IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const {
auto it = m_factories.find( name );
if( it == m_factories.end() )
return nullptr;
- return it->second->create( ReporterConfig( config ) );
+ return it->second->create( CATCH_MOVE(config) );
}
void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) {
- m_factories.emplace(name, std::move(factory));
+ CATCH_ENFORCE( name.find( "::" ) == name.npos,
+ "'::' is not allowed in reporter name: '" + name + '\'' );
+ auto ret = m_factories.emplace(name, CATCH_MOVE(factory));
+ CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" );
}
- void ReporterRegistry::registerListener( IReporterFactoryPtr factory ) {
- m_listeners.push_back( std::move(factory) );
+ void ReporterRegistry::registerListener(
+ Detail::unique_ptr<EventListenerFactory> factory ) {
+ m_listeners.push_back( CATCH_MOVE(factory) );
}
IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
@@ -4180,6 +4667,171 @@ namespace Catch {
+
+
+#include <algorithm>
+
+namespace Catch {
+
+ namespace {
+ struct kvPair {
+ StringRef key, value;
+ };
+
+ kvPair splitKVPair(StringRef kvString) {
+ auto splitPos = static_cast<size_t>( std::distance(
+ kvString.begin(),
+ std::find( kvString.begin(), kvString.end(), '=' ) ) );
+
+ return { kvString.substr( 0, splitPos ),
+ kvString.substr( splitPos + 1, kvString.size() ) };
+ }
+ }
+
+ namespace Detail {
+ std::vector<std::string> splitReporterSpec( StringRef reporterSpec ) {
+ static constexpr auto separator = "::";
+ static constexpr size_t separatorSize = 2;
+
+ size_t separatorPos = 0;
+ auto findNextSeparator = [&reporterSpec]( size_t startPos ) {
+ static_assert(
+ separatorSize == 2,
+ "The code below currently assumes 2 char separator" );
+
+ auto currentPos = startPos;
+ do {
+ while ( currentPos < reporterSpec.size() &&
+ reporterSpec[currentPos] != separator[0] ) {
+ ++currentPos;
+ }
+ if ( currentPos + 1 < reporterSpec.size() &&
+ reporterSpec[currentPos + 1] == separator[1] ) {
+ return currentPos;
+ }
+ ++currentPos;
+ } while ( currentPos < reporterSpec.size() );
+
+ return static_cast<size_t>( -1 );
+ };
+
+ std::vector<std::string> parts;
+
+ while ( separatorPos < reporterSpec.size() ) {
+ const auto nextSeparator = findNextSeparator( separatorPos );
+ parts.push_back( static_cast<std::string>( reporterSpec.substr(
+ separatorPos, nextSeparator - separatorPos ) ) );
+
+ if ( nextSeparator == static_cast<size_t>( -1 ) ) {
+ break;
+ }
+ separatorPos = nextSeparator + separatorSize;
+ }
+
+ // Handle a separator at the end.
+ // This is not a valid spec, but we want to do validation in a
+ // centralized place
+ if ( separatorPos == reporterSpec.size() ) {
+ parts.emplace_back();
+ }
+
+ return parts;
+ }
+
+ Optional<ColourMode> stringToColourMode( StringRef colourMode ) {
+ if ( colourMode == "default" ) {
+ return ColourMode::PlatformDefault;
+ } else if ( colourMode == "ansi" ) {
+ return ColourMode::ANSI;
+ } else if ( colourMode == "win32" ) {
+ return ColourMode::Win32;
+ } else if ( colourMode == "none" ) {
+ return ColourMode::None;
+ } else {
+ return {};
+ }
+ }
+ } // namespace Detail
+
+
+ bool operator==( ReporterSpec const& lhs, ReporterSpec const& rhs ) {
+ return lhs.m_name == rhs.m_name &&
+ lhs.m_outputFileName == rhs.m_outputFileName &&
+ lhs.m_colourMode == rhs.m_colourMode &&
+ lhs.m_customOptions == rhs.m_customOptions;
+ }
+
+ Optional<ReporterSpec> parseReporterSpec( StringRef reporterSpec ) {
+ auto parts = Detail::splitReporterSpec( reporterSpec );
+
+ assert( parts.size() > 0 && "Split should never return empty vector" );
+
+ std::map<std::string, std::string> kvPairs;
+ Optional<std::string> outputFileName;
+ Optional<ColourMode> colourMode;
+
+ // First part is always reporter name, so we skip it
+ for ( size_t i = 1; i < parts.size(); ++i ) {
+ auto kv = splitKVPair( parts[i] );
+ auto key = kv.key, value = kv.value;
+
+ if ( key.empty() || value.empty() ) {
+ return {};
+ } else if ( key[0] == 'X' ) {
+ // This is a reporter-specific option, we don't check these
+ // apart from basic sanity checks
+ if ( key.size() == 1 ) {
+ return {};
+ }
+
+ auto ret = kvPairs.emplace( kv.key, kv.value );
+ if ( !ret.second ) {
+ // Duplicated key. We might want to handle this differently,
+ // e.g. by overwriting the existing value?
+ return {};
+ }
+ } else if ( key == "out" ) {
+ // Duplicated key
+ if ( outputFileName ) {
+ return {};
+ }
+ outputFileName = static_cast<std::string>( value );
+ } else if ( key == "colour-mode" ) {
+ // Duplicated key
+ if ( colourMode ) {
+ return {};
+ }
+ colourMode = Detail::stringToColourMode( value );
+ // Parsing failed
+ if ( !colourMode ) {
+ return {};
+ }
+ } else {
+ // Unrecognized option
+ return {};
+ }
+ }
+
+ return ReporterSpec{ CATCH_MOVE( parts[0] ),
+ CATCH_MOVE( outputFileName ),
+ CATCH_MOVE( colourMode ),
+ CATCH_MOVE( kvPairs ) };
+ }
+
+ReporterSpec::ReporterSpec(
+ std::string name,
+ Optional<std::string> outputFileName,
+ Optional<ColourMode> colourMode,
+ std::map<std::string, std::string> customOptions ):
+ m_name( CATCH_MOVE( name ) ),
+ m_outputFileName( CATCH_MOVE( outputFileName ) ),
+ m_colourMode( CATCH_MOVE( colourMode ) ),
+ m_customOptions( CATCH_MOVE( customOptions ) ) {}
+
+} // namespace Catch
+
+
+
namespace Catch {
bool isOk( ResultWas::OfType resultType ) {
@@ -4200,6 +4852,60 @@ namespace Catch {
+#include <cstdio>
+#include <sstream>
+#include <vector>
+
+namespace Catch {
+
+ // This class encapsulates the idea of a pool of ostringstreams that can be reused.
+ struct StringStreams {
+ std::vector<Detail::unique_ptr<std::ostringstream>> m_streams;
+ std::vector<std::size_t> m_unused;
+ std::ostringstream m_referenceStream; // Used for copy state/ flags from
+
+ auto add() -> std::size_t {
+ if( m_unused.empty() ) {
+ m_streams.push_back( Detail::make_unique<std::ostringstream>() );
+ return m_streams.size()-1;
+ }
+ else {
+ auto index = m_unused.back();
+ m_unused.pop_back();
+ return index;
+ }
+ }
+
+ void release( std::size_t index ) {
+ m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
+ m_unused.push_back(index);
+ }
+ };
+
+ ReusableStringStream::ReusableStringStream()
+ : m_index( Singleton<StringStreams>::getMutable().add() ),
+ m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
+ {}
+
+ ReusableStringStream::~ReusableStringStream() {
+ static_cast<std::ostringstream*>( m_oss )->str("");
+ m_oss->clear();
+ Singleton<StringStreams>::getMutable().release( m_index );
+ }
+
+ std::string ReusableStringStream::str() const {
+ return static_cast<std::ostringstream*>( m_oss )->str();
+ }
+
+ void ReusableStringStream::str( std::string const& str ) {
+ static_cast<std::ostringstream*>( m_oss )->str( str );
+ }
+
+
+}
+
+
+
#include <cassert>
#include <algorithm>
@@ -4213,10 +4919,10 @@ namespace Catch {
GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: TrackerBase( nameAndLocation, ctx, parent )
{}
- ~GeneratorTracker();
+ ~GeneratorTracker() override;
static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
- std::shared_ptr<GeneratorTracker> tracker;
+ GeneratorTracker* tracker;
ITracker& currentTracker = ctx.currentTracker();
// Under specific circumstances, the generator we want
@@ -4230,18 +4936,23 @@ namespace Catch {
// }
//
// without it, the code above creates 5 nested generators.
- if (currentTracker.nameAndLocation() == nameAndLocation) {
- auto thisTracker = currentTracker.parent().findChild(nameAndLocation);
- assert(thisTracker);
- assert(thisTracker->isGeneratorTracker());
- tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker);
- } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ if ( currentTracker.nameAndLocation() == nameAndLocation ) {
+ auto thisTracker =
+ currentTracker.parent()->findChild( nameAndLocation );
+ assert( thisTracker );
+ assert( thisTracker->isGeneratorTracker() );
+ tracker = static_cast<GeneratorTracker*>( thisTracker );
+ } else if ( ITracker* childTracker =
+ currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isGeneratorTracker() );
- tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
+ tracker = static_cast<GeneratorTracker*>( childTracker );
} else {
- tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
- currentTracker.addChild( tracker );
+ auto newTracker =
+ Catch::Detail::make_unique<GeneratorTracker>(
+ nameAndLocation, ctx, &currentTracker );
+ tracker = newTracker.get();
+ currentTracker.addChild( CATCH_MOVE(newTracker) );
}
if( !tracker->isComplete() ) {
@@ -4265,13 +4976,53 @@ namespace Catch {
// `SECTION`s.
// **The check for m_children.empty cannot be removed**.
// doing so would break `GENERATE` _not_ followed by `SECTION`s.
- const bool should_wait_for_child =
- !m_children.empty() &&
- std::find_if( m_children.begin(),
- m_children.end(),
- []( TestCaseTracking::ITrackerPtr tracker ) {
- return tracker->hasStarted();
- } ) == m_children.end();
+ const bool should_wait_for_child = [&]() {
+ // No children -> nobody to wait for
+ if ( m_children.empty() ) {
+ return false;
+ }
+ // If at least one child started executing, don't wait
+ if ( std::find_if(
+ m_children.begin(),
+ m_children.end(),
+ []( TestCaseTracking::ITrackerPtr const& tracker ) {
+ return tracker->hasStarted();
+ } ) != m_children.end() ) {
+ return false;
+ }
+
+ // No children have started. We need to check if they _can_
+ // start, and thus we should wait for them, or they cannot
+ // start (due to filters), and we shouldn't wait for them
+ ITracker* parent = m_parent;
+ // This is safe: there is always at least one section
+ // tracker in a test case tracking tree
+ while ( !parent->isSectionTracker() ) {
+ parent = parent->parent();
+ }
+ assert( parent &&
+ "Missing root (test case) level section" );
+
+ auto const& parentSection =
+ static_cast<SectionTracker const&>( *parent );
+ auto const& filters = parentSection.getFilters();
+ // No filters -> no restrictions on running sections
+ if ( filters.empty() ) {
+ return true;
+ }
+
+ for ( auto const& child : m_children ) {
+ if ( child->isSectionTracker() &&
+ std::find(
+ filters.begin(),
+ filters.end(),
+ static_cast<SectionTracker const&>( *child )
+ .trimmedName() ) != filters.end() ) {
+ return true;
+ }
+ }
+ return false;
+ }();
// This check is a bit tricky, because m_generator->next()
// has a side-effect, where it consumes generator's current
@@ -4279,7 +5030,7 @@ namespace Catch {
// this generator is still waiting for any child to start.
if ( should_wait_for_child ||
( m_runState == CompletedSuccessfully &&
- m_generator->next() ) ) {
+ m_generator->countedNext() ) ) {
m_children.clear();
m_runState = Executing;
}
@@ -4290,21 +5041,20 @@ namespace Catch {
return m_generator;
}
void setGenerator( GeneratorBasePtr&& generator ) override {
- m_generator = std::move( generator );
+ m_generator = CATCH_MOVE( generator );
}
};
- GeneratorTracker::~GeneratorTracker() {}
+ GeneratorTracker::~GeneratorTracker() = default;
}
- RunContext::RunContext(IConfig const* _config, IStreamingReporterPtr&& reporter)
+ RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
: m_runInfo(_config->name()),
m_context(getCurrentMutableContext()),
m_config(_config),
- m_reporter(std::move(reporter)),
+ m_reporter(CATCH_MOVE(reporter)),
m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
{
- m_context.setRunner(this);
m_context.setResultCapture(this);
m_reporter->testRunStarting(m_runInfo);
}
@@ -4313,16 +5063,8 @@ namespace Catch {
m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
}
- void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) {
- m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount));
- }
-
- void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) {
- m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting()));
- }
-
Totals RunContext::runTest(TestCaseHandle const& testCase) {
- Totals prevTotals = m_totals;
+ const Totals prevTotals = m_totals;
std::string redirectedCout;
std::string redirectedCerr;
@@ -4337,10 +5079,58 @@ namespace Catch {
ITracker& rootTracker = m_trackerContext.startRun();
assert(rootTracker.isSectionTracker());
static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
+
+ // We intentionally only seed the internal RNG once per test case,
+ // before it is first invoked. The reason for that is a complex
+ // interplay of generator/section implementation details and the
+ // Random*Generator types.
+ //
+ // The issue boils down to us needing to seed the Random*Generators
+ // with different seed each, so that they return different sequences
+ // of random numbers. We do this by giving them a number from the
+ // shared RNG instance as their seed.
+ //
+ // However, this runs into an issue if the reseeding happens each
+ // time the test case is entered (as opposed to first time only),
+ // because multiple generators could get the same seed, e.g. in
+ // ```cpp
+ // TEST_CASE() {
+ // auto i = GENERATE(take(10, random(0, 100));
+ // SECTION("A") {
+ // auto j = GENERATE(take(10, random(0, 100));
+ // }
+ // SECTION("B") {
+ // auto k = GENERATE(take(10, random(0, 100));
+ // }
+ // }
+ // ```
+ // `i` and `j` would properly return values from different sequences,
+ // but `i` and `k` would return the same sequence, because their seed
+ // would be the same.
+ // (The reason their seeds would be the same is that the generator
+ // for k would be initialized when the test case is entered the second
+ // time, after the shared RNG instance was reset to the same value
+ // it had when the generator for i was initialized.)
+ seedRng( *m_config );
+
+ uint64_t testRuns = 0;
do {
m_trackerContext.startCycle();
m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
- runCurrentTest(redirectedCout, redirectedCerr);
+
+ m_reporter->testCasePartialStarting(testInfo, testRuns);
+
+ const auto beforeRunTotals = m_totals;
+ std::string oneRunCout, oneRunCerr;
+ runCurrentTest(oneRunCout, oneRunCerr);
+ redirectedCout += oneRunCout;
+ redirectedCerr += oneRunCerr;
+
+ const auto singleRunTotals = m_totals.delta(beforeRunTotals);
+ auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting());
+
+ m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
+ ++testRuns;
} while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
Totals deltaTotals = m_totals.delta(prevTotals);
@@ -4367,9 +5157,11 @@ namespace Catch {
if (result.getResultType() == ResultWas::Ok) {
m_totals.assertions.passed++;
m_lastAssertionPassed = true;
- } else if (!result.isOk()) {
+ } else if (!result.succeeded()) {
m_lastAssertionPassed = false;
- if( m_activeTestCase->getTestCaseInfo().okToFail() )
+ if (result.isOk()) {
+ }
+ else if( m_activeTestCase->getTestCaseInfo().okToFail() )
m_totals.assertions.failedButOk++;
else
m_totals.assertions.failed++;
@@ -4378,9 +5170,7 @@ namespace Catch {
m_lastAssertionPassed = true;
}
- // We have no use for the return value (whether messages should be cleared), because messages were made scoped
- // and should be let to clear themselves out.
- static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+ m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
if (result.getResultType() != ResultWas::Warning)
m_messageScopes.clear();
@@ -4452,7 +5242,7 @@ namespace Catch {
m_unfinishedSections.push_back(endInfo);
}
- void RunContext::benchmarkPreparing(std::string const& name) {
+ void RunContext::benchmarkPreparing( StringRef name ) {
m_reporter->benchmarkPreparing(name);
}
void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
@@ -4461,8 +5251,8 @@ namespace Catch {
void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
m_reporter->benchmarkEnded( stats );
}
- void RunContext::benchmarkFailed(std::string const & error) {
- m_reporter->benchmarkFailed(error);
+ void RunContext::benchmarkFailed( StringRef error ) {
+ m_reporter->benchmarkFailed( error );
}
void RunContext::pushScopedMessage(MessageInfo const & message) {
@@ -4525,7 +5315,6 @@ namespace Catch {
std::string(),
false));
m_totals.testCases.failed++;
- testGroupEnded(std::string(), m_totals, 1, 1);
m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
}
@@ -4553,8 +5342,6 @@ namespace Catch {
m_shouldReportUnexpected = true;
m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
- seedRng(*m_config);
-
Timer timer;
CATCH_TRY {
if (m_reporter->getPreferences().shouldRedirectStdOut) {
@@ -4596,10 +5383,10 @@ namespace Catch {
}
void RunContext::invokeActiveTestCase() {
- // We need to register a handler for signals/structured exceptions
+ // We need to engage a handler for signals/structured exceptions
// before running the tests themselves, or the binary can crash
// without failed test being reported.
- FatalConditionHandler _;
+ FatalConditionHandlerGuard _(&m_fatalConditionhandler);
m_activeTestCase->invoke();
}
@@ -4655,7 +5442,7 @@ namespace Catch {
void RunContext::handleMessage(
AssertionInfo const& info,
ResultWas::OfType resultType,
- StringRef const& message,
+ StringRef message,
AssertionReaction& reaction
) {
m_reporter->assertionStarting( info );
@@ -4729,10 +5516,7 @@ namespace Catch {
}
void seedRng(IConfig const& config) {
- if (config.rngSeed() != 0) {
- std::srand(config.rngSeed());
- rng().seed(config.rngSeed());
- }
+ sharedRng().seed(config.rngSeed());
}
unsigned int rngSeed() {
@@ -4743,12 +5527,10 @@ namespace Catch {
-#include <utility>
-
namespace Catch {
Section::Section( SectionInfo&& info ):
- m_info( std::move( info ) ),
+ m_info( CATCH_MOVE( info ) ),
m_sectionIncluded(
getResultCapture().sectionStarted( m_info, m_assertions ) ) {
// Non-"included" sections will not use the timing information
@@ -4791,7 +5573,7 @@ namespace Catch {
}
}
- ISingleton::~ISingleton() {}
+ ISingleton::~ISingleton() = default;
void addSingleton(ISingleton* singleton ) {
getSingletons()->push_back( singleton );
@@ -4808,182 +5590,47 @@ namespace Catch {
-#include <cstdio>
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include <vector>
+#include <cstring>
+#include <ostream>
namespace Catch {
- Catch::IStream::~IStream() = default;
-
-namespace Detail {
- namespace {
- template<typename WriterF, std::size_t bufferSize=256>
- class StreamBufImpl : public std::streambuf {
- char data[bufferSize];
- WriterF m_writer;
-
- public:
- StreamBufImpl() {
- setp( data, data + sizeof(data) );
- }
-
- ~StreamBufImpl() noexcept {
- StreamBufImpl::sync();
- }
-
- private:
- int overflow( int c ) override {
- sync();
-
- if( c != EOF ) {
- if( pbase() == epptr() )
- m_writer( std::string( 1, static_cast<char>( c ) ) );
- else
- sputc( static_cast<char>( c ) );
- }
- return 0;
- }
-
- int sync() override {
- if( pbase() != pptr() ) {
- m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
- setp( pbase(), epptr() );
- }
- return 0;
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- struct OutputDebugWriter {
-
- void operator()( std::string const&str ) {
- writeToDebugConsole( str );
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- class FileStream : public IStream {
- mutable std::ofstream m_ofs;
- public:
- FileStream( StringRef filename ) {
- m_ofs.open( filename.c_str() );
- CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
- }
- ~FileStream() override = default;
- public: // IStream
- std::ostream& stream() const override {
- return m_ofs;
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- class CoutStream : public IStream {
- mutable std::ostream m_os;
- public:
- // Store the streambuf from cout up-front because
- // cout may get redirected when running tests
- CoutStream() : m_os( Catch::cout().rdbuf() ) {}
- ~CoutStream() override = default;
-
- public: // IStream
- std::ostream& stream() const override { return m_os; }
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- class DebugOutStream : public IStream {
- Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
- mutable std::ostream m_os;
- public:
- DebugOutStream()
- : m_streamBuf( Detail::make_unique<StreamBufImpl<OutputDebugWriter>>() ),
- m_os( m_streamBuf.get() )
- {}
-
- ~DebugOutStream() override = default;
-
- public: // IStream
- std::ostream& stream() const override { return m_os; }
- };
-
- } // unnamed namespace
-} // namespace Detail
-
- ///////////////////////////////////////////////////////////////////////////
-
- auto makeStream( StringRef const &filename ) -> IStream const* {
- if( filename.empty() )
- return new Detail::CoutStream();
- else if( filename[0] == '%' ) {
- if( filename == "%debug" )
- return new Detail::DebugOutStream();
- else
- CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
- }
- else
- return new Detail::FileStream( filename );
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
+ return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+ }
+ bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept {
+ // We can assume that the same file will usually have the same pointer.
+ // Thus, if the pointers are the same, there is no point in calling the strcmp
+ return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0));
}
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+ os << info.file << '(' << info.line << ')';
+#else
+ os << info.file << ':' << info.line;
+#endif
+ return os;
+ }
- // This class encapsulates the idea of a pool of ostringstreams that can be reused.
- struct StringStreams {
- std::vector<Detail::unique_ptr<std::ostringstream>> m_streams;
- std::vector<std::size_t> m_unused;
- std::ostringstream m_referenceStream; // Used for copy state/ flags from
-
- auto add() -> std::size_t {
- if( m_unused.empty() ) {
- m_streams.push_back( Detail::unique_ptr<std::ostringstream>( new std::ostringstream ) );
- return m_streams.size()-1;
- }
- else {
- auto index = m_unused.back();
- m_unused.pop_back();
- return index;
- }
- }
-
- void release( std::size_t index ) {
- m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
- m_unused.push_back(index);
- }
- };
-
- ReusableStringStream::ReusableStringStream()
- : m_index( Singleton<StringStreams>::getMutable().add() ),
- m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
- {}
+} // end namespace Catch
- ReusableStringStream::~ReusableStringStream() {
- static_cast<std::ostringstream*>( m_oss )->str("");
- m_oss->clear();
- Singleton<StringStreams>::getMutable().release( m_index );
- }
- std::string ReusableStringStream::str() const {
- return static_cast<std::ostringstream*>( m_oss )->str();
- }
- void ReusableStringStream::str( std::string const& str ) {
- static_cast<std::ostringstream*>( m_oss )->str( str );
- }
- ///////////////////////////////////////////////////////////////////////////
+#include <iostream>
+namespace Catch {
-#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+// If you #define this you must implement these functions
+#if !defined( CATCH_CONFIG_NOSTDOUT )
std::ostream& cout() { return std::cout; }
std::ostream& cerr() { return std::cerr; }
std::ostream& clog() { return std::clog; }
#endif
-}
+
+} // namespace Catch
@@ -4995,16 +5642,10 @@ namespace Detail {
namespace Catch {
- namespace {
- char toLowerCh(char c) {
- return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) );
- }
- }
-
bool startsWith( std::string const& s, std::string const& prefix ) {
return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
}
- bool startsWith( std::string const& s, char prefix ) {
+ bool startsWith( StringRef s, char prefix ) {
return !s.empty() && s[0] == prefix;
}
bool endsWith( std::string const& s, std::string const& suffix ) {
@@ -5017,13 +5658,19 @@ namespace Catch {
return s.find( infix ) != std::string::npos;
}
void toLowerInPlace( std::string& s ) {
- std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+ std::transform( s.begin(), s.end(), s.begin(), []( char c ) {
+ return toLower( c );
+ } );
}
std::string toLower( std::string const& s ) {
std::string lc = s;
toLowerInPlace( lc );
return lc;
}
+ char toLower(char c) {
+ return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
+ }
+
std::string trim( std::string const& str ) {
static char const* whitespaceChars = "\n\r\t ";
std::string::size_type start = str.find_first_not_of( whitespaceChars );
@@ -5073,11 +5720,6 @@ namespace Catch {
return subStrings;
}
- pluralise::pluralise( std::size_t count, std::string const& label )
- : m_count( count ),
- m_label( label )
- {}
-
std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
os << pluraliser.m_count << ' ' << pluraliser.m_label;
if( pluraliser.m_count != 1 )
@@ -5096,28 +5738,44 @@ namespace Catch {
namespace Catch {
StringRef::StringRef( char const* rawChars ) noexcept
- : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
+ : StringRef( rawChars, std::strlen(rawChars) )
{}
- auto StringRef::c_str() const -> char const* {
- CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
- return m_start;
- }
-
- auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
+ auto StringRef::operator == ( StringRef other ) const noexcept -> bool {
return m_size == other.m_size
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
}
- bool StringRef::operator<(StringRef const& rhs) const noexcept {
+ bool StringRef::operator<(StringRef rhs) const noexcept {
if (m_size < rhs.m_size) {
return strncmp(m_start, rhs.m_start, m_size) <= 0;
}
return strncmp(m_start, rhs.m_start, rhs.m_size) < 0;
}
- auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
- return os.write(str.data(), str.size());
+ int StringRef::compare( StringRef rhs ) const {
+ auto cmpResult =
+ strncmp( m_start, rhs.m_start, std::min( m_size, rhs.m_size ) );
+
+ // This means that strncmp found a difference before the strings
+ // ended, and we can return it directly
+ if ( cmpResult != 0 ) {
+ return cmpResult;
+ }
+
+ // If strings are equal up to length, then their comparison results on
+ // their size
+ if ( m_size < rhs.m_size ) {
+ return -1;
+ } else if ( m_size > rhs.m_size ) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ auto operator << ( std::ostream& os, StringRef str ) -> std::ostream& {
+ return os.write(str.data(), static_cast<std::streamsize>(str.size()));
}
std::string operator+(StringRef lhs, StringRef rhs) {
@@ -5128,7 +5786,7 @@ namespace Catch {
return ret;
}
- auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
+ auto operator+=( std::string& lhs, StringRef rhs ) -> std::string& {
lhs.append(rhs.data(), rhs.size());
return lhs;
}
@@ -5172,7 +5830,7 @@ namespace Catch {
<< "\tRedefined at: " << lineInfo );
}
- ITagAliasRegistry::~ITagAliasRegistry() {}
+ ITagAliasRegistry::~ITagAliasRegistry() = default;
ITagAliasRegistry const& ITagAliasRegistry::get() {
return getRegistryHub().getTagAliasRegistry();
@@ -5183,33 +5841,42 @@ namespace Catch {
-#include <algorithm>
-#include <set>
-
namespace Catch {
-
-namespace {
- struct HashTest {
- explicit HashTest(SimplePcg32& rng_inst) {
- basis = rng_inst();
- basis <<= 32;
- basis |= rng_inst();
- }
-
- uint64_t basis;
-
- uint64_t operator()(TestCaseInfo const& t) const {
- // Modified FNV-1a hash
- static constexpr uint64_t prime = 1099511628211;
- uint64_t hash = basis;
- for (const char c : t.name) {
+ TestCaseInfoHasher::TestCaseInfoHasher( hash_t seed ): m_seed( seed ) {}
+
+ uint32_t TestCaseInfoHasher::operator()( TestCaseInfo const& t ) const {
+ // FNV-1a hash algorithm that is designed for uniqueness:
+ const hash_t prime = 1099511628211u;
+ hash_t hash = 14695981039346656037u;
+ for ( const char c : t.name ) {
+ hash ^= c;
+ hash *= prime;
+ }
+ for ( const char c : t.className ) {
+ hash ^= c;
+ hash *= prime;
+ }
+ for ( const Tag& tag : t.tags ) {
+ for ( const char c : tag.original ) {
hash ^= c;
hash *= prime;
}
- return hash;
}
- };
-} // end anonymous namespace
+ hash ^= m_seed;
+ hash *= prime;
+ const uint32_t low{ static_cast<uint32_t>( hash ) };
+ const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) };
+ return low * high;
+ }
+} // namespace Catch
+
+
+
+
+#include <algorithm>
+#include <set>
+
+namespace Catch {
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) {
switch (config.runOrder()) {
@@ -5218,20 +5885,36 @@ namespace {
case TestRunOrder::LexicographicallySorted: {
std::vector<TestCaseHandle> sorted = unsortedTestCases;
- std::sort(sorted.begin(), sorted.end());
+ std::sort(
+ sorted.begin(),
+ sorted.end(),
+ []( TestCaseHandle const& lhs, TestCaseHandle const& rhs ) {
+ return lhs.getTestCaseInfo() < rhs.getTestCaseInfo();
+ }
+ );
return sorted;
}
case TestRunOrder::Randomized: {
seedRng(config);
- HashTest h(rng());
- std::vector<std::pair<uint64_t, TestCaseHandle>> indexed_tests;
+ using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>;
+
+ TestCaseInfoHasher h{ config.rngSeed() };
+ std::vector<TestWithHash> indexed_tests;
indexed_tests.reserve(unsortedTestCases.size());
for (auto const& handle : unsortedTestCases) {
indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle);
}
- std::sort(indexed_tests.begin(), indexed_tests.end());
+ std::sort( indexed_tests.begin(),
+ indexed_tests.end(),
+ []( TestWithHash const& lhs, TestWithHash const& rhs ) {
+ if ( lhs.first == rhs.first ) {
+ return lhs.second.getTestCaseInfo() <
+ rhs.second.getTestCaseInfo();
+ }
+ return lhs.first < rhs.first;
+ } );
std::vector<TestCaseHandle> randomized;
randomized.reserve(indexed_tests.size());
@@ -5255,14 +5938,22 @@ namespace {
return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config );
}
- void enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& functions ) {
- std::set<TestCaseHandle> seenFunctions;
- for( auto const& function : functions ) {
- auto prev = seenFunctions.insert( function );
- CATCH_ENFORCE( prev.second,
- "error: TEST_CASE( \"" << function.getTestCaseInfo().name << "\" ) already defined.\n"
- << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
- << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
+ void
+ enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& tests ) {
+ auto testInfoCmp = []( TestCaseInfo const* lhs,
+ TestCaseInfo const* rhs ) {
+ return *lhs < *rhs;
+ };
+ std::set<TestCaseInfo const*, decltype(testInfoCmp)> seenTests(testInfoCmp);
+ for ( auto const& test : tests ) {
+ const auto infoPtr = &test.getTestCaseInfo();
+ const auto prev = seenTests.insert( infoPtr );
+ CATCH_ENFORCE(
+ prev.second,
+ "error: test case \"" << infoPtr->name << "\", with tags \""
+ << infoPtr->tagsAsString() << "\" already defined.\n"
+ << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n"
+ << "\tRedefined at " << infoPtr->lineInfo );
}
}
@@ -5275,7 +5966,7 @@ namespace {
filtered.push_back(testCase);
}
}
- return filtered;
+ return createShard(filtered, config.shardCount(), config.shardIndex());
}
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config ) {
return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
@@ -5284,8 +5975,8 @@ namespace {
void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) {
m_handles.emplace_back(testInfo.get(), testInvoker.get());
m_viewed_test_infos.push_back(testInfo.get());
- m_owned_test_infos.push_back(std::move(testInfo));
- m_invokers.push_back(std::move(testInvoker));
+ m_owned_test_infos.push_back(CATCH_MOVE(testInfo));
+ m_invokers.push_back(CATCH_MOVE(testInvoker));
}
std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const {
@@ -5313,19 +6004,6 @@ namespace {
m_testAsFunction();
}
- std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
- std::string className(classOrQualifiedMethodName);
- if( startsWith( className, '&' ) )
- {
- std::size_t lastColons = className.rfind( "::" );
- std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
- if( penultimateColons == std::string::npos )
- penultimateColons = 1;
- className = className.substr( penultimateColons, lastColons-penultimateColons );
- }
- return className;
- }
-
} // end namespace Catch
@@ -5333,7 +6011,6 @@ namespace {
#include <algorithm>
#include <cassert>
-#include <memory>
#if defined(__clang__)
# pragma clang diagnostic push
@@ -5351,11 +6028,15 @@ namespace TestCaseTracking {
ITracker::~ITracker() = default;
- void ITracker::addChild( ITrackerPtr const& child ) {
- m_children.push_back( child );
+ void ITracker::markAsNeedingAnotherRun() {
+ m_runState = NeedsAnotherRun;
+ }
+
+ void ITracker::addChild( ITrackerPtr&& child ) {
+ m_children.push_back( CATCH_MOVE(child) );
}
- ITrackerPtr ITracker::findChild( NameAndLocation const& nameAndLocation ) {
+ ITracker* ITracker::findChild( NameAndLocation const& nameAndLocation ) {
auto it = std::find_if(
m_children.begin(),
m_children.end(),
@@ -5364,14 +6045,37 @@ namespace TestCaseTracking {
nameAndLocation.location &&
tracker->nameAndLocation().name == nameAndLocation.name;
} );
- return ( it != m_children.end() ) ? *it : nullptr;
+ return ( it != m_children.end() ) ? it->get() : nullptr;
}
+ bool ITracker::isSectionTracker() const { return false; }
+ bool ITracker::isGeneratorTracker() const { return false; }
+ bool ITracker::isSuccessfullyCompleted() const {
+ return m_runState == CompletedSuccessfully;
+ }
+
+ bool ITracker::isOpen() const {
+ return m_runState != NotStarted && !isComplete();
+ }
+
+ bool ITracker::hasStarted() const { return m_runState != NotStarted; }
+
+ void ITracker::openChild() {
+ if (m_runState != ExecutingChildren) {
+ m_runState = ExecutingChildren;
+ if (m_parent) {
+ m_parent->openChild();
+ }
+ }
+ }
ITracker& TrackerContext::startRun() {
using namespace std::string_literals;
- m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ), *this, nullptr );
+ m_rootTracker = Catch::Detail::make_unique<SectionTracker>(
+ NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ),
+ *this,
+ nullptr );
m_currentTracker = nullptr;
m_runState = Executing;
return *m_rootTracker;
@@ -5403,36 +6107,13 @@ namespace TestCaseTracking {
TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
- ITracker(nameAndLocation),
- m_ctx( ctx ),
- m_parent( parent )
+ ITracker(nameAndLocation, parent),
+ m_ctx( ctx )
{}
bool TrackerBase::isComplete() const {
return m_runState == CompletedSuccessfully || m_runState == Failed;
}
- bool TrackerBase::isSuccessfullyCompleted() const {
- return m_runState == CompletedSuccessfully;
- }
- bool TrackerBase::isOpen() const {
- return m_runState != NotStarted && !isComplete();
- }
-
- ITracker& TrackerBase::parent() {
- assert( m_parent ); // Should always be non-null except for root
- return *m_parent;
- }
-
- void TrackerBase::openChild() {
- if( m_runState != ExecutingChildren ) {
- m_runState = ExecutingChildren;
- if( m_parent )
- m_parent->openChild();
- }
- }
-
- bool TrackerBase::isSectionTracker() const { return false; }
- bool TrackerBase::isGeneratorTracker() const { return false; }
void TrackerBase::open() {
m_runState = Executing;
@@ -5477,9 +6158,6 @@ namespace TestCaseTracking {
moveToParent();
m_ctx.completeCycle();
}
- void TrackerBase::markAsNeedingAnotherRun() {
- m_runState = NeedsAnotherRun;
- }
void TrackerBase::moveToParent() {
assert( m_parent );
@@ -5495,7 +6173,7 @@ namespace TestCaseTracking {
{
if( parent ) {
while( !parent->isSectionTracker() )
- parent = &parent->parent();
+ parent = parent->parent();
SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
addNextFilters( parentSection.m_filters );
@@ -5506,7 +6184,7 @@ namespace TestCaseTracking {
bool complete = true;
if (m_filters.empty()
- || m_filters[0] == ""
+ || m_filters[0].empty()
|| std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
complete = TrackerBase::isComplete();
}
@@ -5516,17 +6194,19 @@ namespace TestCaseTracking {
bool SectionTracker::isSectionTracker() const { return true; }
SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
- std::shared_ptr<SectionTracker> section;
+ SectionTracker* section;
ITracker& currentTracker = ctx.currentTracker();
- if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ if ( ITracker* childTracker =
+ currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isSectionTracker() );
- section = std::static_pointer_cast<SectionTracker>( childTracker );
- }
- else {
- section = std::make_shared<SectionTracker>( nameAndLocation, ctx, &currentTracker );
- currentTracker.addChild( section );
+ section = static_cast<SectionTracker*>( childTracker );
+ } else {
+ auto newSection = Catch::Detail::make_unique<SectionTracker>(
+ nameAndLocation, ctx, &currentTracker );
+ section = newSection.get();
+ currentTracker.addChild( CATCH_MOVE( newSection ) );
}
if( !ctx.completedCycle() )
section->tryOpen();
@@ -5541,21 +6221,25 @@ namespace TestCaseTracking {
void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
if( !filters.empty() ) {
m_filters.reserve( m_filters.size() + filters.size() + 2 );
- m_filters.emplace_back(""); // Root - should never be consulted
- m_filters.emplace_back(""); // Test Case - not a section filter
+ m_filters.emplace_back(StringRef{}); // Root - should never be consulted
+ m_filters.emplace_back(StringRef{}); // Test Case - not a section filter
m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
}
}
- void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
+ void SectionTracker::addNextFilters( std::vector<StringRef> const& filters ) {
if( filters.size() > 1 )
m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
}
-} // namespace TestCaseTracking
+ std::vector<StringRef> const& SectionTracker::getFilters() const {
+ return m_filters;
+ }
+
+ StringRef SectionTracker::trimmedName() const {
+ return m_trimmed_name;
+ }
-using TestCaseTracking::ITracker;
-using TestCaseTracking::TrackerContext;
-using TestCaseTracking::SectionTracker;
+} // namespace TestCaseTracking
} // namespace Catch
@@ -5565,13 +6249,46 @@ using TestCaseTracking::SectionTracker;
+#include <algorithm>
+#include <iterator>
+
namespace Catch {
+ namespace {
+ StringRef extractClassName( StringRef classOrMethodName ) {
+ if ( !startsWith( classOrMethodName, '&' ) ) {
+ return classOrMethodName;
+ }
+
+ // Remove the leading '&' to avoid having to special case it later
+ const auto methodName =
+ classOrMethodName.substr( 1, classOrMethodName.size() );
+
+ auto reverseStart = std::make_reverse_iterator( methodName.end() );
+ auto reverseEnd = std::make_reverse_iterator( methodName.begin() );
+
+ // We make a simplifying assumption that ":" is only present
+ // in the input as part of "::" from C++ typenames (this is
+ // relatively safe assumption because the input is generated
+ // as stringification of type through preprocessor).
+ auto lastColons = std::find( reverseStart, reverseEnd, ':' ) + 1;
+ auto secondLastColons =
+ std::find( lastColons + 1, reverseEnd, ':' );
+
+ auto const startIdx = reverseEnd - secondLastColons;
+ auto const classNameSize = secondLastColons - lastColons - 1;
+
+ return methodName.substr(
+ static_cast<std::size_t>( startIdx ),
+ static_cast<std::size_t>( classNameSize ) );
+ }
+ } // namespace
+
Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) {
- return Detail::unique_ptr<ITestInvoker>( new TestInvokerAsFunction( testAsFunction ));
+ return Detail::make_unique<TestInvokerAsFunction>( testAsFunction );
}
- AutoReg::AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept {
+ AutoReg::AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept {
CATCH_TRY {
getMutableRegistryHub()
.registerTest(
@@ -5579,7 +6296,7 @@ namespace Catch {
extractClassName( classOrMethod ),
nameAndTags,
lineInfo),
- std::move(invoker)
+ CATCH_MOVE(invoker)
);
} CATCH_CATCH_ALL {
// Do not throw when constructing global objects, instead register the exception to be processed later
@@ -5608,7 +6325,7 @@ namespace Catch {
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
//if visitChar fails
if( !visitChar( m_arg[m_pos] ) ){
- m_testSpec.m_invalidArgs.push_back(arg);
+ m_testSpec.m_invalidSpecs.push_back(arg);
break;
}
endMode();
@@ -5616,7 +6333,7 @@ namespace Catch {
}
TestSpec TestSpecParser::testSpec() {
addFilter();
- return std::move(m_testSpec);
+ return CATCH_MOVE(m_testSpec);
}
bool TestSpecParser::visitChar( char c ) {
if( (m_mode != EscapedName) && (c == '\\') ) {
@@ -5732,7 +6449,7 @@ namespace Catch {
void TestSpecParser::addFilter() {
if( !m_currentFilter.m_required.empty() || !m_currentFilter.m_forbidden.empty() ) {
- m_testSpec.m_filters.push_back( std::move(m_currentFilter) );
+ m_testSpec.m_filters.push_back( CATCH_MOVE(m_currentFilter) );
m_currentFilter = TestSpec::Filter();
}
}
@@ -5825,6 +6542,8 @@ namespace Catch {
} // namespace Catch
+
+#include <algorithm>
#include <cstring>
#include <ostream>
@@ -5858,96 +6577,107 @@ namespace {
namespace Catch {
namespace TextFlow {
- void Column::iterator::calcLength() {
- m_suffix = false;
- auto width = m_column.m_width - indent();
- m_end = m_pos;
+ void Column::const_iterator::calcLength() {
+ m_addHyphen = false;
+ m_parsedTo = m_lineStart;
+
std::string const& current_line = m_column.m_string;
- if ( current_line[m_pos] == '\n' ) {
- ++m_end;
+ if ( current_line[m_lineStart] == '\n' ) {
+ ++m_parsedTo;
}
- while ( m_end < current_line.size() &&
- current_line[m_end] != '\n' ) {
- ++m_end;
+
+ const auto maxLineLength = m_column.m_width - indentSize();
+ const auto maxParseTo = std::min(current_line.size(), m_lineStart + maxLineLength);
+ while ( m_parsedTo < maxParseTo &&
+ current_line[m_parsedTo] != '\n' ) {
+ ++m_parsedTo;
}
- if ( m_end < m_pos + width ) {
- m_len = m_end - m_pos;
+ // If we encountered a newline before the column is filled,
+ // then we linebreak at the newline and consider this line
+ // finished.
+ if ( m_parsedTo < m_lineStart + maxLineLength ) {
+ m_lineLength = m_parsedTo - m_lineStart;
} else {
- size_t len = width;
- while ( len > 0 && !isBoundary( current_line, m_pos + len ) ) {
- --len;
+ // Look for a natural linebreak boundary in the column
+ // (We look from the end, so that the first found boundary is
+ // the right one)
+ size_t newLineLength = maxLineLength;
+ while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) {
+ --newLineLength;
}
- while ( len > 0 &&
- isWhitespace( current_line[m_pos + len - 1] ) ) {
- --len;
+ while ( newLineLength > 0 &&
+ isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) {
+ --newLineLength;
}
- if ( len > 0 ) {
- m_len = len;
+ // If we found one, then that is where we linebreak
+ if ( newLineLength > 0 ) {
+ m_lineLength = newLineLength;
} else {
- m_suffix = true;
- m_len = width - 1;
+ // Otherwise we have to split text with a hyphen
+ m_addHyphen = true;
+ m_lineLength = maxLineLength - 1;
}
}
}
- size_t Column::iterator::indent() const {
+ size_t Column::const_iterator::indentSize() const {
auto initial =
- m_pos == 0 ? m_column.m_initialIndent : std::string::npos;
+ m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos;
return initial == std::string::npos ? m_column.m_indent : initial;
}
std::string
- Column::iterator::addIndentAndSuffix( size_t position,
+ Column::const_iterator::addIndentAndSuffix( size_t position,
size_t length ) const {
std::string ret;
- const auto desired_indent = indent();
- ret.reserve( desired_indent + length + m_suffix );
+ const auto desired_indent = indentSize();
+ ret.reserve( desired_indent + length + m_addHyphen );
ret.append( desired_indent, ' ' );
ret.append( m_column.m_string, position, length );
- if ( m_suffix ) {
+ if ( m_addHyphen ) {
ret.push_back( '-' );
}
return ret;
}
- Column::iterator::iterator( Column const& column ): m_column( column ) {
+ Column::const_iterator::const_iterator( Column const& column ): m_column( column ) {
assert( m_column.m_width > m_column.m_indent );
assert( m_column.m_initialIndent == std::string::npos ||
m_column.m_width > m_column.m_initialIndent );
calcLength();
- if ( m_len == 0 ) {
- m_pos = m_column.m_string.size();
+ if ( m_lineLength == 0 ) {
+ m_lineStart = m_column.m_string.size();
}
}
- std::string Column::iterator::operator*() const {
- assert( m_pos <= m_end );
- return addIndentAndSuffix( m_pos, m_len );
+ std::string Column::const_iterator::operator*() const {
+ assert( m_lineStart <= m_parsedTo );
+ return addIndentAndSuffix( m_lineStart, m_lineLength );
}
- Column::iterator& Column::iterator::operator++() {
- m_pos += m_len;
+ Column::const_iterator& Column::const_iterator::operator++() {
+ m_lineStart += m_lineLength;
std::string const& current_line = m_column.m_string;
- if ( m_pos < current_line.size() && current_line[m_pos] == '\n' ) {
- m_pos += 1;
+ if ( m_lineStart < current_line.size() && current_line[m_lineStart] == '\n' ) {
+ m_lineStart += 1;
} else {
- while ( m_pos < current_line.size() &&
- isWhitespace( current_line[m_pos] ) ) {
- ++m_pos;
+ while ( m_lineStart < current_line.size() &&
+ isWhitespace( current_line[m_lineStart] ) ) {
+ ++m_lineStart;
}
}
- if ( m_pos != current_line.size() ) {
+ if ( m_lineStart != current_line.size() ) {
calcLength();
}
return *this;
}
- Column::iterator Column::iterator::operator++( int ) {
- iterator prev( *this );
+ Column::const_iterator Column::const_iterator::operator++( int ) {
+ const_iterator prev( *this );
operator++();
return prev;
}
@@ -6100,7 +6830,9 @@ namespace Catch {
}
-
+// Note: swapping these two includes around causes MSVC to error out
+// while in /permissive- mode. No, I don't know why.
+// Tested on VS 2019, 18.{3, 4}.x
#include <iomanip>
#include <type_traits>
@@ -6168,7 +6900,7 @@ namespace {
}
- XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
+ XmlEncode::XmlEncode( StringRef str, ForWhat forWhat )
: m_str( str ),
m_forWhat( forWhat )
{}
@@ -6178,7 +6910,7 @@ namespace {
// (see: http://www.w3.org/TR/xml/#syntax)
for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
- unsigned char c = m_str[idx];
+ unsigned char c = static_cast<unsigned char>(m_str[idx]);
switch (c) {
case '<': os << "&lt;"; break;
case '&': os << "&amp;"; break;
@@ -6238,7 +6970,7 @@ namespace {
bool valid = true;
uint32_t value = headerValue(c);
for (std::size_t n = 1; n < encBytes; ++n) {
- unsigned char nc = m_str[idx + n];
+ unsigned char nc = static_cast<unsigned char>(m_str[idx + n]);
valid &= ((nc & 0xC0) == 0x80);
value = (value << 6) | (nc & 0x3F);
}
@@ -6302,11 +7034,20 @@ namespace {
}
}
- XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
+ XmlWriter::ScopedElement&
+ XmlWriter::ScopedElement::writeText( StringRef text, XmlFormatting fmt ) {
m_writer->writeText( text, fmt );
return *this;
}
+ XmlWriter::ScopedElement&
+ XmlWriter::ScopedElement::writeAttribute( StringRef name,
+ StringRef attribute ) {
+ m_writer->writeAttribute( name, attribute );
+ return *this;
+ }
+
+
XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
{
writeDeclaration();
@@ -6350,7 +7091,7 @@ namespace {
if (shouldIndent(fmt)) {
m_os << m_indent;
}
- m_os << "</" << m_tags.back() << ">";
+ m_os << "</" << m_tags.back() << '>';
}
m_os << std::flush;
applyFormatting(fmt);
@@ -6358,48 +7099,50 @@ namespace {
return *this;
}
- XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
+ XmlWriter& XmlWriter::writeAttribute( StringRef name,
+ StringRef attribute ) {
if( !name.empty() && !attribute.empty() )
m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
return *this;
}
- XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
- m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+ XmlWriter& XmlWriter::writeAttribute( StringRef name, bool attribute ) {
+ writeAttribute(name, (attribute ? "true"_sr : "false"_sr));
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeAttribute( StringRef name,
+ char const* attribute ) {
+ writeAttribute( name, StringRef( attribute ) );
return *this;
}
- XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
+ XmlWriter& XmlWriter::writeText( StringRef text, XmlFormatting fmt ) {
+ CATCH_ENFORCE(!m_tags.empty(), "Cannot write text as top level element");
if( !text.empty() ){
bool tagWasOpen = m_tagIsOpen;
ensureTagClosed();
if (tagWasOpen && shouldIndent(fmt)) {
m_os << m_indent;
}
- m_os << XmlEncode( text );
+ m_os << XmlEncode( text, XmlEncode::ForTextNodes );
applyFormatting(fmt);
}
return *this;
}
- XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
+ XmlWriter& XmlWriter::writeComment( StringRef text, XmlFormatting fmt ) {
ensureTagClosed();
if (shouldIndent(fmt)) {
m_os << m_indent;
}
- m_os << "<!--" << text << "-->";
+ m_os << "<!-- " << text << " -->";
applyFormatting(fmt);
return *this;
}
- void XmlWriter::writeStylesheetRef( std::string const& url ) {
- m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
- }
-
- XmlWriter& XmlWriter::writeBlankLine() {
- ensureTagClosed();
- m_os << '\n';
- return *this;
+ void XmlWriter::writeStylesheetRef( StringRef url ) {
+ m_os << R"(<?xml-stylesheet type="text/xsl" href=")" << url << R"("?>)" << '\n';
}
void XmlWriter::ensureTagClosed() {
@@ -6415,12 +7158,12 @@ namespace {
}
void XmlWriter::writeDeclaration() {
- m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ m_os << R"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n';
}
void XmlWriter::newlineIfNecessary() {
if( m_needsNewline ) {
- m_os << std::endl;
+ m_os << '\n' << std::flush;
m_needsNewline = false;
}
}
@@ -6432,7 +7175,6 @@ namespace {
#include <cmath>
#include <cstdlib>
#include <cstdint>
-#include <cstring>
#include <sstream>
#include <iomanip>
#include <limits>
@@ -6441,20 +7183,6 @@ namespace {
namespace Catch {
namespace {
- int32_t convert(float f) {
- static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
- int32_t i;
- std::memcpy(&i, &f, sizeof(f));
- return i;
- }
-
- int64_t convert(double d) {
- static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
- int64_t i;
- std::memcpy(&i, &d, sizeof(d));
- return i;
- }
-
template <typename FP>
bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
// Comparison with NaN should always be false.
@@ -6463,16 +7191,10 @@ namespace {
return false;
}
- auto lc = convert(lhs);
- auto rc = convert(rhs);
-
- if ((lc < 0) != (rc < 0)) {
- // Potentially we can have +0 and -0
- return lhs == rhs;
- }
+ // This should also handle positive and negative zeros, infinities
+ const auto ulpDist = ulpDistance(lhs, rhs);
- auto ulpDiff = std::abs(lc - rc);
- return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
+ return ulpDist <= maxUlpDiff;
}
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
@@ -6547,6 +7269,9 @@ namespace Detail {
CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double
|| m_ulps < (std::numeric_limits<uint32_t>::max)(),
"Provided ULP is impossibly large for a float comparison.");
+ CATCH_ENFORCE( std::numeric_limits<double>::is_iec559,
+ "WithinUlp matcher only supports platforms with "
+ "IEEE-754 compatible floating point representation" );
}
#if defined(__clang__)
@@ -6584,14 +7309,26 @@ namespace Detail {
ret << " ([";
if (m_type == Detail::FloatingPointKind::Double) {
- write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
+ write( ret,
+ step( m_target,
+ -std::numeric_limits<double>::infinity(),
+ m_ulps ) );
ret << ", ";
- write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
+ write( ret,
+ step( m_target,
+ std::numeric_limits<double>::infinity(),
+ m_ulps ) );
} else {
// We have to cast INFINITY to float because of MinGW, see #1782
- write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
+ write( ret,
+ step( static_cast<float>( m_target ),
+ -std::numeric_limits<float>::infinity(),
+ m_ulps ) );
ret << ", ";
- write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
+ write( ret,
+ step( static_cast<float>( m_target ),
+ std::numeric_limits<float>::infinity(),
+ m_ulps ) );
}
ret << "])";
@@ -6673,7 +7410,7 @@ namespace Matchers {
}
- StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
+ StringMatcherBase::StringMatcherBase( StringRef operation, CasedString const& comparator )
: m_comparator( comparator ),
m_operation( operation ) {
}
@@ -6685,33 +7422,33 @@ namespace Matchers {
description += m_operation;
description += ": \"";
description += m_comparator.m_str;
- description += "\"";
+ description += '"';
description += m_comparator.caseSensitivitySuffix();
return description;
}
- StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+ StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals"_sr, comparator ) {}
bool StringEqualsMatcher::match( std::string const& source ) const {
return m_comparator.adjustString( source ) == m_comparator.m_str;
}
- StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+ StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains"_sr, comparator ) {}
bool StringContainsMatcher::match( std::string const& source ) const {
return contains( m_comparator.adjustString( source ), m_comparator.m_str );
}
- StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+ StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with"_sr, comparator ) {}
bool StartsWithMatcher::match( std::string const& source ) const {
return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
}
- EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+ EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with"_sr, comparator ) {}
bool EndsWithMatcher::match( std::string const& source ) const {
return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
@@ -6719,7 +7456,7 @@ namespace Matchers {
- RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
+ RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(CATCH_MOVE(regex)), m_caseSensitivity(caseSensitivity) {}
bool RegexMatcher::match(std::string const& matchee) const {
auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
@@ -6738,7 +7475,7 @@ namespace Matchers {
StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity ) {
return StringEqualsMatcher( CasedString( str, caseSensitivity) );
}
- StringContainsMatcher Contains( std::string const& str, CaseSensitive caseSensitivity ) {
+ StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity ) {
return StringContainsMatcher( CasedString( str, caseSensitivity) );
}
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity ) {
@@ -6769,7 +7506,7 @@ namespace Matchers {
for ( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) {
combined_size += desc->size();
}
- combined_size += (descriptions_end - descriptions_begin - 1) * combine.size();
+ combined_size += static_cast<size_t>(descriptions_end - descriptions_begin - 1) * combine.size();
description.reserve(combined_size);
@@ -6812,9 +7549,9 @@ namespace Catch {
// This is the general overload that takes a any string matcher
// There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
// the Equals matcher (so the header does not mention matchers)
- void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) {
+ void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) {
std::string exceptionMessage = Catch::translateActiveException();
- MatchExpr<std::string, StringMatcher const&> expr( std::move(exceptionMessage), matcher, matcherString );
+ MatchExpr<std::string, StringMatcher const&> expr( CATCH_MOVE(exceptionMessage), matcher, matcherString );
handler.handleExpr( expr );
}
@@ -6901,7 +7638,7 @@ bool ExceptionMessageMatcher::match(std::exception const& ex) const {
}
std::string ExceptionMessageMatcher::describe() const {
- return "exception message matches \"" + m_message + "\"";
+ return "exception message matches \"" + m_message + '"';
}
ExceptionMessageMatcher Message(std::string const& message) {
@@ -6921,20 +7658,20 @@ namespace Catch {
void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
// Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR.
- stream << ":test-result: ";
+ m_stream << ":test-result: ";
if (_testCaseStats.totals.assertions.allPassed()) {
- stream << "PASS";
+ m_stream << "PASS";
} else if (_testCaseStats.totals.assertions.allOk()) {
- stream << "XFAIL";
+ m_stream << "XFAIL";
} else {
- stream << "FAIL";
+ m_stream << "FAIL";
}
- stream << ' ' << _testCaseStats.testInfo->name << '\n';
+ m_stream << ' ' << _testCaseStats.testInfo->name << '\n';
StreamingReporterBase::testCaseEnded(_testCaseStats);
}
void AutomakeReporter::skipTest(TestCaseInfo const& testInfo) {
- stream << ":test-result: SKIP " << testInfo.name << '\n';
+ m_stream << ":test-result: SKIP " << testInfo.name << '\n';
}
} // end namespace Catch
@@ -6953,12 +7690,33 @@ namespace Catch {
*/
+#include <algorithm>
#include <cfloat>
#include <cstdio>
#include <ostream>
+#include <iomanip>
namespace Catch {
+ namespace {
+ void listTestNamesOnly(std::ostream& out,
+ std::vector<TestCaseHandle> const& tests) {
+ for (auto const& test : tests) {
+ auto const& testCaseInfo = test.getTestCaseInfo();
+
+ if (startsWith(testCaseInfo.name, '#')) {
+ out << '"' << testCaseInfo.name << '"';
+ } else {
+ out << testCaseInfo.name;
+ }
+
+ out << '\n';
+ }
+ out << std::flush;
+ }
+ } // end unnamed namespace
+
+
// Because formatting using c++ streams is stateful, drop down to C is
// required Alternatively we could use stringstream, but its performance
// is... not good.
@@ -6973,11 +7731,13 @@ namespace Catch {
// Save previous errno, to prevent sprintf from overwriting it
ErrnoGuard guard;
#ifdef _MSC_VER
- sprintf_s( buffer, "%.3f", duration );
+ size_t printedLength = static_cast<size_t>(
+ sprintf_s( buffer, "%.3f", duration ) );
#else
- std::sprintf( buffer, "%.3f", duration );
+ size_t printedLength = static_cast<size_t>(
+ std::snprintf( buffer, maxDoubleSize, "%.3f", duration ) );
#endif
- return std::string( buffer );
+ return std::string( buffer, printedLength );
}
bool shouldShowDuration( IConfig const& config, double duration ) {
@@ -7020,31 +7780,160 @@ namespace Catch {
return out;
}
+ void
+ defaultListReporters( std::ostream& out,
+ std::vector<ReporterDescription> const& descriptions,
+ Verbosity verbosity ) {
+ out << "Available reporters:\n";
+ const auto maxNameLen =
+ std::max_element( descriptions.begin(),
+ descriptions.end(),
+ []( ReporterDescription const& lhs,
+ ReporterDescription const& rhs ) {
+ return lhs.name.size() < rhs.name.size();
+ } )
+ ->name.size();
+
+ for ( auto const& desc : descriptions ) {
+ if ( verbosity == Verbosity::Quiet ) {
+ out << TextFlow::Column( desc.name )
+ .indent( 2 )
+ .width( 5 + maxNameLen )
+ << '\n';
+ } else {
+ out << TextFlow::Column( desc.name + ':' )
+ .indent( 2 )
+ .width( 5 + maxNameLen ) +
+ TextFlow::Column( desc.description )
+ .initialIndent( 0 )
+ .indent( 2 )
+ .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 )
+ << '\n';
+ }
+ }
+ out << '\n' << std::flush;
+ }
+
+ void defaultListListeners( std::ostream& out,
+ std::vector<ListenerDescription> const& descriptions ) {
+ const auto maxNameLen =
+ std::max_element( descriptions.begin(),
+ descriptions.end(),
+ []( ListenerDescription const& lhs,
+ ListenerDescription const& rhs ) {
+ return lhs.name.size() < rhs.name.size();
+ } )
+ ->name.size();
+
+ out << "Registered listeners:\n";
+ for ( auto const& desc : descriptions ) {
+ out << TextFlow::Column( static_cast<std::string>( desc.name ) +
+ ':' )
+ .indent( 2 )
+ .width( maxNameLen + 5 ) +
+ TextFlow::Column( desc.description )
+ .initialIndent( 0 )
+ .indent( 2 )
+ .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 )
+ << '\n';
+ }
+
+ out << '\n' << std::flush;
+ }
+
+ void defaultListTags( std::ostream& out,
+ std::vector<TagInfo> const& tags,
+ bool isFiltered ) {
+ if ( isFiltered ) {
+ out << "Tags for matching test cases:\n";
+ } else {
+ out << "All available tags:\n";
+ }
+
+ for ( auto const& tagCount : tags ) {
+ ReusableStringStream rss;
+ rss << " " << std::setw( 2 ) << tagCount.count << " ";
+ auto str = rss.str();
+ auto wrapper = TextFlow::Column( tagCount.all() )
+ .initialIndent( 0 )
+ .indent( str.size() )
+ .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 );
+ out << str << wrapper << '\n';
+ }
+ out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush;
+ }
+
+ void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector<TestCaseHandle> const& tests, bool isFiltered, Verbosity verbosity) {
+ // We special case this to provide the equivalent of old
+ // `--list-test-names-only`, which could then be used by the
+ // `--input-file` option.
+ if (verbosity == Verbosity::Quiet) {
+ listTestNamesOnly(out, tests);
+ return;
+ }
+
+ if (isFiltered) {
+ out << "Matching test cases:\n";
+ } else {
+ out << "All available test cases:\n";
+ }
+
+ for (auto const& test : tests) {
+ auto const& testCaseInfo = test.getTestCaseInfo();
+ Colour::Code colour = testCaseInfo.isHidden()
+ ? Colour::SecondaryText
+ : Colour::None;
+ auto colourGuard = streamColour->guardColour( colour ).engage( out );
+
+ out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n';
+ if (verbosity >= Verbosity::High) {
+ out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n';
+ }
+ if (!testCaseInfo.tags.empty() &&
+ verbosity > Verbosity::Quiet) {
+ out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n';
+ }
+ }
+
+ if (isFiltered) {
+ out << pluralise(tests.size(), "matching test case"_sr);
+ } else {
+ out << pluralise(tests.size(), "test case"_sr);
+ }
+ out << "\n\n" << std::flush;
+ }
+
} // namespace Catch
namespace Catch {
+
+ void EventListenerBase::fatalErrorEncountered( StringRef ) {}
+
+ void EventListenerBase::benchmarkPreparing( StringRef ) {}
+ void EventListenerBase::benchmarkStarting( BenchmarkInfo const& ) {}
+ void EventListenerBase::benchmarkEnded( BenchmarkStats<> const& ) {}
+ void EventListenerBase::benchmarkFailed( StringRef ) {}
+
void EventListenerBase::assertionStarting( AssertionInfo const& ) {}
- bool EventListenerBase::assertionEnded( AssertionStats const& ) {
- return false;
- }
- void
- EventListenerBase::listReporters( std::vector<ReporterDescription> const&,
- IConfig const& ) {}
- void EventListenerBase::listTests( std::vector<TestCaseHandle> const&,
- IConfig const& ) {}
- void EventListenerBase::listTags( std::vector<TagInfo> const&,
- IConfig const& ) {}
- void EventListenerBase::noMatchingTestCases( std::string const& ) {}
+ void EventListenerBase::assertionEnded( AssertionStats const& ) {}
+ void EventListenerBase::listReporters(
+ std::vector<ReporterDescription> const& ) {}
+ void EventListenerBase::listListeners(
+ std::vector<ListenerDescription> const& ) {}
+ void EventListenerBase::listTests( std::vector<TestCaseHandle> const& ) {}
+ void EventListenerBase::listTags( std::vector<TagInfo> const& ) {}
+ void EventListenerBase::noMatchingTestCases( StringRef ) {}
+ void EventListenerBase::reportInvalidTestSpec( StringRef ) {}
void EventListenerBase::testRunStarting( TestRunInfo const& ) {}
- void EventListenerBase::testGroupStarting( GroupInfo const& ) {}
void EventListenerBase::testCaseStarting( TestCaseInfo const& ) {}
+ void EventListenerBase::testCasePartialStarting(TestCaseInfo const&, uint64_t) {}
void EventListenerBase::sectionStarting( SectionInfo const& ) {}
void EventListenerBase::sectionEnded( SectionStats const& ) {}
+ void EventListenerBase::testCasePartialEnded(TestCaseStats const&, uint64_t) {}
void EventListenerBase::testCaseEnded( TestCaseStats const& ) {}
- void EventListenerBase::testGroupEnded( TestGroupStats const& ) {}
void EventListenerBase::testRunEnded( TestRunStats const& ) {}
void EventListenerBase::skipTest( TestCaseInfo const& ) {}
} // namespace Catch
@@ -7052,14 +7941,51 @@ namespace Catch {
+
+
+namespace Catch {
+ ReporterBase::ReporterBase( ReporterConfig&& config ):
+ IEventListener( config.fullConfig() ),
+ m_wrapped_stream( CATCH_MOVE(config).takeStream() ),
+ m_stream( m_wrapped_stream->stream() ),
+ m_colour( makeColourImpl( config.colourMode(), m_wrapped_stream.get() ) ),
+ m_customOptions( config.customOptions() )
+ {}
+
+ ReporterBase::~ReporterBase() = default;
+
+ void ReporterBase::listReporters(
+ std::vector<ReporterDescription> const& descriptions ) {
+ defaultListReporters(m_stream, descriptions, m_config->verbosity());
+ }
+
+ void ReporterBase::listListeners(
+ std::vector<ListenerDescription> const& descriptions ) {
+ defaultListListeners( m_stream, descriptions );
+ }
+
+ void ReporterBase::listTests(std::vector<TestCaseHandle> const& tests) {
+ defaultListTests(m_stream,
+ m_colour.get(),
+ tests,
+ m_config->hasTestFilters(),
+ m_config->verbosity());
+ }
+
+ void ReporterBase::listTags(std::vector<TagInfo> const& tags) {
+ defaultListTags( m_stream, tags, m_config->hasTestFilters() );
+ }
+
+} // namespace Catch
+
+
+
+
#include <ostream>
namespace {
- // Colour::LightGrey
- Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
-
- Catch::StringRef bothOrAll( std::size_t count ) {
+ constexpr Catch::StringRef bothOrAll( std::uint64_t count ) {
switch (count) {
case 1:
return Catch::StringRef{};
@@ -7076,6 +8002,9 @@ namespace {
namespace Catch {
namespace {
+ // Colour::LightGrey
+ static constexpr Colour::Code compactDimColour = Colour::FileName;
+
#ifdef CATCH_PLATFORM_MAC
static constexpr Catch::StringRef compactFailedString = "FAILED"_sr;
static constexpr Catch::StringRef compactPassedString = "PASSED"_sr;
@@ -7090,35 +8019,33 @@ namespace {
// - white: Passed [both/all] N test cases (no assertions).
// - red: Failed N tests cases, failed M assertions.
// - green: Passed [both/all] N tests cases with M assertions.
-void printTotals(std::ostream& out, const Totals& totals) {
+void printTotals(std::ostream& out, const Totals& totals, ColourImpl* colourImpl) {
if (totals.testCases.total() == 0) {
out << "No tests ran.";
} else if (totals.testCases.failed == totals.testCases.total()) {
- Colour colour(Colour::ResultError);
+ auto guard = colourImpl->guardColour( Colour::ResultError ).engage( out );
const StringRef qualify_assertions_failed =
totals.assertions.failed == totals.assertions.total() ?
bothOrAll(totals.assertions.failed) : StringRef{};
out <<
"Failed " << bothOrAll(totals.testCases.failed)
- << pluralise(totals.testCases.failed, "test case") << ", "
+ << pluralise(totals.testCases.failed, "test case"_sr) << ", "
"failed " << qualify_assertions_failed <<
- pluralise(totals.assertions.failed, "assertion") << '.';
+ pluralise(totals.assertions.failed, "assertion"_sr) << '.';
} else if (totals.assertions.total() == 0) {
out <<
"Passed " << bothOrAll(totals.testCases.total())
- << pluralise(totals.testCases.total(), "test case")
+ << pluralise(totals.testCases.total(), "test case"_sr)
<< " (no assertions).";
} else if (totals.assertions.failed) {
- Colour colour(Colour::ResultError);
- out <<
- "Failed " << pluralise(totals.testCases.failed, "test case") << ", "
- "failed " << pluralise(totals.assertions.failed, "assertion") << '.';
+ out << colourImpl->guardColour( Colour::ResultError ) <<
+ "Failed " << pluralise(totals.testCases.failed, "test case"_sr) << ", "
+ "failed " << pluralise(totals.assertions.failed, "assertion"_sr) << '.';
} else {
- Colour colour(Colour::ResultSuccess);
- out <<
+ out << colourImpl->guardColour( Colour::ResultSuccess ) <<
"Passed " << bothOrAll(totals.testCases.passed)
- << pluralise(totals.testCases.passed, "test case") <<
- " with " << pluralise(totals.assertions.passed, "assertion") << '.';
+ << pluralise(totals.testCases.passed, "test case"_sr) <<
+ " with " << pluralise(totals.assertions.passed, "assertion"_sr) << '.';
}
}
@@ -7127,12 +8054,14 @@ class AssertionPrinter {
public:
AssertionPrinter& operator= (AssertionPrinter const&) = delete;
AssertionPrinter(AssertionPrinter const&) = delete;
- AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
+ AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages, ColourImpl* colourImpl_)
: stream(_stream)
, result(_stats.assertionResult)
, messages(_stats.infoMessages)
, itMessage(_stats.infoMessages.begin())
- , printInfoMessages(_printInfoMessages) {}
+ , printInfoMessages(_printInfoMessages)
+ , colourImpl(colourImpl_)
+ {}
void print() {
printSourceInfo();
@@ -7204,16 +8133,13 @@ public:
private:
void printSourceInfo() const {
- Colour colourGuard(Colour::FileName);
- stream << result.getSourceInfo() << ':';
+ stream << colourImpl->guardColour( Colour::FileName )
+ << result.getSourceInfo() << ':';
}
void printResultType(Colour::Code colour, StringRef passOrFail) const {
if (!passOrFail.empty()) {
- {
- Colour colourGuard(colour);
- stream << ' ' << passOrFail;
- }
+ stream << colourImpl->guardColour(colour) << ' ' << passOrFail;
stream << ':';
}
}
@@ -7226,8 +8152,7 @@ private:
if (result.hasExpression()) {
stream << ';';
{
- Colour colour(dimColour());
- stream << " expression was:";
+ stream << colourImpl->guardColour(compactDimColour) << " expression was:";
}
printOriginalExpression();
}
@@ -7241,10 +8166,7 @@ private:
void printReconstructedExpression() const {
if (result.hasExpandedExpression()) {
- {
- Colour colour(dimColour());
- stream << " for: ";
- }
+ stream << colourImpl->guardColour(compactDimColour) << " for: ";
stream << result.getExpandedExpression();
}
}
@@ -7256,25 +8178,22 @@ private:
}
}
- void printRemainingMessages(Colour::Code colour = dimColour()) {
+ void printRemainingMessages(Colour::Code colour = compactDimColour) {
if (itMessage == messages.end())
return;
const auto itEnd = messages.cend();
const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
- {
- Colour colourGuard(colour);
- stream << " with " << pluralise(N, "message") << ':';
- }
+ stream << colourImpl->guardColour( colour ) << " with "
+ << pluralise( N, "message"_sr ) << ':';
while (itMessage != itEnd) {
// If this assertion is a warning ignore any INFO messages
if (printInfoMessages || itMessage->type != ResultWas::Info) {
printMessage();
if (itMessage != itEnd) {
- Colour colourGuard(dimColour());
- stream << " and";
+ stream << colourImpl->guardColour(compactDimColour) << " and";
}
continue;
}
@@ -7288,6 +8207,7 @@ private:
std::vector<MessageInfo> messages;
std::vector<MessageInfo>::const_iterator itMessage;
bool printInfoMessages;
+ ColourImpl* colourImpl;
};
} // anon namespace
@@ -7296,13 +8216,21 @@ private:
return "Reports test results on a single line, suitable for IDEs";
}
- void CompactReporter::noMatchingTestCases( std::string const& spec ) {
- stream << "No test cases matched '" << spec << '\'' << std::endl;
+ void CompactReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+ m_stream << "No test cases matched '" << unmatchedSpec << "'\n";
}
- void CompactReporter::assertionStarting( AssertionInfo const& ) {}
+ void CompactReporter::testRunStarting( TestRunInfo const& ) {
+ if ( m_config->testSpec().hasFilters() ) {
+ m_stream << m_colour->guardColour( Colour::BrightYellow )
+ << "Filters: "
+ << serializeFilters( m_config->getTestsOrTags() )
+ << '\n';
+ }
+ m_stream << "RNG seed: " << m_config->rngSeed() << '\n';
+ }
- bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
+ void CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
AssertionResult const& result = _assertionStats.assertionResult;
bool printInfoMessages = true;
@@ -7310,27 +8238,26 @@ private:
// Drop out if result was successful and we're not printing those
if( !m_config->includeSuccessfulResults() && result.isOk() ) {
if( result.getResultType() != ResultWas::Warning )
- return false;
+ return;
printInfoMessages = false;
}
- AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ AssertionPrinter printer( m_stream, _assertionStats, printInfoMessages, m_colour.get() );
printer.print();
- stream << std::endl;
- return true;
+ m_stream << '\n' << std::flush;
}
void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
double dur = _sectionStats.durationInSeconds;
if ( shouldShowDuration( *m_config, dur ) ) {
- stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ m_stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush;
}
}
void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
- printTotals( stream, _testRunStats.totals );
- stream << '\n' << std::endl;
+ printTotals( m_stream, _testRunStats.totals, m_colour.get() );
+ m_stream << "\n\n" << std::flush;
StreamingReporterBase::testRunEnded( _testRunStats );
}
@@ -7341,7 +8268,6 @@ private:
-#include <cfloat>
#include <cstdio>
#if defined(_MSC_VER)
@@ -7367,18 +8293,19 @@ class ConsoleAssertionPrinter {
public:
ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete;
ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete;
- ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
+ ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, ColourImpl* colourImpl_, bool _printInfoMessages)
: stream(_stream),
stats(_stats),
result(_stats.assertionResult),
colour(Colour::None),
message(result.getMessage()),
messages(_stats.infoMessages),
+ colourImpl(colourImpl_),
printInfoMessages(_printInfoMessages) {
switch (result.getResultType()) {
case ResultWas::Ok:
colour = Colour::Success;
- passOrFail = "PASSED";
+ passOrFail = "PASSED"_sr;
//if( result.hasMessage() )
if (_stats.infoMessages.size() == 1)
messageLabel = "with message";
@@ -7388,10 +8315,10 @@ public:
case ResultWas::ExpressionFailed:
if (result.isOk()) {
colour = Colour::Success;
- passOrFail = "FAILED - but was ok";
+ passOrFail = "FAILED - but was ok"_sr;
} else {
colour = Colour::Error;
- passOrFail = "FAILED";
+ passOrFail = "FAILED"_sr;
}
if (_stats.infoMessages.size() == 1)
messageLabel = "with message";
@@ -7400,7 +8327,7 @@ public:
break;
case ResultWas::ThrewException:
colour = Colour::Error;
- passOrFail = "FAILED";
+ passOrFail = "FAILED"_sr;
messageLabel = "due to unexpected exception with ";
if (_stats.infoMessages.size() == 1)
messageLabel += "message";
@@ -7409,12 +8336,12 @@ public:
break;
case ResultWas::FatalErrorCondition:
colour = Colour::Error;
- passOrFail = "FAILED";
+ passOrFail = "FAILED"_sr;
messageLabel = "due to a fatal error condition";
break;
case ResultWas::DidntThrowException:
colour = Colour::Error;
- passOrFail = "FAILED";
+ passOrFail = "FAILED"_sr;
messageLabel = "because no exception was thrown where one was expected";
break;
case ResultWas::Info:
@@ -7424,7 +8351,7 @@ public:
messageLabel = "warning";
break;
case ResultWas::ExplicitFailure:
- passOrFail = "FAILED";
+ passOrFail = "FAILED"_sr;
colour = Colour::Error;
if (_stats.infoMessages.size() == 1)
messageLabel = "explicitly with message";
@@ -7435,7 +8362,7 @@ public:
case ResultWas::Unknown:
case ResultWas::FailureBit:
case ResultWas::Exception:
- passOrFail = "** internal error **";
+ passOrFail = "** internal error **"_sr;
colour = Colour::Error;
break;
}
@@ -7456,23 +8383,22 @@ public:
private:
void printResultType() const {
if (!passOrFail.empty()) {
- Colour colourGuard(colour);
- stream << passOrFail << ":\n";
+ stream << colourImpl->guardColour(colour) << passOrFail << ":\n";
}
}
void printOriginalExpression() const {
if (result.hasExpression()) {
- Colour colourGuard(Colour::OriginalExpression);
- stream << " ";
- stream << result.getExpressionInMacro();
- stream << '\n';
+ stream << colourImpl->guardColour( Colour::OriginalExpression )
+ << " " << result.getExpressionInMacro() << '\n';
}
}
void printReconstructedExpression() const {
if (result.hasExpandedExpression()) {
stream << "with expansion:\n";
- Colour colourGuard(Colour::ReconstructedExpression);
- stream << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n';
+ stream << colourImpl->guardColour( Colour::ReconstructedExpression )
+ << TextFlow::Column( result.getExpandedExpression() )
+ .indent( 2 )
+ << '\n';
}
}
void printMessage() const {
@@ -7485,27 +8411,28 @@ private:
}
}
void printSourceInfo() const {
- Colour colourGuard(Colour::FileName);
- stream << result.getSourceInfo() << ": ";
+ stream << colourImpl->guardColour( Colour::FileName )
+ << result.getSourceInfo() << ": ";
}
std::ostream& stream;
AssertionStats const& stats;
AssertionResult const& result;
Colour::Code colour;
- std::string passOrFail;
+ StringRef passOrFail;
std::string messageLabel;
std::string message;
std::vector<MessageInfo> messages;
+ ColourImpl* colourImpl;
bool printInfoMessages;
};
-std::size_t makeRatio(std::size_t number, std::size_t total) {
- std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
- return (ratio == 0 && number > 0) ? 1 : ratio;
+std::size_t makeRatio( std::uint64_t number, std::uint64_t total ) {
+ const auto ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
+ return (ratio == 0 && number > 0) ? 1 : static_cast<std::size_t>(ratio);
}
-std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) {
+std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
if (i > j && i > k)
return i;
else if (j > k)
@@ -7518,7 +8445,7 @@ enum class Justification { Left, Right };
struct ColumnInfo {
std::string name;
- int width;
+ std::size_t width;
Justification justification;
};
struct ColumnBreak {};
@@ -7607,7 +8534,7 @@ class TablePrinter {
public:
TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
: m_os( os ),
- m_columnInfos( std::move( columnInfos ) ) {}
+ m_columnInfos( CATCH_MOVE( columnInfos ) ) {}
auto columnInfos() const -> std::vector<ColumnInfo> const& {
return m_columnInfos;
@@ -7621,7 +8548,8 @@ public:
TextFlow::Columns headerCols;
auto spacer = TextFlow::Spacer(2);
for (auto const& info : m_columnInfos) {
- headerCols += TextFlow::Column(info.name).width(static_cast<std::size_t>(info.width - 2));
+ assert(info.width > 2);
+ headerCols += TextFlow::Column(info.name).width(info.width - 2);
headerCols += spacer;
}
m_os << headerCols << '\n';
@@ -7632,7 +8560,7 @@ public:
void close() {
if (m_isOpen) {
*this << RowBreak();
- m_os << std::endl;
+ m_os << '\n' << std::flush;
m_isOpen = false;
}
}
@@ -7655,7 +8583,7 @@ public:
tp.m_currentColumn++;
auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
- auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width))
+ auto padding = (strSize + 1 < colInfo.width)
? std::string(colInfo.width - (strSize + 1), ' ')
: std::string();
if (colInfo.justification == Justification::Left)
@@ -7674,9 +8602,9 @@ public:
}
};
-ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
- : StreamingReporterBase(config),
- m_tablePrinter(new TablePrinter(config.stream(),
+ConsoleReporter::ConsoleReporter(ReporterConfig&& config):
+ StreamingReporterBase( CATCH_MOVE( config ) ),
+ m_tablePrinter(Detail::make_unique<TablePrinter>(m_stream,
[&config]() -> std::vector<ColumnInfo> {
if (config.fullConfig()->benchmarkNoAnalysis())
{
@@ -7703,31 +8631,30 @@ std::string ConsoleReporter::getDescription() {
return "Reports test results as plain lines of text";
}
-void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
- stream << "No test cases matched '" << spec << '\'' << std::endl;
+void ConsoleReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+ m_stream << "No test cases matched '" << unmatchedSpec << "'\n";
}
-void ConsoleReporter::reportInvalidArguments(std::string const&arg){
- stream << "Invalid Filter: " << arg << std::endl;
+void ConsoleReporter::reportInvalidTestSpec( StringRef arg ) {
+ m_stream << "Invalid Filter: " << arg << '\n';
}
void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
-bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
+void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
AssertionResult const& result = _assertionStats.assertionResult;
bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
// Drop out if result was successful but we're not printing them.
if (!includeResults && result.getResultType() != ResultWas::Warning)
- return false;
+ return;
lazyPrint();
- ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults);
+ ConsoleAssertionPrinter printer(m_stream, _assertionStats, m_colour.get(), includeResults);
printer.print();
- stream << std::endl;
- return true;
+ m_stream << '\n' << std::flush;
}
void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
@@ -7739,16 +8666,17 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
m_tablePrinter->close();
if (_sectionStats.missingAssertions) {
lazyPrint();
- Colour colour(Colour::ResultError);
+ auto guard =
+ m_colour->guardColour( Colour::ResultError ).engage( m_stream );
if (m_sectionStack.size() > 1)
- stream << "\nNo assertions in section";
+ m_stream << "\nNo assertions in section";
else
- stream << "\nNo assertions in test case";
- stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+ m_stream << "\nNo assertions in test case";
+ m_stream << " '" << _sectionStats.sectionInfo.name << "'\n\n" << std::flush;
}
double dur = _sectionStats.durationInSeconds;
if (shouldShowDuration(*m_config, dur)) {
- stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ m_stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush;
}
if (m_headerPrinted) {
m_headerPrinted = false;
@@ -7756,10 +8684,11 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
StreamingReporterBase::sectionEnded(_sectionStats);
}
-void ConsoleReporter::benchmarkPreparing(std::string const& name) {
+void ConsoleReporter::benchmarkPreparing( StringRef name ) {
lazyPrintWithoutClosingBenchmarkTable();
- auto nameCol = TextFlow::Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
+ auto nameCol = TextFlow::Column( static_cast<std::string>( name ) )
+ .width( m_tablePrinter->columnInfos()[0].width - 2 );
bool firstLine = true;
for (auto line : nameCol) {
@@ -7795,8 +8724,8 @@ void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
}
}
-void ConsoleReporter::benchmarkFailed(std::string const& error) {
- Colour colour(Colour::Red);
+void ConsoleReporter::benchmarkFailed( StringRef error ) {
+ auto guard = m_colour->guardColour( Colour::Red ).engage( m_stream );
(*m_tablePrinter)
<< "Benchmark failed (" << error << ')'
<< ColumnBreak() << RowBreak();
@@ -7807,24 +8736,19 @@ void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
StreamingReporterBase::testCaseEnded(_testCaseStats);
m_headerPrinted = false;
}
-void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
- if (currentGroupInfo.used) {
- printSummaryDivider();
- stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
- printTotals(_testGroupStats.totals);
- stream << '\n' << std::endl;
- }
- StreamingReporterBase::testGroupEnded(_testGroupStats);
-}
void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
printTotalsDivider(_testRunStats.totals);
printTotals(_testRunStats.totals);
- stream << std::endl;
+ m_stream << '\n' << std::flush;
StreamingReporterBase::testRunEnded(_testRunStats);
}
void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
StreamingReporterBase::testRunStarting(_testInfo);
- printTestFilters();
+ if ( m_config->testSpec().hasFilters() ) {
+ m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: "
+ << serializeFilters( m_config->getTestsOrTags() ) << '\n';
+ }
+ m_stream << "Randomness seeded to: " << m_config->rngSeed() << '\n';
}
void ConsoleReporter::lazyPrint() {
@@ -7835,40 +8759,30 @@ void ConsoleReporter::lazyPrint() {
void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
- if (!currentTestRunInfo.used)
+ if ( !m_testRunInfoPrinted ) {
lazyPrintRunInfo();
- if (!currentGroupInfo.used)
- lazyPrintGroupInfo();
-
+ }
if (!m_headerPrinted) {
printTestCaseAndSectionHeader();
m_headerPrinted = true;
}
}
void ConsoleReporter::lazyPrintRunInfo() {
- stream << '\n' << lineOfChars('~') << '\n';
- Colour colour(Colour::SecondaryText);
- stream << currentTestRunInfo->name
- << " is a Catch v" << libraryVersion() << " host application.\n"
- << "Run with -? for options\n\n";
-
- if (m_config->rngSeed() != 0)
- stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
-
- currentTestRunInfo.used = true;
-}
-void ConsoleReporter::lazyPrintGroupInfo() {
- if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
- printClosedHeader("Group: " + currentGroupInfo->name);
- currentGroupInfo.used = true;
- }
+ m_stream << '\n'
+ << lineOfChars( '~' ) << '\n'
+ << m_colour->guardColour( Colour::SecondaryText )
+ << currentTestRunInfo.name << " is a Catch2 v" << libraryVersion()
+ << " host application.\n"
+ << "Run with -? for options\n\n";
+
+ m_testRunInfoPrinted = true;
}
void ConsoleReporter::printTestCaseAndSectionHeader() {
assert(!m_sectionStack.empty());
printOpenHeader(currentTestCaseInfo->name);
if (m_sectionStack.size() > 1) {
- Colour colourGuard(Colour::Headers);
+ auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream );
auto
it = m_sectionStack.begin() + 1, // Skip first section (test case)
@@ -7880,41 +8794,63 @@ void ConsoleReporter::printTestCaseAndSectionHeader() {
SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
- stream << lineOfChars('-') << '\n';
- Colour colourGuard(Colour::FileName);
- stream << lineInfo << '\n';
- stream << lineOfChars('.') << '\n' << std::endl;
+ m_stream << lineOfChars( '-' ) << '\n'
+ << m_colour->guardColour( Colour::FileName ) << lineInfo << '\n'
+ << lineOfChars( '.' ) << "\n\n"
+ << std::flush;
}
void ConsoleReporter::printClosedHeader(std::string const& _name) {
printOpenHeader(_name);
- stream << lineOfChars('.') << '\n';
+ m_stream << lineOfChars('.') << '\n';
}
void ConsoleReporter::printOpenHeader(std::string const& _name) {
- stream << lineOfChars('-') << '\n';
+ m_stream << lineOfChars('-') << '\n';
{
- Colour colourGuard(Colour::Headers);
+ auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream );
printHeaderString(_name);
}
}
-// if string has a : in first line will set indent to follow it on
-// subsequent lines
void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) {
- std::size_t i = _string.find(": ");
- if (i != std::string::npos)
- i += 2;
- else
- i = 0;
- stream << TextFlow::Column(_string).indent(indent + i).initialIndent(indent) << '\n';
+ // We want to get a bit fancy with line breaking here, so that subsequent
+ // lines start after ":" if one is present, e.g.
+ // ```
+ // blablabla: Fancy
+ // linebreaking
+ // ```
+ // but we also want to avoid problems with overly long indentation causing
+ // the text to take up too many lines, e.g.
+ // ```
+ // blablabla: F
+ // a
+ // n
+ // c
+ // y
+ // .
+ // .
+ // .
+ // ```
+ // So we limit the prefix indentation check to first quarter of the possible
+ // width
+ std::size_t idx = _string.find( ": " );
+ if ( idx != std::string::npos && idx < CATCH_CONFIG_CONSOLE_WIDTH / 4 ) {
+ idx += 2;
+ } else {
+ idx = 0;
+ }
+ m_stream << TextFlow::Column( _string )
+ .indent( indent + idx )
+ .initialIndent( indent )
+ << '\n';
}
struct SummaryColumn {
SummaryColumn( std::string _label, Colour::Code _colour )
- : label( std::move( _label ) ),
+ : label( CATCH_MOVE( _label ) ),
colour( _colour ) {}
- SummaryColumn addRow( std::size_t count ) {
+ SummaryColumn addRow( std::uint64_t count ) {
ReusableStringStream rss;
rss << count;
std::string row = rss.str();
@@ -7936,12 +8872,14 @@ struct SummaryColumn {
void ConsoleReporter::printTotals( Totals const& totals ) {
if (totals.testCases.total() == 0) {
- stream << Colour(Colour::Warning) << "No tests ran\n";
+ m_stream << m_colour->guardColour( Colour::Warning )
+ << "No tests ran\n";
} else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
- stream << Colour(Colour::ResultSuccess) << "All tests passed";
- stream << " ("
- << pluralise(totals.assertions.passed, "assertion") << " in "
- << pluralise(totals.testCases.passed, "test case") << ')'
+ m_stream << m_colour->guardColour( Colour::ResultSuccess )
+ << "All tests passed";
+ m_stream << " ("
+ << pluralise(totals.assertions.passed, "assertion"_sr) << " in "
+ << pluralise(totals.testCases.passed, "test case"_sr) << ')'
<< '\n';
} else {
@@ -7959,26 +8897,28 @@ void ConsoleReporter::printTotals( Totals const& totals ) {
.addRow(totals.testCases.failedButOk)
.addRow(totals.assertions.failedButOk));
- printSummaryRow("test cases", columns, 0);
- printSummaryRow("assertions", columns, 1);
+ printSummaryRow("test cases"_sr, columns, 0);
+ printSummaryRow("assertions"_sr, columns, 1);
}
}
-void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) {
+void ConsoleReporter::printSummaryRow(StringRef label, std::vector<SummaryColumn> const& cols, std::size_t row) {
for (auto col : cols) {
- std::string value = col.rows[row];
+ std::string const& value = col.rows[row];
if (col.label.empty()) {
- stream << label << ": ";
- if (value != "0")
- stream << value;
- else
- stream << Colour(Colour::Warning) << "- none -";
+ m_stream << label << ": ";
+ if ( value != "0" ) {
+ m_stream << value;
+ } else {
+ m_stream << m_colour->guardColour( Colour::Warning )
+ << "- none -";
+ }
} else if (value != "0") {
- stream << Colour(Colour::LightGrey) << " | ";
- stream << Colour(col.colour)
- << value << ' ' << col.label;
+ m_stream << m_colour->guardColour( Colour::LightGrey ) << " | "
+ << m_colour->guardColour( col.colour ) << value << ' '
+ << col.label;
}
}
- stream << '\n';
+ m_stream << '\n';
}
void ConsoleReporter::printTotalsDivider(Totals const& totals) {
@@ -7991,26 +8931,25 @@ void ConsoleReporter::printTotalsDivider(Totals const& totals) {
while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
findMax(failedRatio, failedButOkRatio, passedRatio)--;
- stream << Colour(Colour::Error) << std::string(failedRatio, '=');
- stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '=');
- if (totals.testCases.allPassed())
- stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '=');
- else
- stream << Colour(Colour::Success) << std::string(passedRatio, '=');
+ m_stream << m_colour->guardColour( Colour::Error )
+ << std::string( failedRatio, '=' )
+ << m_colour->guardColour( Colour::ResultExpectedFailure )
+ << std::string( failedButOkRatio, '=' );
+ if ( totals.testCases.allPassed() ) {
+ m_stream << m_colour->guardColour( Colour::ResultSuccess )
+ << std::string( passedRatio, '=' );
+ } else {
+ m_stream << m_colour->guardColour( Colour::Success )
+ << std::string( passedRatio, '=' );
+ }
} else {
- stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '=');
+ m_stream << m_colour->guardColour( Colour::Warning )
+ << std::string( CATCH_CONFIG_CONSOLE_WIDTH - 1, '=' );
}
- stream << '\n';
+ m_stream << '\n';
}
void ConsoleReporter::printSummaryDivider() {
- stream << lineOfChars('-') << '\n';
-}
-
-void ConsoleReporter::printTestFilters() {
- if (m_config->testSpec().hasFilters()) {
- Colour guard(Colour::BrightYellow);
- stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
- }
+ m_stream << lineOfChars('-') << '\n';
}
} // end namespace Catch
@@ -8025,6 +8964,7 @@ void ConsoleReporter::printTestFilters() {
+
#include <algorithm>
#include <cassert>
@@ -8035,7 +8975,7 @@ namespace Catch {
BySectionInfo( BySectionInfo const& other ):
m_other( other.m_other ) {}
bool operator()(
- std::shared_ptr<CumulativeReporterBase::SectionNode> const&
+ Detail::unique_ptr<CumulativeReporterBase::SectionNode> const&
node ) const {
return (
( node->stats.sectionInfo.name == m_other.name ) &&
@@ -8047,40 +8987,73 @@ namespace Catch {
SectionInfo const& m_other;
};
- void prepareExpandedExpression( AssertionResult& result ) {
- result.getExpandedExpression();
- }
} // namespace
+ namespace Detail {
+ AssertionOrBenchmarkResult::AssertionOrBenchmarkResult(
+ AssertionStats const& assertion ):
+ m_assertion( assertion ) {}
+
+ AssertionOrBenchmarkResult::AssertionOrBenchmarkResult(
+ BenchmarkStats<> const& benchmark ):
+ m_benchmark( benchmark ) {}
+
+ bool AssertionOrBenchmarkResult::isAssertion() const {
+ return m_assertion.some();
+ }
+ bool AssertionOrBenchmarkResult::isBenchmark() const {
+ return m_benchmark.some();
+ }
+
+ AssertionStats const& AssertionOrBenchmarkResult::asAssertion() const {
+ assert(m_assertion.some());
+
+ return *m_assertion;
+ }
+ BenchmarkStats<> const& AssertionOrBenchmarkResult::asBenchmark() const {
+ assert(m_benchmark.some());
+
+ return *m_benchmark;
+ }
+
+ }
CumulativeReporterBase::~CumulativeReporterBase() = default;
+ void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
+ m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats);
+ }
+
void
CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) {
SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
- std::shared_ptr<SectionNode> node;
+ SectionNode* node;
if ( m_sectionStack.empty() ) {
- if ( !m_rootSection )
+ if ( !m_rootSection ) {
m_rootSection =
- std::make_shared<SectionNode>( incompleteStats );
- node = m_rootSection;
+ Detail::make_unique<SectionNode>( incompleteStats );
+ }
+ node = m_rootSection.get();
} else {
SectionNode& parentNode = *m_sectionStack.back();
auto it = std::find_if( parentNode.childSections.begin(),
parentNode.childSections.end(),
BySectionInfo( sectionInfo ) );
if ( it == parentNode.childSections.end() ) {
- node = std::make_shared<SectionNode>( incompleteStats );
- parentNode.childSections.push_back( node );
+ auto newNode =
+ Detail::make_unique<SectionNode>( incompleteStats );
+ node = newNode.get();
+ parentNode.childSections.push_back( CATCH_MOVE( newNode ) );
} else {
- node = *it;
+ node = it->get();
}
}
+
+ m_deepestSection = node;
m_sectionStack.push_back( node );
- m_deepestSection = std::move( node );
}
- bool CumulativeReporterBase::assertionEnded(
+ void CumulativeReporterBase::assertionEnded(
AssertionStats const& assertionStats ) {
assert( !m_sectionStack.empty() );
// AssertionResult holds a pointer to a temporary DecomposedExpression,
@@ -8088,11 +9061,18 @@ namespace Catch {
// Our section stack copy of the assertionResult will likely outlive the
// temporary, so it must be expanded or discarded now to avoid calling
// a destroyed object later.
- prepareExpandedExpression(
- const_cast<AssertionResult&>( assertionStats.assertionResult ) );
+ if ( m_shouldStoreFailedAssertions &&
+ !assertionStats.assertionResult.isOk() ) {
+ static_cast<void>(
+ assertionStats.assertionResult.getExpandedExpression() );
+ }
+ if ( m_shouldStoreSuccesfulAssertions &&
+ assertionStats.assertionResult.isOk() ) {
+ static_cast<void>(
+ assertionStats.assertionResult.getExpandedExpression() );
+ }
SectionNode& sectionNode = *m_sectionStack.back();
- sectionNode.assertions.push_back( assertionStats );
- return true;
+ sectionNode.assertionsAndBenchmarks.emplace_back( assertionStats );
}
void CumulativeReporterBase::sectionEnded( SectionStats const& sectionStats ) {
@@ -8104,31 +9084,33 @@ namespace Catch {
void CumulativeReporterBase::testCaseEnded(
TestCaseStats const& testCaseStats ) {
- auto node = std::make_shared<TestCaseNode>( testCaseStats );
+ auto node = Detail::make_unique<TestCaseNode>( testCaseStats );
assert( m_sectionStack.size() == 0 );
- node->children.push_back( m_rootSection );
- m_testCases.push_back( node );
- m_rootSection.reset();
+ node->children.push_back( CATCH_MOVE(m_rootSection) );
+ m_testCases.push_back( CATCH_MOVE(node) );
assert( m_deepestSection );
m_deepestSection->stdOut = testCaseStats.stdOut;
m_deepestSection->stdErr = testCaseStats.stdErr;
}
- void CumulativeReporterBase::testGroupEnded(
- TestGroupStats const& testGroupStats ) {
- auto node = std::make_shared<TestGroupNode>( testGroupStats );
- node->children.swap( m_testCases );
- m_testGroups.push_back( node );
- }
void CumulativeReporterBase::testRunEnded( TestRunStats const& testRunStats ) {
- auto node = std::make_shared<TestRunNode>( testRunStats );
- node->children.swap( m_testGroups );
- m_testRuns.push_back( node );
+ assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run");
+ m_testRun = Detail::make_unique<TestRunNode>( testRunStats );
+ m_testRun->children.swap( m_testCases );
testRunEndedCumulative();
}
+ bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const {
+ return std::any_of(
+ assertionsAndBenchmarks.begin(),
+ assertionsAndBenchmarks.end(),
+ []( Detail::AssertionOrBenchmarkResult const& res ) {
+ return res.isAssertion();
+ } );
+ }
+
} // end namespace Catch
@@ -8137,34 +9119,29 @@ namespace Catch {
#include <cassert>
#include <ctime>
#include <algorithm>
+#include <iomanip>
namespace Catch {
namespace {
std::string getCurrentTimestamp() {
- // Beware, this is not reentrant because of backward compatibility issues
- // Also, UTC only, again because of backward compatibility (%z is C++11)
time_t rawtime;
std::time(&rawtime);
- auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
-#ifdef _MSC_VER
std::tm timeInfo = {};
+#if defined (_MSC_VER) || defined (__MINGW32__)
gmtime_s(&timeInfo, &rawtime);
#else
- std::tm* timeInfo;
- timeInfo = std::gmtime(&rawtime);
+ gmtime_r(&rawtime, &timeInfo);
#endif
+ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
char timeStamp[timeStampSize];
const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
-#ifdef _MSC_VER
std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
-#else
- std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
-#endif
- return std::string(timeStamp);
+
+ return std::string(timeStamp, timeStampSize - 1);
}
std::string fileNameTag(std::vector<Tag> const& tags) {
@@ -8180,45 +9157,49 @@ namespace Catch {
}
return std::string();
}
+
+ // Formats the duration in seconds to 3 decimal places.
+ // This is done because some genius defined Maven Surefire schema
+ // in a way that only accepts 3 decimal places, and tools like
+ // Jenkins use that schema for validation JUnit reporter output.
+ std::string formatDuration( double seconds ) {
+ ReusableStringStream rss;
+ rss << std::fixed << std::setprecision( 3 ) << seconds;
+ return rss.str();
+ }
+
} // anonymous namespace
- JunitReporter::JunitReporter( ReporterConfig const& _config )
- : CumulativeReporterBase( _config ),
- xml( _config.stream() )
+ JunitReporter::JunitReporter( ReporterConfig&& _config )
+ : CumulativeReporterBase( CATCH_MOVE(_config) ),
+ xml( m_stream )
{
m_preferences.shouldRedirectStdOut = true;
m_preferences.shouldReportAllAssertions = true;
+ m_shouldStoreSuccesfulAssertions = false;
}
- JunitReporter::~JunitReporter() {}
-
std::string JunitReporter::getDescription() {
return "Reports test results in an XML format that looks like Ant's junitreport target";
}
- void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {}
-
void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) {
CumulativeReporterBase::testRunStarting( runInfo );
xml.startElement( "testsuites" );
- }
-
- void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) {
suiteTimer.start();
stdOutForSuite.clear();
stdErrForSuite.clear();
unexpectedExceptions = 0;
- CumulativeReporterBase::testGroupStarting( groupInfo );
}
void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) {
m_okToFail = testCaseInfo.okToFail();
}
- bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
+ void JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
unexpectedExceptions++;
- return CumulativeReporterBase::assertionEnded( assertionStats );
+ CumulativeReporterBase::assertionEnded( assertionStats );
}
void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
@@ -8227,48 +9208,42 @@ namespace Catch {
CumulativeReporterBase::testCaseEnded( testCaseStats );
}
- void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
- double suiteTime = suiteTimer.getElapsedSeconds();
- CumulativeReporterBase::testGroupEnded( testGroupStats );
- writeGroup( *m_testGroups.back(), suiteTime );
- }
-
void JunitReporter::testRunEndedCumulative() {
+ const auto suiteTime = suiteTimer.getElapsedSeconds();
+ writeRun( *m_testRun, suiteTime );
xml.endElement();
}
- void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+ void JunitReporter::writeRun( TestRunNode const& testRunNode, double suiteTime ) {
XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
- TestGroupStats const& stats = groupNode.value;
- xml.writeAttribute( "name", stats.groupInfo.name );
- xml.writeAttribute( "errors", unexpectedExceptions );
- xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
- xml.writeAttribute( "tests", stats.totals.assertions.total() );
- xml.writeAttribute( "hostname", "tbd" ); // !TBD
+ TestRunStats const& stats = testRunNode.value;
+ xml.writeAttribute( "name"_sr, stats.runInfo.name );
+ xml.writeAttribute( "errors"_sr, unexpectedExceptions );
+ xml.writeAttribute( "failures"_sr, stats.totals.assertions.failed-unexpectedExceptions );
+ xml.writeAttribute( "tests"_sr, stats.totals.assertions.total() );
+ xml.writeAttribute( "hostname"_sr, "tbd"_sr ); // !TBD
if( m_config->showDurations() == ShowDurations::Never )
- xml.writeAttribute( "time", "" );
+ xml.writeAttribute( "time"_sr, ""_sr );
else
- xml.writeAttribute( "time", suiteTime );
- xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+ xml.writeAttribute( "time"_sr, formatDuration( suiteTime ) );
+ xml.writeAttribute( "timestamp"_sr, getCurrentTimestamp() );
- // Write properties if there are any
- if (m_config->hasTestFilters() || m_config->rngSeed() != 0) {
+ // Write properties
+ {
auto properties = xml.scopedElement("properties");
+ xml.scopedElement("property")
+ .writeAttribute("name"_sr, "random-seed"_sr)
+ .writeAttribute("value"_sr, m_config->rngSeed());
if (m_config->hasTestFilters()) {
xml.scopedElement("property")
- .writeAttribute("name", "filters")
- .writeAttribute("value", serializeFilters(m_config->getTestsOrTags()));
- }
- if (m_config->rngSeed() != 0) {
- xml.scopedElement("property")
- .writeAttribute("name", "random-seed")
- .writeAttribute("value", m_config->rngSeed());
+ .writeAttribute("name"_sr, "filters"_sr)
+ .writeAttribute("value"_sr, serializeFilters(m_config->getTestsOrTags()));
}
}
// Write test cases
- for( auto const& child : groupNode.children )
+ for( auto const& child : testRunNode.children )
writeTestCase( *child );
xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
@@ -8283,48 +9258,57 @@ namespace Catch {
assert( testCaseNode.children.size() == 1 );
SectionNode const& rootSection = *testCaseNode.children.front();
- std::string className = stats.testInfo->className;
+ std::string className =
+ static_cast<std::string>( stats.testInfo->className );
if( className.empty() ) {
className = fileNameTag(stats.testInfo->tags);
- if ( className.empty() )
+ if ( className.empty() ) {
className = "global";
+ }
}
if ( !m_config->name().empty() )
- className = m_config->name() + "." + className;
+ className = static_cast<std::string>(m_config->name()) + '.' + className;
- writeSection( className, "", rootSection );
+ writeSection( className, "", rootSection, stats.testInfo->okToFail() );
}
- void JunitReporter::writeSection( std::string const& className,
- std::string const& rootName,
- SectionNode const& sectionNode ) {
+ void JunitReporter::writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode,
+ bool testOkToFail) {
std::string name = trim( sectionNode.stats.sectionInfo.name );
if( !rootName.empty() )
name = rootName + '/' + name;
- if( !sectionNode.assertions.empty() ||
- !sectionNode.stdOut.empty() ||
- !sectionNode.stdErr.empty() ) {
+ if( sectionNode.hasAnyAssertions()
+ || !sectionNode.stdOut.empty()
+ || !sectionNode.stdErr.empty() ) {
XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
if( className.empty() ) {
- xml.writeAttribute( "classname", name );
- xml.writeAttribute( "name", "root" );
+ xml.writeAttribute( "classname"_sr, name );
+ xml.writeAttribute( "name"_sr, "root"_sr );
}
else {
- xml.writeAttribute( "classname", className );
- xml.writeAttribute( "name", name );
+ xml.writeAttribute( "classname"_sr, className );
+ xml.writeAttribute( "name"_sr, name );
}
- xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
+ xml.writeAttribute( "time"_sr, formatDuration( sectionNode.stats.durationInSeconds ) );
// This is not ideal, but it should be enough to mimic gtest's
// junit output.
// Ideally the JUnit reporter would also handle `skipTest`
// events and write those out appropriately.
- xml.writeAttribute( "status", "run" );
+ xml.writeAttribute( "status"_sr, "run"_sr );
+
+ if (sectionNode.stats.assertions.failedButOk) {
+ xml.scopedElement("skipped")
+ .writeAttribute("message", "TEST_CASE tagged with !mayfail");
+ }
writeAssertions( sectionNode );
+
if( !sectionNode.stdOut.empty() )
xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
if( !sectionNode.stdErr.empty() )
@@ -8332,14 +9316,17 @@ namespace Catch {
}
for( auto const& childNode : sectionNode.childSections )
if( className.empty() )
- writeSection( name, "", *childNode );
+ writeSection( name, "", *childNode, testOkToFail );
else
- writeSection( className, name, *childNode );
+ writeSection( className, name, *childNode, testOkToFail );
}
void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
- for( auto const& assertion : sectionNode.assertions )
- writeAssertion( assertion );
+ for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) {
+ if (assertionOrBenchmark.isAssertion()) {
+ writeAssertion(assertionOrBenchmark.asAssertion());
+ }
+ }
}
void JunitReporter::writeAssertion( AssertionStats const& stats ) {
@@ -8370,8 +9357,8 @@ namespace Catch {
XmlWriter::ScopedElement e = xml.scopedElement( elementName );
- xml.writeAttribute( "message", result.getExpression() );
- xml.writeAttribute( "type", result.getTestMacroName() );
+ xml.writeAttribute( "message"_sr, result.getExpression() );
+ xml.writeAttribute( "type"_sr, result.getTestMacroName() );
ReusableStringStream rss;
if (stats.totals.assertions.total() > 0) {
@@ -8404,164 +9391,189 @@ namespace Catch {
-#include <cassert>
+
+#include <ostream>
namespace Catch {
+ void MultiReporter::updatePreferences(IEventListener const& reporterish) {
+ m_preferences.shouldRedirectStdOut |=
+ reporterish.getPreferences().shouldRedirectStdOut;
+ m_preferences.shouldReportAllAssertions |=
+ reporterish.getPreferences().shouldReportAllAssertions;
+ }
- ListeningReporter::ListeningReporter() {
- // We will assume that listeners will always want all assertions
- m_preferences.shouldReportAllAssertions = true;
+ void MultiReporter::addListener( IEventListenerPtr&& listener ) {
+ updatePreferences(*listener);
+ m_reporterLikes.insert(m_reporterLikes.begin() + m_insertedListeners, CATCH_MOVE(listener) );
+ ++m_insertedListeners;
}
- void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) {
- m_listeners.push_back( std::move( listener ) );
+ void MultiReporter::addReporter( IEventListenerPtr&& reporter ) {
+ updatePreferences(*reporter);
+
+ // We will need to output the captured stdout if there are reporters
+ // that do not want it captured.
+ // We do not consider listeners, because it is generally assumed that
+ // listeners are output-transparent, even though they can ask for stdout
+ // capture to do something with it.
+ m_haveNoncapturingReporters |= !reporter->getPreferences().shouldRedirectStdOut;
+
+ // Reporters can always be placed to the back without breaking the
+ // reporting order
+ m_reporterLikes.push_back( CATCH_MOVE( reporter ) );
}
- void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) {
- assert(!m_reporter && "Listening reporter can wrap only 1 real reporter");
- m_reporter = std::move( reporter );
- m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut;
+ void MultiReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->noMatchingTestCases( unmatchedSpec );
+ }
}
- void ListeningReporter::noMatchingTestCases( std::string const& spec ) {
- for ( auto const& listener : m_listeners ) {
- listener->noMatchingTestCases( spec );
+ void MultiReporter::fatalErrorEncountered( StringRef error ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->fatalErrorEncountered( error );
}
- m_reporter->noMatchingTestCases( spec );
}
- void ListeningReporter::reportInvalidArguments(std::string const&arg){
- for ( auto const& listener : m_listeners ) {
- listener->reportInvalidArguments( arg );
+ void MultiReporter::reportInvalidTestSpec( StringRef arg ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->reportInvalidTestSpec( arg );
}
- m_reporter->reportInvalidArguments( arg );
}
- void ListeningReporter::benchmarkPreparing( std::string const& name ) {
- for (auto const& listener : m_listeners) {
- listener->benchmarkPreparing(name);
+ void MultiReporter::benchmarkPreparing( StringRef name ) {
+ for (auto& reporterish : m_reporterLikes) {
+ reporterish->benchmarkPreparing(name);
}
- m_reporter->benchmarkPreparing(name);
}
- void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
- for ( auto const& listener : m_listeners ) {
- listener->benchmarkStarting( benchmarkInfo );
+ void MultiReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->benchmarkStarting( benchmarkInfo );
}
- m_reporter->benchmarkStarting( benchmarkInfo );
}
- void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
- for ( auto const& listener : m_listeners ) {
- listener->benchmarkEnded( benchmarkStats );
+ void MultiReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->benchmarkEnded( benchmarkStats );
}
- m_reporter->benchmarkEnded( benchmarkStats );
}
- void ListeningReporter::benchmarkFailed( std::string const& error ) {
- for (auto const& listener : m_listeners) {
- listener->benchmarkFailed(error);
+ void MultiReporter::benchmarkFailed( StringRef error ) {
+ for (auto& reporterish : m_reporterLikes) {
+ reporterish->benchmarkFailed(error);
}
- m_reporter->benchmarkFailed(error);
}
- void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
- for ( auto const& listener : m_listeners ) {
- listener->testRunStarting( testRunInfo );
+ void MultiReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->testRunStarting( testRunInfo );
}
- m_reporter->testRunStarting( testRunInfo );
}
- void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) {
- for ( auto const& listener : m_listeners ) {
- listener->testGroupStarting( groupInfo );
+ void MultiReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->testCaseStarting( testInfo );
}
- m_reporter->testGroupStarting( groupInfo );
}
-
- void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
- for ( auto const& listener : m_listeners ) {
- listener->testCaseStarting( testInfo );
+ void
+ MultiReporter::testCasePartialStarting( TestCaseInfo const& testInfo,
+ uint64_t partNumber ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->testCasePartialStarting( testInfo, partNumber );
}
- m_reporter->testCaseStarting( testInfo );
}
- void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) {
- for ( auto const& listener : m_listeners ) {
- listener->sectionStarting( sectionInfo );
+ void MultiReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->sectionStarting( sectionInfo );
}
- m_reporter->sectionStarting( sectionInfo );
}
- void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
- for ( auto const& listener : m_listeners ) {
- listener->assertionStarting( assertionInfo );
+ void MultiReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->assertionStarting( assertionInfo );
}
- m_reporter->assertionStarting( assertionInfo );
}
// The return value indicates if the messages buffer should be cleared:
- bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) {
- for( auto const& listener : m_listeners ) {
- static_cast<void>( listener->assertionEnded( assertionStats ) );
+ void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) {
+ const bool reportByDefault =
+ assertionStats.assertionResult.getResultType() != ResultWas::Ok ||
+ m_config->includeSuccessfulResults();
+
+ for ( auto & reporterish : m_reporterLikes ) {
+ if ( reportByDefault ||
+ reporterish->getPreferences().shouldReportAllAssertions ) {
+ reporterish->assertionEnded( assertionStats );
+ }
}
- return m_reporter->assertionEnded( assertionStats );
}
- void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) {
- for ( auto const& listener : m_listeners ) {
- listener->sectionEnded( sectionStats );
+ void MultiReporter::sectionEnded( SectionStats const& sectionStats ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->sectionEnded( sectionStats );
}
- m_reporter->sectionEnded( sectionStats );
}
- void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
- for ( auto const& listener : m_listeners ) {
- listener->testCaseEnded( testCaseStats );
+ void MultiReporter::testCasePartialEnded( TestCaseStats const& testStats,
+ uint64_t partNumber ) {
+ if ( m_preferences.shouldRedirectStdOut &&
+ m_haveNoncapturingReporters ) {
+ if ( !testStats.stdOut.empty() ) {
+ Catch::cout() << testStats.stdOut << std::flush;
+ }
+ if ( !testStats.stdErr.empty() ) {
+ Catch::cerr() << testStats.stdErr << std::flush;
+ }
+ }
+
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->testCasePartialEnded( testStats, partNumber );
}
- m_reporter->testCaseEnded( testCaseStats );
}
- void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
- for ( auto const& listener : m_listeners ) {
- listener->testGroupEnded( testGroupStats );
+ void MultiReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->testCaseEnded( testCaseStats );
}
- m_reporter->testGroupEnded( testGroupStats );
}
- void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) {
- for ( auto const& listener : m_listeners ) {
- listener->testRunEnded( testRunStats );
+ void MultiReporter::testRunEnded( TestRunStats const& testRunStats ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->testRunEnded( testRunStats );
}
- m_reporter->testRunEnded( testRunStats );
}
- void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) {
- for ( auto const& listener : m_listeners ) {
- listener->skipTest( testInfo );
+ void MultiReporter::skipTest( TestCaseInfo const& testInfo ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->skipTest( testInfo );
}
- m_reporter->skipTest( testInfo );
}
- void ListeningReporter::listReporters(std::vector<ReporterDescription> const& descriptions, IConfig const& config) {
- for (auto const& listener : m_listeners) {
- listener->listReporters(descriptions, config);
+ void MultiReporter::listReporters(std::vector<ReporterDescription> const& descriptions) {
+ for (auto& reporterish : m_reporterLikes) {
+ reporterish->listReporters(descriptions);
}
- m_reporter->listReporters(descriptions, config);
}
- void ListeningReporter::listTests(std::vector<TestCaseHandle> const& tests, IConfig const& config) {
- for (auto const& listener : m_listeners) {
- listener->listTests(tests, config);
+ void MultiReporter::listListeners(
+ std::vector<ListenerDescription> const& descriptions ) {
+ for ( auto& reporterish : m_reporterLikes ) {
+ reporterish->listListeners( descriptions );
}
- m_reporter->listTests(tests, config);
}
- void ListeningReporter::listTags(std::vector<TagInfo> const& tags, IConfig const& config) {
- for (auto const& listener : m_listeners) {
- listener->listTags(tags, config);
+ void MultiReporter::listTests(std::vector<TestCaseHandle> const& tests) {
+ for (auto& reporterish : m_reporterLikes) {
+ reporterish->listTests(tests);
+ }
+ }
+
+ void MultiReporter::listTags(std::vector<TagInfo> const& tags) {
+ for (auto& reporterish : m_reporterLikes) {
+ reporterish->listTags(tags);
}
- m_reporter->listTags(tags, config);
}
} // end namespace Catch
@@ -8569,35 +9581,65 @@ namespace Catch {
+
+namespace Catch {
+ namespace Detail {
+
+ void registerReporterImpl( std::string const& name,
+ IReporterFactoryPtr reporterPtr ) {
+ CATCH_TRY {
+ getMutableRegistryHub().registerReporter(
+ name, CATCH_MOVE( reporterPtr ) );
+ }
+ CATCH_CATCH_ALL {
+ // Do not throw when constructing global objects, instead
+ // register the exception to be processed later
+ getMutableRegistryHub().registerStartupException();
+ }
+ }
+
+ } // namespace Detail
+} // namespace Catch
+
+
+
+
#include <map>
namespace Catch {
- SonarQubeReporter::~SonarQubeReporter() {}
+ namespace {
+ std::string createRngSeedString(uint32_t seed) {
+ ReusableStringStream sstr;
+ sstr << "rng-seed=" << seed;
+ return sstr.str();
+ }
+ }
void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) {
CumulativeReporterBase::testRunStarting(testRunInfo);
+
+ xml.writeComment( createRngSeedString( m_config->rngSeed() ) );
xml.startElement("testExecutions");
- xml.writeAttribute("version", '1');
+ xml.writeAttribute("version"_sr, '1');
}
- void SonarQubeReporter::testGroupEnded(TestGroupStats const& testGroupStats) {
- CumulativeReporterBase::testGroupEnded(testGroupStats);
- writeGroup(*m_testGroups.back());
- }
+ void SonarQubeReporter::writeRun( TestRunNode const& runNode ) {
+ std::map<std::string, std::vector<TestCaseNode const*>> testsPerFile;
- void SonarQubeReporter::writeGroup(TestGroupNode const& groupNode) {
- std::map<std::string, TestGroupNode::ChildNodes> testsPerFile;
- for (auto const& child : groupNode.children)
- testsPerFile[child->value.testInfo->lineInfo.file].push_back(child);
+ for ( auto const& child : runNode.children ) {
+ testsPerFile[child->value.testInfo->lineInfo.file].push_back(
+ child.get() );
+ }
- for (auto const& kv : testsPerFile)
- writeTestFile(kv.first, kv.second);
+ for ( auto const& kv : testsPerFile ) {
+ writeTestFile( kv.first, kv.second );
+ }
}
- void SonarQubeReporter::writeTestFile(std::string const& filename, TestGroupNode::ChildNodes const& testCaseNodes) {
+ void SonarQubeReporter::writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes) {
XmlWriter::ScopedElement e = xml.scopedElement("file");
- xml.writeAttribute("path", filename);
+ xml.writeAttribute("path"_sr, filename);
for (auto const& child : testCaseNodes)
writeTestCase(*child);
@@ -8616,10 +9658,12 @@ namespace Catch {
if (!rootName.empty())
name = rootName + '/' + name;
- if (!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
+ if ( sectionNode.hasAnyAssertions()
+ || !sectionNode.stdOut.empty()
+ || !sectionNode.stdErr.empty() ) {
XmlWriter::ScopedElement e = xml.scopedElement("testCase");
- xml.writeAttribute("name", name);
- xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
+ xml.writeAttribute("name"_sr, name);
+ xml.writeAttribute("duration"_sr, static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
writeAssertions(sectionNode, okToFail);
}
@@ -8629,8 +9673,11 @@ namespace Catch {
}
void SonarQubeReporter::writeAssertions(SectionNode const& sectionNode, bool okToFail) {
- for (auto const& assertion : sectionNode.assertions)
- writeAssertion(assertion, okToFail);
+ for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) {
+ if (assertionOrBenchmark.isAssertion()) {
+ writeAssertion(assertionOrBenchmark.asAssertion(), okToFail);
+ }
+ }
}
void SonarQubeReporter::writeAssertion(AssertionStats const& stats, bool okToFail) {
@@ -8670,26 +9717,26 @@ namespace Catch {
XmlWriter::ScopedElement e = xml.scopedElement(elementName);
ReusableStringStream messageRss;
- messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
- xml.writeAttribute("message", messageRss.str());
+ messageRss << result.getTestMacroName() << '(' << result.getExpression() << ')';
+ xml.writeAttribute("message"_sr, messageRss.str());
ReusableStringStream textRss;
if (stats.totals.assertions.total() > 0) {
textRss << "FAILED:\n";
if (result.hasExpression()) {
- textRss << "\t" << result.getExpressionInMacro() << "\n";
+ textRss << '\t' << result.getExpressionInMacro() << '\n';
}
if (result.hasExpandedExpression()) {
- textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
+ textRss << "with expansion:\n\t" << result.getExpandedExpression() << '\n';
}
}
if (!result.getMessage().empty())
- textRss << result.getMessage() << "\n";
+ textRss << result.getMessage() << '\n';
for (auto const& msg : stats.infoMessages)
if (msg.type == ResultWas::Info)
- textRss << msg.message << "\n";
+ textRss << msg.message << '\n';
textRss << "at " << result.getSourceInfo();
xml.writeText(textRss.str(), XmlFormatting::Newline);
@@ -8709,19 +9756,8 @@ namespace Catch {
currentTestRunInfo = _testRunInfo;
}
- void
- StreamingReporterBase::testGroupStarting( GroupInfo const& _groupInfo ) {
- currentGroupInfo = _groupInfo;
- }
-
- void StreamingReporterBase::testGroupEnded( TestGroupStats const& ) {
- currentGroupInfo.reset();
- }
-
void StreamingReporterBase::testRunEnded( TestRunStats const& ) {
currentTestCaseInfo = nullptr;
- currentGroupInfo.reset();
- currentTestRunInfo.reset();
}
} // end namespace Catch
@@ -8729,6 +9765,7 @@ namespace Catch {
#include <algorithm>
+#include <iterator>
#include <ostream>
namespace Catch {
@@ -8738,18 +9775,20 @@ namespace Catch {
// Making older compiler happy is hard.
static constexpr StringRef tapFailedString = "not ok"_sr;
static constexpr StringRef tapPassedString = "ok"_sr;
+ static constexpr Colour::Code tapDimColour = Colour::FileName;
class TapAssertionPrinter {
public:
TapAssertionPrinter& operator= (TapAssertionPrinter const&) = delete;
TapAssertionPrinter(TapAssertionPrinter const&) = delete;
- TapAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter)
+ TapAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter, ColourImpl* colour_)
: stream(_stream)
, result(_stats.assertionResult)
, messages(_stats.infoMessages)
, itMessage(_stats.infoMessages.begin())
, printInfoMessages(true)
- , counter(_counter) {}
+ , counter(_counter)
+ , colourImpl( colour_ ) {}
void print() {
itMessage = messages.begin();
@@ -8822,13 +9861,6 @@ namespace Catch {
}
private:
- static Colour::Code dimColour() { return Colour::FileName; }
-
- void printSourceInfo() const {
- Colour colourGuard(dimColour());
- stream << result.getSourceInfo() << ':';
- }
-
void printResultType(StringRef passOrFail) const {
if (!passOrFail.empty()) {
stream << passOrFail << ' ' << counter << " -";
@@ -8842,10 +9874,8 @@ namespace Catch {
void printExpressionWas() {
if (result.hasExpression()) {
stream << ';';
- {
- Colour colour(dimColour());
- stream << " expression was:";
- }
+ stream << colourImpl->guardColour( tapDimColour )
+ << " expression was:";
printOriginalExpression();
}
}
@@ -8858,10 +9888,8 @@ namespace Catch {
void printReconstructedExpression() const {
if (result.hasExpandedExpression()) {
- {
- Colour colour(dimColour());
- stream << " for: ";
- }
+ stream << colourImpl->guardColour( tapDimColour ) << " for: ";
+
std::string expr = result.getExpandedExpression();
std::replace(expr.begin(), expr.end(), '\n', ' ');
stream << expr;
@@ -8875,7 +9903,7 @@ namespace Catch {
}
}
- void printRemainingMessages(Colour::Code colour = dimColour()) {
+ void printRemainingMessages(Colour::Code colour = tapDimColour) {
if (itMessage == messages.end()) {
return;
}
@@ -8884,18 +9912,15 @@ namespace Catch {
std::vector<MessageInfo>::const_iterator itEnd = messages.end();
const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
- {
- Colour colourGuard(colour);
- stream << " with " << pluralise(N, "message") << ':';
- }
+ stream << colourImpl->guardColour( colour ) << " with "
+ << pluralise( N, "message"_sr ) << ':';
for (; itMessage != itEnd; ) {
// If this assertion is a warning ignore any INFO messages
if (printInfoMessages || itMessage->type != ResultWas::Info) {
stream << " '" << itMessage->message << '\'';
if (++itMessage != itEnd) {
- Colour colourGuard(dimColour());
- stream << " and";
+ stream << colourImpl->guardColour(tapDimColour) << " and";
}
}
}
@@ -8908,33 +9933,35 @@ namespace Catch {
std::vector<MessageInfo>::const_iterator itMessage;
bool printInfoMessages;
std::size_t counter;
+ ColourImpl* colourImpl;
};
} // End anonymous namespace
- TAPReporter::~TAPReporter() {}
+ void TAPReporter::testRunStarting( TestRunInfo const& ) {
+ m_stream << "# rng-seed: " << m_config->rngSeed() << '\n';
+ }
- void TAPReporter::noMatchingTestCases(std::string const& spec) {
- stream << "# No test cases matched '" << spec << "'\n";
+ void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+ m_stream << "# No test cases matched '" << unmatchedSpec << "'\n";
}
- bool TAPReporter::assertionEnded(AssertionStats const& _assertionStats) {
+ void TAPReporter::assertionEnded(AssertionStats const& _assertionStats) {
++counter;
- stream << "# " << currentTestCaseInfo->name << '\n';
- TapAssertionPrinter printer(stream, _assertionStats, counter);
+ m_stream << "# " << currentTestCaseInfo->name << '\n';
+ TapAssertionPrinter printer(m_stream, _assertionStats, counter, m_colour.get());
printer.print();
- stream << '\n' << std::flush;
- return true;
+ m_stream << '\n' << std::flush;
}
void TAPReporter::testRunEnded(TestRunStats const& _testRunStats) {
- stream << "1.." << _testRunStats.totals.assertions.total();
+ m_stream << "1.." << _testRunStats.totals.assertions.total();
if (_testRunStats.totals.testCases.total() == 0) {
- stream << " # Skipped: No tests ran.";
+ m_stream << " # Skipped: No tests ran.";
}
- stream << "\n\n" << std::flush;
+ m_stream << "\n\n" << std::flush;
StreamingReporterBase::testRunEnded(_testRunStats);
}
@@ -8947,6 +9974,7 @@ namespace Catch {
#include <cassert>
+#include <ostream>
namespace Catch {
@@ -8964,8 +9992,8 @@ namespace Catch {
.initialIndent(indent) << '\n';
}
- std::string escape(std::string const& str) {
- std::string escaped = str;
+ std::string escape(StringRef str) {
+ std::string escaped = static_cast<std::string>(str);
replaceInPlace(escaped, "|", "||");
replaceInPlace(escaped, "'", "|'");
replaceInPlace(escaped, "\n", "|n");
@@ -8979,19 +10007,17 @@ namespace Catch {
TeamCityReporter::~TeamCityReporter() {}
- void TeamCityReporter::testGroupStarting(GroupInfo const& groupInfo) {
- StreamingReporterBase::testGroupStarting(groupInfo);
- stream << "##teamcity[testSuiteStarted name='"
- << escape(groupInfo.name) << "']\n";
+ void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) {
+ m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name )
+ << "']\n";
}
- void TeamCityReporter::testGroupEnded(TestGroupStats const& testGroupStats) {
- StreamingReporterBase::testGroupEnded(testGroupStats);
- stream << "##teamcity[testSuiteFinished name='"
- << escape(testGroupStats.groupInfo.name) << "']\n";
+ void TeamCityReporter::testRunEnded( TestRunStats const& runStats ) {
+ m_stream << "##teamcity[testSuiteFinished name='"
+ << escape( runStats.runInfo.name ) << "']\n";
}
- bool TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) {
+ void TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) {
AssertionResult const& result = assertionStats.assertionResult;
if (!result.isOk()) {
@@ -9047,44 +10073,43 @@ namespace Catch {
if (currentTestCaseInfo->okToFail()) {
msg << "- failure ignore as test marked as 'ok to fail'\n";
- stream << "##teamcity[testIgnored"
+ m_stream << "##teamcity[testIgnored"
<< " name='" << escape(currentTestCaseInfo->name) << '\''
<< " message='" << escape(msg.str()) << '\''
<< "]\n";
} else {
- stream << "##teamcity[testFailed"
+ m_stream << "##teamcity[testFailed"
<< " name='" << escape(currentTestCaseInfo->name) << '\''
<< " message='" << escape(msg.str()) << '\''
<< "]\n";
}
}
- stream.flush();
- return true;
+ m_stream.flush();
}
void TeamCityReporter::testCaseStarting(TestCaseInfo const& testInfo) {
m_testTimer.start();
StreamingReporterBase::testCaseStarting(testInfo);
- stream << "##teamcity[testStarted name='"
+ m_stream << "##teamcity[testStarted name='"
<< escape(testInfo.name) << "']\n";
- stream.flush();
+ m_stream.flush();
}
void TeamCityReporter::testCaseEnded(TestCaseStats const& testCaseStats) {
StreamingReporterBase::testCaseEnded(testCaseStats);
auto const& testCaseInfo = *testCaseStats.testInfo;
if (!testCaseStats.stdOut.empty())
- stream << "##teamcity[testStdOut name='"
+ m_stream << "##teamcity[testStdOut name='"
<< escape(testCaseInfo.name)
<< "' out='" << escape(testCaseStats.stdOut) << "']\n";
if (!testCaseStats.stdErr.empty())
- stream << "##teamcity[testStdErr name='"
+ m_stream << "##teamcity[testStdErr name='"
<< escape(testCaseInfo.name)
<< "' out='" << escape(testCaseStats.stdErr) << "']\n";
- stream << "##teamcity[testFinished name='"
+ m_stream << "##teamcity[testFinished name='"
<< escape(testCaseInfo.name) << "' duration='"
<< m_testTimer.getElapsedMilliseconds() << "']\n";
- stream.flush();
+ m_stream.flush();
}
void TeamCityReporter::printSectionHeader(std::ostream& os) {
@@ -9120,9 +10145,9 @@ namespace Catch {
#endif
namespace Catch {
- XmlReporter::XmlReporter( ReporterConfig const& _config )
- : StreamingReporterBase( _config ),
- m_xml(_config.stream())
+ XmlReporter::XmlReporter( ReporterConfig&& _config )
+ : StreamingReporterBase( CATCH_MOVE(_config) ),
+ m_xml(m_stream)
{
m_preferences.shouldRedirectStdOut = true;
m_preferences.shouldReportAllAssertions = true;
@@ -9140,12 +10165,8 @@ namespace Catch {
void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) {
m_xml
- .writeAttribute( "filename", sourceInfo.file )
- .writeAttribute( "line", sourceInfo.line );
- }
-
- void XmlReporter::noMatchingTestCases( std::string const& s ) {
- StreamingReporterBase::noMatchingTestCases( s );
+ .writeAttribute( "filename"_sr, sourceInfo.file )
+ .writeAttribute( "line"_sr, sourceInfo.line );
}
void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) {
@@ -9153,27 +10174,19 @@ namespace Catch {
std::string stylesheetRef = getStylesheetRef();
if( !stylesheetRef.empty() )
m_xml.writeStylesheetRef( stylesheetRef );
- m_xml.startElement( "Catch" );
- if( !m_config->name().empty() )
- m_xml.writeAttribute( "name", m_config->name() );
+ m_xml.startElement("Catch2TestRun")
+ .writeAttribute("name"_sr, m_config->name())
+ .writeAttribute("rng-seed"_sr, m_config->rngSeed())
+ .writeAttribute("catch2-version"_sr, libraryVersion());
if (m_config->testSpec().hasFilters())
- m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) );
- if( m_config->rngSeed() != 0 )
- m_xml.scopedElement( "Randomness" )
- .writeAttribute( "seed", m_config->rngSeed() );
- }
-
- void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) {
- StreamingReporterBase::testGroupStarting( groupInfo );
- m_xml.startElement( "Group" )
- .writeAttribute( "name", groupInfo.name );
+ m_xml.writeAttribute( "filters"_sr, serializeFilters( m_config->getTestsOrTags() ) );
}
void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
StreamingReporterBase::testCaseStarting(testInfo);
m_xml.startElement( "TestCase" )
- .writeAttribute( "name", trim( testInfo.name ) )
- .writeAttribute( "tags", testInfo.tagsAsString() );
+ .writeAttribute( "name"_sr, trim( testInfo.name ) )
+ .writeAttribute( "tags"_sr, testInfo.tagsAsString() );
writeSourceInfo( testInfo.lineInfo );
@@ -9186,7 +10199,7 @@ namespace Catch {
StreamingReporterBase::sectionStarting( sectionInfo );
if( m_sectionDepth++ > 0 ) {
m_xml.startElement( "Section" )
- .writeAttribute( "name", trim( sectionInfo.name ) );
+ .writeAttribute( "name"_sr, trim( sectionInfo.name ) );
writeSourceInfo( sectionInfo.lineInfo );
m_xml.ensureTagClosed();
}
@@ -9194,7 +10207,7 @@ namespace Catch {
void XmlReporter::assertionStarting( AssertionInfo const& ) { }
- bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
+ void XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
AssertionResult const& result = assertionStats.assertionResult;
@@ -9215,14 +10228,14 @@ namespace Catch {
// Drop out if result was successful but we're not printing them.
if( !includeResults && result.getResultType() != ResultWas::Warning )
- return true;
+ return;
// Print the expression if there is one.
if( result.hasExpression() ) {
m_xml.startElement( "Expression" )
- .writeAttribute( "success", result.succeeded() )
- .writeAttribute( "type", result.getTestMacroName() );
+ .writeAttribute( "success"_sr, result.succeeded() )
+ .writeAttribute( "type"_sr, result.getTestMacroName() );
writeSourceInfo( result.getSourceInfo() );
@@ -9248,7 +10261,7 @@ namespace Catch {
break;
case ResultWas::Info:
m_xml.scopedElement( "Info" )
- .writeText( result.getMessage() );
+ .writeText( result.getMessage() );
break;
case ResultWas::Warning:
// Warning will already have been written
@@ -9265,20 +10278,18 @@ namespace Catch {
if( result.hasExpression() )
m_xml.endElement();
-
- return true;
}
void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
StreamingReporterBase::sectionEnded( sectionStats );
if( --m_sectionDepth > 0 ) {
XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
- e.writeAttribute( "successes", sectionStats.assertions.passed );
- e.writeAttribute( "failures", sectionStats.assertions.failed );
- e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+ e.writeAttribute( "successes"_sr, sectionStats.assertions.passed );
+ e.writeAttribute( "failures"_sr, sectionStats.assertions.failed );
+ e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk );
if ( m_config->showDurations() == ShowDurations::Always )
- e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+ e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds );
m_xml.endElement();
}
@@ -9287,10 +10298,10 @@ namespace Catch {
void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
StreamingReporterBase::testCaseEnded( testCaseStats );
XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
- e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+ e.writeAttribute( "success"_sr, testCaseStats.totals.assertions.allOk() );
if ( m_config->showDurations() == ShowDurations::Always )
- e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+ e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() );
if( !testCaseStats.stdOut.empty() )
m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
@@ -9300,77 +10311,63 @@ namespace Catch {
m_xml.endElement();
}
- void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
- StreamingReporterBase::testGroupEnded( testGroupStats );
- // TODO: Check testGroupStats.aborting and act accordingly.
- m_xml.scopedElement( "OverallResults" )
- .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
- .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
- .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
- m_xml.scopedElement( "OverallResultsCases")
- .writeAttribute( "successes", testGroupStats.totals.testCases.passed )
- .writeAttribute( "failures", testGroupStats.totals.testCases.failed )
- .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk );
- m_xml.endElement();
- }
-
void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) {
StreamingReporterBase::testRunEnded( testRunStats );
m_xml.scopedElement( "OverallResults" )
- .writeAttribute( "successes", testRunStats.totals.assertions.passed )
- .writeAttribute( "failures", testRunStats.totals.assertions.failed )
- .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+ .writeAttribute( "successes"_sr, testRunStats.totals.assertions.passed )
+ .writeAttribute( "failures"_sr, testRunStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk );
m_xml.scopedElement( "OverallResultsCases")
- .writeAttribute( "successes", testRunStats.totals.testCases.passed )
- .writeAttribute( "failures", testRunStats.totals.testCases.failed )
- .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk );
+ .writeAttribute( "successes"_sr, testRunStats.totals.testCases.passed )
+ .writeAttribute( "failures"_sr, testRunStats.totals.testCases.failed )
+ .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk );
m_xml.endElement();
}
- void XmlReporter::benchmarkPreparing(std::string const& name) {
+ void XmlReporter::benchmarkPreparing( StringRef name ) {
m_xml.startElement("BenchmarkResults")
- .writeAttribute("name", name);
+ .writeAttribute("name"_sr, name);
}
void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
- m_xml.writeAttribute("samples", info.samples)
- .writeAttribute("resamples", info.resamples)
- .writeAttribute("iterations", info.iterations)
- .writeAttribute("clockResolution", info.clockResolution)
- .writeAttribute("estimatedDuration", info.estimatedDuration)
- .writeComment("All values in nano seconds");
+ m_xml.writeAttribute("samples"_sr, info.samples)
+ .writeAttribute("resamples"_sr, info.resamples)
+ .writeAttribute("iterations"_sr, info.iterations)
+ .writeAttribute("clockResolution"_sr, info.clockResolution)
+ .writeAttribute("estimatedDuration"_sr, info.estimatedDuration)
+ .writeComment("All values in nano seconds"_sr);
}
void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
m_xml.startElement("mean")
- .writeAttribute("value", benchmarkStats.mean.point.count())
- .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count())
- .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count())
- .writeAttribute("ci", benchmarkStats.mean.confidence_interval);
+ .writeAttribute("value"_sr, benchmarkStats.mean.point.count())
+ .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count())
+ .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count())
+ .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval);
m_xml.endElement();
m_xml.startElement("standardDeviation")
- .writeAttribute("value", benchmarkStats.standardDeviation.point.count())
- .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count())
- .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count())
- .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval);
+ .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count())
+ .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count())
+ .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count())
+ .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval);
m_xml.endElement();
m_xml.startElement("outliers")
- .writeAttribute("variance", benchmarkStats.outlierVariance)
- .writeAttribute("lowMild", benchmarkStats.outliers.low_mild)
- .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe)
- .writeAttribute("highMild", benchmarkStats.outliers.high_mild)
- .writeAttribute("highSevere", benchmarkStats.outliers.high_severe);
+ .writeAttribute("variance"_sr, benchmarkStats.outlierVariance)
+ .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild)
+ .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe)
+ .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild)
+ .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe);
m_xml.endElement();
m_xml.endElement();
}
- void XmlReporter::benchmarkFailed(std::string const &error) {
+ void XmlReporter::benchmarkFailed(StringRef error) {
m_xml.scopedElement("failed").
- writeAttribute("message", error);
+ writeAttribute("message"_sr, error);
m_xml.endElement();
}
- void XmlReporter::listReporters(std::vector<ReporterDescription> const& descriptions, IConfig const&) {
+ void XmlReporter::listReporters(std::vector<ReporterDescription> const& descriptions) {
auto outerTag = m_xml.scopedElement("AvailableReporters");
for (auto const& reporter : descriptions) {
auto inner = m_xml.scopedElement("Reporter");
@@ -9383,7 +10380,20 @@ namespace Catch {
}
}
- void XmlReporter::listTests(std::vector<TestCaseHandle> const& tests, IConfig const&) {
+ void XmlReporter::listListeners(std::vector<ListenerDescription> const& descriptions) {
+ auto outerTag = m_xml.scopedElement( "RegisteredListeners" );
+ for ( auto const& listener : descriptions ) {
+ auto inner = m_xml.scopedElement( "Listener" );
+ m_xml.startElement( "Name", XmlFormatting::Indent )
+ .writeText( listener.name, XmlFormatting::None )
+ .endElement( XmlFormatting::Newline );
+ m_xml.startElement( "Description", XmlFormatting::Indent )
+ .writeText( listener.description, XmlFormatting::None )
+ .endElement( XmlFormatting::Newline );
+ }
+ }
+
+ void XmlReporter::listTests(std::vector<TestCaseHandle> const& tests) {
auto outerTag = m_xml.scopedElement("MatchingTests");
for (auto const& test : tests) {
auto innerTag = m_xml.scopedElement("TestCase");
@@ -9408,7 +10418,7 @@ namespace Catch {
}
}
- void XmlReporter::listTags(std::vector<TagInfo> const& tags, IConfig const&) {
+ void XmlReporter::listTags(std::vector<TagInfo> const& tags) {
auto outerTag = m_xml.scopedElement("TagsFromMatchingTests");
for (auto const& tag : tags) {
auto innerTag = m_xml.scopedElement("Tag");
@@ -9418,7 +10428,7 @@ namespace Catch {
auto aliasTag = m_xml.scopedElement("Aliases");
for (auto const& alias : tag.spellings) {
m_xml.startElement("Alias", XmlFormatting::Indent)
- .writeText(static_cast<std::string>(alias), XmlFormatting::None)
+ .writeText(alias, XmlFormatting::None)
.endElement(XmlFormatting::Newline);
}
}
diff --git a/include/catch2/catch_amalgamated.hpp b/include/catch2/catch_amalgamated.hpp
index ddbbb4e..5c1caa3 100644
--- a/include/catch2/catch_amalgamated.hpp
+++ b/include/catch2/catch_amalgamated.hpp
@@ -5,8 +5,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.0.0-preview.3
-// Generated: 2020-10-08 13:59:26.309308
+// Catch v3.0.1
+// Generated: 2022-05-17 22:08:46.674860
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -86,6 +86,117 @@ namespace Catch {
#endif // CATCH_NONCOPYABLE_HPP_INCLUDED
+
+#ifndef CATCH_STRINGREF_HPP_INCLUDED
+#define CATCH_STRINGREF_HPP_INCLUDED
+
+#include <cstddef>
+#include <string>
+#include <iosfwd>
+#include <cassert>
+
+namespace Catch {
+
+ /// A non-owning string class (similar to the forthcoming std::string_view)
+ /// Note that, because a StringRef may be a substring of another string,
+ /// it may not be null terminated.
+ class StringRef {
+ public:
+ using size_type = std::size_t;
+ using const_iterator = const char*;
+
+ private:
+ static constexpr char const* const s_empty = "";
+
+ char const* m_start = s_empty;
+ size_type m_size = 0;
+
+ public: // construction
+ constexpr StringRef() noexcept = default;
+
+ StringRef( char const* rawChars ) noexcept;
+
+ constexpr StringRef( char const* rawChars, size_type size ) noexcept
+ : m_start( rawChars ),
+ m_size( size )
+ {}
+
+ StringRef( std::string const& stdString ) noexcept
+ : m_start( stdString.c_str() ),
+ m_size( stdString.size() )
+ {}
+
+ explicit operator std::string() const {
+ return std::string(m_start, m_size);
+ }
+
+ public: // operators
+ auto operator == ( StringRef other ) const noexcept -> bool;
+ auto operator != (StringRef other) const noexcept -> bool {
+ return !(*this == other);
+ }
+
+ constexpr auto operator[] ( size_type index ) const noexcept -> char {
+ assert(index < m_size);
+ return m_start[index];
+ }
+
+ bool operator<(StringRef rhs) const noexcept;
+
+ public: // named queries
+ constexpr auto empty() const noexcept -> bool {
+ return m_size == 0;
+ }
+ constexpr auto size() const noexcept -> size_type {
+ return m_size;
+ }
+
+ // Returns a substring of [start, start + length).
+ // If start + length > size(), then the substring is [start, start + size()).
+ // If start > size(), then the substring is empty.
+ constexpr StringRef substr(size_type start, size_type length) const noexcept {
+ if (start < m_size) {
+ const auto shortened_size = m_size - start;
+ return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length);
+ } else {
+ return StringRef();
+ }
+ }
+
+ // Returns the current start pointer. May not be null-terminated.
+ constexpr char const* data() const noexcept {
+ return m_start;
+ }
+
+ constexpr const_iterator begin() const { return m_start; }
+ constexpr const_iterator end() const { return m_start + m_size; }
+
+
+ friend std::string& operator += (std::string& lhs, StringRef sr);
+ friend std::ostream& operator << (std::ostream& os, StringRef sr);
+ friend std::string operator+(StringRef lhs, StringRef rhs);
+
+ /**
+ * Provides a three-way comparison with rhs
+ *
+ * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive
+ * number if lhs > rhs
+ */
+ int compare( StringRef rhs ) const;
+ };
+
+
+ constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ return StringRef( rawChars, size );
+ }
+} // namespace Catch
+
+constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+ return Catch::StringRef( rawChars, size );
+}
+
+#endif // CATCH_STRINGREF_HPP_INCLUDED
+
#include <chrono>
#include <iosfwd>
#include <string>
@@ -101,8 +212,10 @@ namespace Catch {
struct WarnAbout { enum What {
Nothing = 0x00,
+ //! A test case or leaf section did not run any assertions
NoAssertions = 0x01,
- NoTests = 0x02
+ //! A command line test spec matched no test cases
+ UnmatchedTestSpec = 0x02,
}; };
enum class ShowDurations {
@@ -115,10 +228,15 @@ namespace Catch {
LexicographicallySorted,
Randomized
};
- enum class UseColour {
- Auto,
- Yes,
- No
+ enum class ColourMode : std::uint8_t {
+ //! Let Catch2 pick implementation based on platform detection
+ PlatformDefault,
+ //! Use ANSI colour code escapes
+ ANSI,
+ //! Use Win32 console colour API
+ Win32,
+ //! Don't use any colour
+ None
};
struct WaitForKeypress { enum When {
Never,
@@ -128,18 +246,19 @@ namespace Catch {
}; };
class TestSpec;
+ class IStream;
- struct IConfig : Detail::NonCopyable {
-
+ class IConfig : public Detail::NonCopyable {
+ public:
virtual ~IConfig();
virtual bool allowThrows() const = 0;
- virtual std::ostream& stream() const = 0;
- virtual std::string name() const = 0;
+ virtual StringRef name() const = 0;
virtual bool includeSuccessfulResults() const = 0;
virtual bool shouldDebugBreak() const = 0;
virtual bool warnAboutMissingAssertions() const = 0;
- virtual bool warnAboutNoTests() const = 0;
+ virtual bool warnAboutUnmatchedTestSpecs() const = 0;
+ virtual bool zeroTestsCountAsSuccess() const = 0;
virtual int abortAfter() const = 0;
virtual bool showInvisibles() const = 0;
virtual ShowDurations showDurations() const = 0;
@@ -148,13 +267,16 @@ namespace Catch {
virtual bool hasTestFilters() const = 0;
virtual std::vector<std::string> const& getTestsOrTags() const = 0;
virtual TestRunOrder runOrder() const = 0;
- virtual unsigned int rngSeed() const = 0;
- virtual UseColour useColour() const = 0;
+ virtual uint32_t rngSeed() const = 0;
+ virtual unsigned int shardCount() const = 0;
+ virtual unsigned int shardIndex() const = 0;
+ virtual ColourMode defaultColourMode() const = 0;
virtual std::vector<std::string> const& getSectionsToRun() const = 0;
virtual Verbosity verbosity() const = 0;
+ virtual bool skipBenchmarks() const = 0;
virtual bool benchmarkNoAnalysis() const = 0;
- virtual int benchmarkSamples() const = 0;
+ virtual unsigned int benchmarkSamples() const = 0;
virtual double benchmarkConfidenceInterval() const = 0;
virtual unsigned int benchmarkResamples() const = 0;
virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
@@ -164,82 +286,12 @@ namespace Catch {
#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED
-#ifndef CATCH_CONTEXT_HPP_INCLUDED
-#define CATCH_CONTEXT_HPP_INCLUDED
-
-namespace Catch {
-
- struct IResultCapture;
- struct IRunner;
- struct IConfig;
-
- struct IContext
- {
- virtual ~IContext();
-
- virtual IResultCapture* getResultCapture() = 0;
- virtual IRunner* getRunner() = 0;
- virtual IConfig const* getConfig() const = 0;
- };
-
- struct IMutableContext : IContext
- {
- virtual ~IMutableContext();
- virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
- virtual void setRunner( IRunner* runner ) = 0;
- virtual void setConfig( IConfig const* config ) = 0;
-
- private:
- static IMutableContext *currentContext;
- friend IMutableContext& getCurrentMutableContext();
- friend void cleanUpContext();
- static void createContext();
- };
-
- inline IMutableContext& getCurrentMutableContext()
- {
- if( !IMutableContext::currentContext )
- IMutableContext::createContext();
- // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
- return *IMutableContext::currentContext;
- }
-
- inline IContext& getCurrentContext()
- {
- return getCurrentMutableContext();
- }
-
- void cleanUpContext();
-
- class SimplePcg32;
- SimplePcg32& rng();
-}
-
-#endif // CATCH_CONTEXT_HPP_INCLUDED
-
-
-#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED
-#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED
-
-
-
-#ifndef CATCH_SECTION_INFO_HPP_INCLUDED
-#define CATCH_SECTION_INFO_HPP_INCLUDED
-
-
-
-#ifndef CATCH_COMMON_HPP_INCLUDED
-#define CATCH_COMMON_HPP_INCLUDED
-
-
-
#ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
#define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
// Detect a number of compiler features - by compiler
// The following features are defined:
//
-// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
@@ -258,13 +310,16 @@ namespace Catch {
#ifndef CATCH_PLATFORM_HPP_INCLUDED
#define CATCH_PLATFORM_HPP_INCLUDED
+// See e.g.:
+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
#ifdef __APPLE__
-# include <TargetConditionals.h>
-# if TARGET_OS_OSX == 1
-# define CATCH_PLATFORM_MAC
-# elif TARGET_OS_IPHONE == 1
-# define CATCH_PLATFORM_IPHONE
-# endif
+# include <TargetConditionals.h>
+# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
+ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
+# define CATCH_PLATFORM_MAC
+# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+# define CATCH_PLATFORM_IPHONE
+# endif
#elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX
@@ -287,9 +342,9 @@ namespace Catch {
#endif
-// We have to avoid both ICC and Clang, because they try to mask themselves
-// as gcc, and we want only GCC in this block
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__)
+// Only GCC compiler should be used in this block, so other compilers trying to
+// mask themselves as GCC should be ignored.
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
@@ -305,7 +360,7 @@ namespace Catch {
#endif
-#if defined(__clang__)
+#if defined(__clang__) && !defined(_MSC_VER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
@@ -320,8 +375,13 @@ namespace Catch {
// REQUIRE(std::string("12") + "34" == "1234")
// ```
//
+// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which
+// results in calls to the immediately evaluated lambda expressions to be
+// reported as unevaluated lambdas.
+// https://developer.nvidia.com/nvidia_bug/3321845.
+//
// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
-# if !defined(__ibmxl__) && !defined(__CUDACC__)
+# if !defined(__ibmxl__) && !defined(__CUDACC__) && !defined( __NVCOMPILER )
# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
# endif
@@ -359,14 +419,12 @@ namespace Catch {
#ifdef __OS400__
# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
-# define CATCH_CONFIG_COLOUR_NONE
#endif
////////////////////////////////////////////////////////////////////////////////
// Android somehow still does not support std::to_string
#if defined(__ANDROID__)
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
-# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
#endif
////////////////////////////////////////////////////////////////////////////////
@@ -408,7 +466,7 @@ namespace Catch {
// Universal Windows platform does not support SEH
// Or console colours (or console at all...)
# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
-# define CATCH_CONFIG_COLOUR_NONE
+# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# else
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
# endif
@@ -435,11 +493,6 @@ namespace Catch {
# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
#endif
-////////////////////////////////////////////////////////////////////////////////
-// DJGPP
-#ifdef __DJGPP__
-# define CATCH_INTERNAL_CONFIG_NO_WCHAR
-#endif // __DJGPP__
////////////////////////////////////////////////////////////////////////////////
// Embarcadero C++Build
@@ -449,25 +502,13 @@ namespace Catch {
////////////////////////////////////////////////////////////////////////////////
-// Use of __COUNTER__ is suppressed during code analysis in
-// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
-// handled by it.
-// Otherwise all supported compilers support COUNTER macro,
-// but user still might want to turn it off
-#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
- #define CATCH_INTERNAL_CONFIG_COUNTER
-#endif
-
-
-////////////////////////////////////////////////////////////////////////////////
-
// RTX is a special version of Windows that is real time.
// This means that it is detected as Windows, but does not provide
// the same set of capabilities as real Windows does.
#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
#define CATCH_INTERNAL_CONFIG_NO_ASYNC
- #define CATCH_CONFIG_COLOUR_NONE
+ #define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
#endif
#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
@@ -489,7 +530,7 @@ namespace Catch {
// Check if byte is available and usable
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
# include <cstddef>
- # if __cpp_lib_byte > 0
+ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
# endif
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
@@ -512,9 +553,6 @@ namespace Catch {
#endif // defined(__has_include)
-#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
-# define CATCH_CONFIG_COUNTER
-#endif
#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
# define CATCH_CONFIG_WINDOWS_SEH
#endif
@@ -522,10 +560,6 @@ namespace Catch {
#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
# define CATCH_CONFIG_POSIX_SIGNALS
#endif
-// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
-#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
-# define CATCH_CONFIG_WCHAR
-#endif
#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
# define CATCH_CONFIG_CPP11_TO_STRING
@@ -556,7 +590,9 @@ namespace Catch {
# define CATCH_CONFIG_NEW_CAPTURE
#endif
-#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#if !defined( CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED ) && \
+ !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) && \
+ !defined( CATCH_CONFIG_NO_DISABLE_EXCEPTIONS )
# define CATCH_CONFIG_DISABLE_EXCEPTIONS
#endif
@@ -568,10 +604,6 @@ namespace Catch {
# define CATCH_CONFIG_USE_ASYNC
#endif
-#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
-# define CATCH_CONFIG_ANDROID_LOGWRITE
-#endif
-
#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
# define CATCH_CONFIG_GLOBAL_NEXTAFTER
#endif
@@ -628,135 +660,98 @@ namespace Catch {
#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#endif
-#endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+#if defined( CATCH_PLATFORM_WINDOWS ) && \
+ !defined( CATCH_CONFIG_COLOUR_WIN32 ) && \
+ !defined( CATCH_CONFIG_NO_COLOUR_WIN32 ) && \
+ !defined( CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 )
+# define CATCH_CONFIG_COLOUR_WIN32
+#endif
-#ifndef CATCH_STRINGREF_HPP_INCLUDED
-#define CATCH_STRINGREF_HPP_INCLUDED
+#endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
-#include <cstddef>
-#include <string>
-#include <iosfwd>
-#include <cassert>
+
+#ifndef CATCH_CONTEXT_HPP_INCLUDED
+#define CATCH_CONTEXT_HPP_INCLUDED
namespace Catch {
- /// A non-owning string class (similar to the forthcoming std::string_view)
- /// Note that, because a StringRef may be a substring of another string,
- /// it may not be null terminated.
- class StringRef {
+ class IResultCapture;
+ class IConfig;
+
+ class IContext {
public:
- using size_type = std::size_t;
- using const_iterator = const char*;
+ virtual ~IContext(); // = default
- private:
- static constexpr char const* const s_empty = "";
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IConfig const* getConfig() const = 0;
+ };
- char const* m_start = s_empty;
- size_type m_size = 0;
+ class IMutableContext : public IContext {
+ public:
+ ~IMutableContext() override; // = default
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setConfig( IConfig const* config ) = 0;
- public: // construction
- constexpr StringRef() noexcept = default;
+ private:
+ static IMutableContext *currentContext;
+ friend IMutableContext& getCurrentMutableContext();
+ friend void cleanUpContext();
+ static void createContext();
+ };
- StringRef( char const* rawChars ) noexcept;
+ inline IMutableContext& getCurrentMutableContext()
+ {
+ if( !IMutableContext::currentContext )
+ IMutableContext::createContext();
+ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+ return *IMutableContext::currentContext;
+ }
- constexpr StringRef( char const* rawChars, size_type size ) noexcept
- : m_start( rawChars ),
- m_size( size )
- {}
+ inline IContext& getCurrentContext()
+ {
+ return getCurrentMutableContext();
+ }
- StringRef( std::string const& stdString ) noexcept
- : m_start( stdString.c_str() ),
- m_size( stdString.size() )
- {}
+ void cleanUpContext();
- explicit operator std::string() const {
- return std::string(m_start, m_size);
- }
+ class SimplePcg32;
+ SimplePcg32& sharedRng();
+}
- public: // operators
- auto operator == ( StringRef const& other ) const noexcept -> bool;
- auto operator != (StringRef const& other) const noexcept -> bool {
- return !(*this == other);
- }
+#endif // CATCH_CONTEXT_HPP_INCLUDED
- constexpr auto operator[] ( size_type index ) const noexcept -> char {
- assert(index < m_size);
- return m_start[index];
- }
- bool operator<(StringRef const& rhs) const noexcept;
+#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED
+#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED
- public: // named queries
- constexpr auto empty() const noexcept -> bool {
- return m_size == 0;
- }
- constexpr auto size() const noexcept -> size_type {
- return m_size;
- }
- // Returns the current start pointer. If the StringRef is not
- // null-terminated, throws std::domain_exception
- auto c_str() const -> char const*;
- public: // substrings and searches
- // Returns a substring of [start, start + length).
- // If start + length > size(), then the substring is [start, start + size()).
- // If start > size(), then the substring is empty.
- constexpr StringRef substr(size_type start, size_type length) const noexcept {
- if (start < m_size) {
- const auto shortened_size = m_size - start;
- return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length);
- } else {
- return StringRef();
- }
- }
+#ifndef CATCH_SECTION_INFO_HPP_INCLUDED
+#define CATCH_SECTION_INFO_HPP_INCLUDED
- // Returns the current start pointer. May not be null-terminated.
- constexpr char const* data() const noexcept {
- return m_start;
- }
- constexpr auto isNullTerminated() const noexcept -> bool {
- return m_start[m_size] == '\0';
- }
- public: // iterators
- constexpr const_iterator begin() const { return m_start; }
- constexpr const_iterator end() const { return m_start + m_size; }
+#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
+#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
+#include <type_traits>
- friend std::string& operator += (std::string& lhs, StringRef const& sr);
- friend std::ostream& operator << (std::ostream& os, StringRef const& sr);
- friend std::string operator+(StringRef lhs, StringRef rhs);
- };
+//! Replacement for std::move with better compile time performance
+#define CATCH_MOVE(...) static_cast<std::remove_reference_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__)
+//! Replacement for std::forward with better compile time performance
+#define CATCH_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
- constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
- return StringRef( rawChars, size );
- }
-} // namespace Catch
+#endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
-constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
- return Catch::StringRef( rawChars, size );
-}
-#endif // CATCH_STRINGREF_HPP_INCLUDED
-
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#ifdef CATCH_CONFIG_COUNTER
-# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
-#else
-# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
-#endif
+#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
+#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
+#include <cstddef>
#include <iosfwd>
-// We need a dummy global operator<< so we can bring it into Catch namespace later
-struct Catch_global_namespace_dummy {};
-std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
-
namespace Catch {
struct SourceLineInfo {
@@ -775,39 +770,18 @@ namespace Catch {
friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info);
};
-
-
- // Bring in operator<< from global namespace into Catch namespace
- // This is necessary because the overload of operator<< above makes
- // lookup stop at namespace Catch
- using ::operator<<;
-
- // Use this in variadic streaming macros to allow
- // >> +StreamEndStop
- // as well as
- // >> stuff +StreamEndStop
- struct StreamEndStop {
- StringRef operator+() const {
- return StringRef();
- }
-
- template<typename T>
- friend T const& operator + ( T const& value, StreamEndStop ) {
- return value;
- }
- };
}
#define CATCH_INTERNAL_LINEINFO \
::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
-#endif // CATCH_COMMON_HPP_INCLUDED
+#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
#ifndef CATCH_TOTALS_HPP_INCLUDED
#define CATCH_TOTALS_HPP_INCLUDED
-#include <cstddef>
+#include <cstdint>
namespace Catch {
@@ -815,13 +789,13 @@ namespace Catch {
Counts operator - ( Counts const& other ) const;
Counts& operator += ( Counts const& other );
- std::size_t total() const;
+ std::uint64_t total() const;
bool allPassed() const;
bool allOk() const;
- std::size_t passed = 0;
- std::size_t failed = 0;
- std::size_t failedButOk = 0;
+ std::uint64_t passed = 0;
+ std::uint64_t failed = 0;
+ std::uint64_t failedButOk = 0;
};
struct Totals {
@@ -831,7 +805,6 @@ namespace Catch {
Totals delta( Totals const& prevTotals ) const;
- int error = 0;
Counts assertions;
Counts testCases;
};
@@ -849,7 +822,7 @@ namespace Catch {
// still use the `-c` flag comfortably.
SectionInfo( SourceLineInfo const& _lineInfo, std::string _name,
const char* const = nullptr ):
- name(std::move(_name)),
+ name(CATCH_MOVE(_name)),
lineInfo(_lineInfo)
{}
@@ -871,7 +844,6 @@ namespace Catch {
#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED
#define CATCH_ASSERTION_RESULT_HPP_INCLUDED
-#include <string>
#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED
@@ -950,8 +922,8 @@ namespace Catch {
#include <iosfwd>
namespace Catch {
-
- struct ITransientExpression;
+
+ class ITransientExpression;
class LazyExpression {
friend class AssertionHandler;
@@ -978,6 +950,8 @@ namespace Catch {
#endif // CATCH_LAZY_EXPR_HPP_INCLUDED
+#include <string>
+
namespace Catch {
struct AssertionResultData
@@ -1008,7 +982,7 @@ namespace Catch {
std::string getExpressionInMacro() const;
bool hasExpandedExpression() const;
std::string getExpandedExpression() const;
- std::string getMessage() const;
+ StringRef getMessage() const;
SourceLineInfo getSourceInfo() const;
StringRef getTestMacroName() const;
@@ -1046,15 +1020,15 @@ namespace Catch {
struct AssertionReaction;
struct SourceLineInfo;
- struct ITransientExpression;
- struct IGeneratorTracker;
+ class ITransientExpression;
+ class IGeneratorTracker;
struct BenchmarkInfo;
template <typename Duration = std::chrono::duration<double, std::nano>>
struct BenchmarkStats;
- struct IResultCapture {
-
+ class IResultCapture {
+ public:
virtual ~IResultCapture();
virtual bool sectionStarted( SectionInfo const& sectionInfo,
@@ -1064,10 +1038,10 @@ namespace Catch {
virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
- virtual void benchmarkPreparing( std::string const& name ) = 0;
+ virtual void benchmarkPreparing( StringRef name ) = 0;
virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
- virtual void benchmarkFailed( std::string const& error ) = 0;
+ virtual void benchmarkFailed( StringRef error ) = 0;
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;
@@ -1083,7 +1057,7 @@ namespace Catch {
virtual void handleMessage
( AssertionInfo const& info,
ResultWas::OfType resultType,
- StringRef const& message,
+ StringRef message,
AssertionReaction& reaction ) = 0;
virtual void handleUnexpectedExceptionNotThrown
( AssertionInfo const& info,
@@ -1120,7 +1094,7 @@ namespace Catch {
namespace Catch {
struct MessageInfo {
- MessageInfo( StringRef const& _macroName,
+ MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type );
@@ -1151,11 +1125,14 @@ namespace Catch {
#include <cassert>
#include <type_traits>
+
namespace Catch {
namespace Detail {
- // reimplementation of unique_ptr for improved compilation times
- // Does not support custom deleters (and thus does not require EBO)
- // Does not support arrays
+ /**
+ * A reimplementation of `std::unique_ptr` for improved compilation performance
+ *
+ * Does not support arrays nor custom deleters.
+ */
template <typename T>
class unique_ptr {
T* m_ptr;
@@ -1204,7 +1181,11 @@ namespace Detail {
assert(m_ptr);
return *m_ptr;
}
- T* operator->() const noexcept {
+ T* operator->() noexcept {
+ assert(m_ptr);
+ return m_ptr;
+ }
+ T const* operator->() const noexcept {
assert(m_ptr);
return m_ptr;
}
@@ -1234,20 +1215,13 @@ namespace Detail {
}
};
- // Purposefully doesn't exist
- // We could also rely on compiler warning + werror for calling plain delete
- // on a T[], but this seems better.
- // Maybe add definition and a static assert?
+ //! Specialization to cause compile-time error for arrays
template <typename T>
class unique_ptr<T[]>;
template <typename T, typename... Args>
unique_ptr<T> make_unique(Args&&... args) {
- // static_cast<Args&&> does the same thing as std::forward in
- // this case, but does not require including big header (<utility>)
- // and compiles faster thanks to not requiring template instantiation
- // and overload resolution
- return unique_ptr<T>(new T(static_cast<Args&&>(args)...));
+ return unique_ptr<T>(new T(CATCH_FORWARD(args)...));
}
@@ -1257,7 +1231,6 @@ namespace Detail {
#endif // CATCH_UNIQUE_PTR_HPP_INCLUDED
-
// Adapted from donated nonius code.
#ifndef CATCH_ESTIMATE_HPP_INCLUDED
@@ -1307,6 +1280,7 @@ namespace Catch {
#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED
+#include <map>
#include <string>
#include <vector>
#include <iosfwd>
@@ -1314,36 +1288,39 @@ namespace Catch {
namespace Catch {
struct ReporterDescription;
+ struct ListenerDescription;
struct TagInfo;
struct TestCaseInfo;
class TestCaseHandle;
- struct IConfig;
+ class IConfig;
+ class IStream;
+ enum class ColourMode : std::uint8_t;
struct ReporterConfig {
- explicit ReporterConfig( IConfig const* _fullConfig );
+ ReporterConfig( IConfig const* _fullConfig,
+ Detail::unique_ptr<IStream> _stream,
+ ColourMode colourMode,
+ std::map<std::string, std::string> customOptions );
- ReporterConfig( IConfig const* _fullConfig, std::ostream& _stream );
+ ReporterConfig( ReporterConfig&& ) = default;
+ ReporterConfig& operator=( ReporterConfig&& ) = default;
+ ~ReporterConfig(); // = default
- std::ostream& stream() const;
+ Detail::unique_ptr<IStream> takeStream() &&;
IConfig const* fullConfig() const;
+ ColourMode colourMode() const;
+ std::map<std::string, std::string> const& customOptions() const;
private:
- std::ostream* m_stream;
+ Detail::unique_ptr<IStream> m_stream;
IConfig const* m_fullConfig;
+ ColourMode m_colourMode;
+ std::map<std::string, std::string> m_customOptions;
};
struct TestRunInfo {
- TestRunInfo( std::string const& _name );
- std::string name;
- };
- struct GroupInfo {
- GroupInfo( std::string const& _name,
- std::size_t _groupIndex,
- std::size_t _groupsCount );
-
- std::string name;
- std::size_t groupIndex;
- std::size_t groupsCounts;
+ constexpr TestRunInfo(StringRef _name) : name(_name) {}
+ StringRef name;
};
struct AssertionStats {
@@ -1387,17 +1364,6 @@ namespace Catch {
bool aborting;
};
- struct TestGroupStats {
- TestGroupStats( GroupInfo const& _groupInfo,
- Totals const& _totals,
- bool _aborting );
- TestGroupStats( GroupInfo const& _groupInfo );
-
- GroupInfo groupInfo;
- Totals totals;
- bool aborting;
- };
-
struct TestRunStats {
TestRunStats( TestRunInfo const& _runInfo,
Totals const& _totals,
@@ -1413,7 +1379,7 @@ namespace Catch {
std::string name;
double estimatedDuration;
int iterations;
- int samples;
+ unsigned int samples;
unsigned int resamples;
double clockResolution;
double clockCost;
@@ -1438,7 +1404,7 @@ namespace Catch {
}
return {
info,
- std::move(samples2),
+ CATCH_MOVE(samples2),
mean,
standardDeviation,
outliers,
@@ -1459,13 +1425,29 @@ namespace Catch {
bool shouldReportAllAssertions = false;
};
-
- struct IStreamingReporter {
+ /**
+ * The common base for all reporters and event listeners
+ *
+ * Implementing classes must also implement:
+ *
+ * //! User-friendly description of the reporter/listener type
+ * static std::string getDescription()
+ *
+ * Generally shouldn't be derived from by users of Catch2 directly,
+ * instead they should derive from one of the utility bases that
+ * derive from this class.
+ */
+ class IEventListener {
protected:
//! Derived classes can set up their preferences here
ReporterPreferences m_preferences;
+ //! The test run's config as filled in from CLI and defaults
+ IConfig const* m_config;
+
public:
- virtual ~IStreamingReporter() = default;
+ IEventListener( IConfig const* config ): m_config( config ) {}
+
+ virtual ~IEventListener(); // = default;
// Implementing class must also provide the following static methods:
// static std::string getDescription();
@@ -1474,51 +1456,115 @@ namespace Catch {
return m_preferences;
}
- virtual void noMatchingTestCases( std::string const& spec ) = 0;
-
- virtual void reportInvalidArguments(std::string const&) {}
+ //! Called when no test cases match provided test spec
+ virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0;
+ //! Called for all invalid test specs from the cli
+ virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0;
+ /**
+ * Called once in a testing run before tests are started
+ *
+ * Not called if tests won't be run (e.g. only listing will happen)
+ */
virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
- virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+ //! Called _once_ for each TEST_CASE, no matter how many times it is entered
virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+ //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
+ virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0;
+ //! Called when a `SECTION` is being entered. Not called for skipped sections
virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
- virtual void benchmarkPreparing( std::string const& ) {}
- virtual void benchmarkStarting( BenchmarkInfo const& ) {}
- virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
- virtual void benchmarkFailed( std::string const& ) {}
+ //! Called when user-code is being probed before the actual benchmark runs
+ virtual void benchmarkPreparing( StringRef benchmarkName ) = 0;
+ //! Called after probe but before the user-code is being benchmarked
+ virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0;
+ //! Called with the benchmark results if benchmark successfully finishes
+ virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0;
+ //! Called if running the benchmarks fails for any reason
+ virtual void benchmarkFailed( StringRef benchmarkName ) = 0;
+ //! Called before assertion success/failure is evaluated
virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
- // The return value indicates if the messages buffer should be cleared:
- virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+ //! Called after assertion was fully evaluated
+ virtual void assertionEnded( AssertionStats const& assertionStats ) = 0;
+ //! Called after a `SECTION` has finished running
virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+ //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
+ virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0;
+ //! Called _once_ for each TEST_CASE, no matter how many times it is entered
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
- virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+ /**
+ * Called once after all tests in a testing run are finished
+ *
+ * Not called if tests weren't run (e.g. only listings happened)
+ */
virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+ //! Called with test cases that are skipped due to the test run aborting
virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
- // Default empty implementation provided
- virtual void fatalErrorEncountered( StringRef name );
+ //! Called if a fatal error (signal/structured exception) occured
+ virtual void fatalErrorEncountered( StringRef error ) = 0;
//! Writes out information about provided reporters using reporter-specific format
- virtual void listReporters(std::vector<ReporterDescription> const& descriptions, IConfig const& config);
+ virtual void listReporters(std::vector<ReporterDescription> const& descriptions) = 0;
+ //! Writes out the provided listeners descriptions using reporter-specific format
+ virtual void listListeners(std::vector<ListenerDescription> const& descriptions) = 0;
//! Writes out information about provided tests using reporter-specific format
- virtual void listTests(std::vector<TestCaseHandle> const& tests, IConfig const& config);
+ virtual void listTests(std::vector<TestCaseHandle> const& tests) = 0;
//! Writes out information about the provided tags using reporter-specific format
- virtual void listTags(std::vector<TagInfo> const& tags, IConfig const& config);
-
+ virtual void listTags(std::vector<TagInfo> const& tags) = 0;
};
- using IStreamingReporterPtr = Detail::unique_ptr<IStreamingReporter>;
+ using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
} // end namespace Catch
#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED
+#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED
+#define CATCH_UNIQUE_NAME_HPP_INCLUDED
+
+
+
+
+/** \file
+ * Wrapper for the CONFIG configuration option
+ *
+ * When generating internal unique names, there are two options. Either
+ * we mix in the current line number, or mix in an incrementing number.
+ * We prefer the latter, using `__COUNTER__`, but users might want to
+ * use the former.
+ */
+
+#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED
+#define CATCH_CONFIG_COUNTER_HPP_INCLUDED
+
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
+ #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \
+ !defined( CATCH_CONFIG_NO_COUNTER ) && \
+ !defined( CATCH_CONFIG_COUNTER )
+# define CATCH_CONFIG_COUNTER
+#endif
+
+
+#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED
+
// Adapted from donated nonius code.
@@ -1570,8 +1616,8 @@ namespace Catch {
# include <atomic> // atomic_thread_fence
#endif
+
#include <type_traits>
-#include <utility>
namespace Catch {
namespace Benchmark {
@@ -1612,13 +1658,13 @@ namespace Catch {
}
template <typename Fn, typename... Args>
- inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type {
- deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...)));
+ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<!std::is_same<void, decltype(fn(args...))>::value> {
+ deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...));
}
template <typename Fn, typename... Args>
- inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type {
- std::forward<Fn>(fn) (std::forward<Args...>(args...));
+ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
+ CATCH_FORWARD(fn) (CATCH_FORWARD(args)...);
}
} // namespace Benchmark
} // namespace Catch
@@ -1633,113 +1679,17 @@ namespace Catch {
-#ifndef CATCH_ENFORCE_HPP_INCLUDED
-#define CATCH_ENFORCE_HPP_INCLUDED
-
-
-
-#ifndef CATCH_STREAM_HPP_INCLUDED
-#define CATCH_STREAM_HPP_INCLUDED
-
-
-#include <iosfwd>
-#include <cstddef>
-#include <ostream>
-
-namespace Catch {
-
- std::ostream& cout();
- std::ostream& cerr();
- std::ostream& clog();
-
- class StringRef;
-
- struct IStream {
- virtual ~IStream();
- virtual std::ostream& stream() const = 0;
- };
-
- auto makeStream( StringRef const &filename ) -> IStream const*;
-
- class ReusableStringStream : Detail::NonCopyable {
- std::size_t m_index;
- std::ostream* m_oss;
- public:
- ReusableStringStream();
- ~ReusableStringStream();
-
- //! Returns the serialized state
- std::string str() const;
- //! Sets internal state to `str`
- void str(std::string const& str);
-
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic push
-// Old versions of GCC do not understand -Wnonnull-compare
-#pragma GCC diagnostic ignored "-Wpragmas"
-// Streaming a function pointer triggers Waddress and Wnonnull-compare
-// on GCC, because it implicitly converts it to bool and then decides
-// that the check it uses (a? true : false) is tautological and cannot
-// be null...
-#pragma GCC diagnostic ignored "-Waddress"
-#pragma GCC diagnostic ignored "-Wnonnull-compare"
-#endif
-
- template<typename T>
- auto operator << ( T const& value ) -> ReusableStringStream& {
- *m_oss << value;
- return *this;
- }
-
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
- auto get() -> std::ostream& { return *m_oss; }
- };
-}
-
-#endif // CATCH_STREAM_HPP_INCLUDED
-
-#include <exception>
+#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
+#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
namespace Catch {
-#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
- template <typename Ex>
- [[noreturn]]
- void throw_exception(Ex const& e) {
- throw e;
- }
-#else // ^^ Exceptions are enabled // Exceptions are disabled vv
- [[noreturn]]
- void throw_exception(std::exception const& e);
-#endif
-
- [[noreturn]]
- void throw_logic_error(std::string const& msg);
- [[noreturn]]
- void throw_domain_error(std::string const& msg);
- [[noreturn]]
- void throw_runtime_error(std::string const& msg);
-
-} // namespace Catch;
-
-#define CATCH_MAKE_MSG(...) \
- (Catch::ReusableStringStream() << __VA_ARGS__).str()
-
-#define CATCH_INTERNAL_ERROR(...) \
- Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
-
-#define CATCH_ERROR(...) \
- Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
-#define CATCH_RUNTIME_ERROR(...) \
- Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
-
-#define CATCH_ENFORCE( condition, ... ) \
- do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+ //! Used to signal that an assertion macro failed
+ struct TestFailureException{};
+} // namespace Catch
-#endif // CATCH_ENFORCE_HPP_INCLUDED
+#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
#ifndef CATCH_META_HPP_INCLUDED
@@ -1795,22 +1745,24 @@ namespace Catch {
class TestCaseHandle;
struct TestCaseInfo;
- struct ITestCaseRegistry;
- struct IExceptionTranslatorRegistry;
- struct IExceptionTranslator;
- struct IReporterRegistry;
- struct IReporterFactory;
- struct ITagAliasRegistry;
- struct ITestInvoker;
- struct IMutableEnumValuesRegistry;
+ class ITestCaseRegistry;
+ class IExceptionTranslatorRegistry;
+ class IExceptionTranslator;
+ class IReporterRegistry;
+ class IReporterFactory;
+ class ITagAliasRegistry;
+ class ITestInvoker;
+ class IMutableEnumValuesRegistry;
struct SourceLineInfo;
class StartupExceptionRegistry;
+ class EventListenerFactory;
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
- struct IRegistryHub {
- virtual ~IRegistryHub();
+ class IRegistryHub {
+ public:
+ virtual ~IRegistryHub(); // = default
virtual IReporterRegistry const& getReporterRegistry() const = 0;
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
@@ -1821,12 +1773,13 @@ namespace Catch {
virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
};
- struct IMutableRegistryHub {
- virtual ~IMutableRegistryHub();
+ class IMutableRegistryHub {
+ public:
+ virtual ~IMutableRegistryHub(); // = default
virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0;
- virtual void registerListener( IReporterFactoryPtr factory ) = 0;
+ virtual void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) = 0;
virtual void registerTest(Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker) = 0;
- virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ virtual void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) = 0;
virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
virtual void registerStartupException() noexcept = 0;
virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
@@ -1842,7 +1795,6 @@ namespace Catch {
#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED
#include <type_traits>
-#include <utility>
namespace Catch {
namespace Benchmark {
@@ -1859,14 +1811,14 @@ namespace Catch {
struct CompleteInvoker {
template <typename Fun, typename... Args>
static Result invoke(Fun&& fun, Args&&... args) {
- return std::forward<Fun>(fun)(std::forward<Args>(args)...);
+ return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
}
};
template <>
struct CompleteInvoker<void> {
template <typename Fun, typename... Args>
static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
- std::forward<Fun>(fun)(std::forward<Args>(args)...);
+ CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
return {};
}
};
@@ -1874,20 +1826,14 @@ namespace Catch {
// invoke and not return void :(
template <typename Fun, typename... Args>
CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
- return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
+ return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...);
}
- extern const std::string benchmarkErrorMsg;
} // namespace Detail
template <typename Fun>
Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
- CATCH_TRY{
- return Detail::complete_invoke(std::forward<Fun>(fun));
- } CATCH_CATCH_ALL{
- getResultCapture().benchmarkFailed(translateActiveException());
- CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg);
- }
+ return Detail::complete_invoke(CATCH_FORWARD(fun));
}
} // namespace Benchmark
} // namespace Catch
@@ -1921,7 +1867,7 @@ namespace Catch {
struct Chronometer {
public:
template <typename Fun>
- void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
+ void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable<Fun(int)>()); }
int runs() const { return repeats; }
@@ -1996,18 +1942,14 @@ namespace Catch {
#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
-#include <cassert>
#include <type_traits>
-#include <utility>
namespace Catch {
namespace Benchmark {
namespace Detail {
- template <typename T>
- using Decay = typename std::decay<T>::type;
template <typename T, typename U>
struct is_related
- : std::is_same<Decay<T>, Decay<U>> {};
+ : std::is_same<std::decay_t<T>, std::decay_t<U>> {};
/// We need to reinvent std::function because every piece of code that might add overhead
/// in a measurement context needs to have consistent performance characteristics so that we
@@ -2020,7 +1962,7 @@ namespace Catch {
private:
struct callable {
virtual void call(Chronometer meter) const = 0;
- virtual callable* clone() const = 0;
+ virtual Catch::Detail::unique_ptr<callable> clone() const = 0;
virtual ~callable(); // = default;
callable() = default;
@@ -2029,10 +1971,12 @@ namespace Catch {
};
template <typename Fun>
struct model : public callable {
- model(Fun&& fun_) : fun(std::move(fun_)) {}
+ model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
model(Fun const& fun_) : fun(fun_) {}
- model<Fun>* clone() const override { return new model<Fun>(*this); }
+ 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)>());
@@ -2057,24 +2001,24 @@ namespace Catch {
: f(new model<do_nothing>{ {} }) {}
template <typename Fun,
- typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
+ std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0>
BenchmarkFunction(Fun&& fun)
- : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
+ : f(new model<std::decay_t<Fun>>(CATCH_FORWARD(fun))) {}
BenchmarkFunction( BenchmarkFunction&& that ) noexcept:
- f( std::move( that.f ) ) {}
+ f( CATCH_MOVE( that.f ) ) {}
BenchmarkFunction(BenchmarkFunction const& that)
: f(that.f->clone()) {}
BenchmarkFunction&
operator=( BenchmarkFunction&& that ) noexcept {
- f = std::move( that.f );
+ f = CATCH_MOVE( that.f );
return *this;
}
BenchmarkFunction& operator=(BenchmarkFunction const& that) {
- f.reset(that.f->clone());
+ f = that.f->clone();
return *this;
}
@@ -2096,7 +2040,6 @@ namespace Catch {
#define CATCH_REPEAT_HPP_INCLUDED
#include <type_traits>
-#include <utility>
namespace Catch {
namespace Benchmark {
@@ -2111,8 +2054,8 @@ namespace Catch {
Fun fun;
};
template <typename Fun>
- repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
- return { std::forward<Fun>(fun) };
+ repeater<std::decay_t<Fun>> repeat(Fun&& fun) {
+ return { CATCH_FORWARD(fun) };
}
} // namespace Detail
} // namespace Benchmark
@@ -2158,18 +2101,16 @@ namespace Catch {
#endif // CATCH_TIMING_HPP_INCLUDED
-#include <utility>
-
namespace Catch {
namespace Benchmark {
namespace Detail {
template <typename Clock, typename Fun, typename... Args>
TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) {
auto start = Clock::now();
- auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...);
+ auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...);
auto end = Clock::now();
auto delta = end - start;
- return { delta, std::forward<decltype(r)>(r), 1 };
+ return { delta, CATCH_FORWARD(r), 1 };
}
} // namespace Detail
} // namespace Benchmark
@@ -2177,7 +2118,6 @@ namespace Catch {
#endif // CATCH_MEASURE_HPP_INCLUDED
-#include <utility>
#include <type_traits>
namespace Catch {
@@ -2192,24 +2132,27 @@ namespace Catch {
Detail::ChronometerModel<Clock> meter;
auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
- return { meter.elapsed(), std::move(result), iters };
+ return { meter.elapsed(), CATCH_MOVE(result), iters };
}
template <typename Clock, typename Fun>
- using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
+ using run_for_at_least_argument_t = std::conditional_t<is_callable<Fun(Chronometer)>::value, Chronometer, int>;
[[noreturn]]
void throw_optimized_away_error();
template <typename Clock, typename Fun>
- TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) {
- auto iters = seed;
+ TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>>
+ run_for_at_least(ClockDuration<Clock> how_long,
+ const int initial_iterations,
+ Fun&& fun) {
+ auto iters = initial_iterations;
while (iters < (1 << 30)) {
auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
if (Timing.elapsed >= how_long) {
- return { Timing.elapsed, std::move(Timing.result), iters };
+ return { Timing.elapsed, CATCH_MOVE(Timing.result), iters };
}
iters *= 2;
}
@@ -2222,6 +2165,7 @@ namespace Catch {
#endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
#include <algorithm>
+#include <iterator>
namespace Catch {
namespace Benchmark {
@@ -2279,13 +2223,16 @@ namespace Catch {
#include <numeric>
#include <tuple>
#include <cmath>
-#include <utility>
namespace Catch {
namespace Benchmark {
namespace Detail {
using sample = std::vector<double>;
+ // Used when we know we want == comparison of two doubles
+ // to centralize warning suppression
+ bool directCompare( double lhs, double rhs );
+
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
template <typename Iterator>
@@ -2316,12 +2263,12 @@ namespace Catch {
double mean(Iterator first, Iterator last) {
auto count = last - first;
double sum = std::accumulate(first, last, 0.);
- return sum / count;
+ return sum / static_cast<double>(count);
}
template <typename Estimator, typename Iterator>
sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
- auto n = last - first;
+ auto n = static_cast<size_t>(last - first);
auto second = first;
++second;
sample results;
@@ -2362,23 +2309,26 @@ namespace Catch {
});
double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
- int n = static_cast<int>(resample.size());
- double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n;
+ long n = static_cast<long>(resample.size());
+ double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast<double>(n);
// degenerate case with uniform samples
- if (prob_n == 0) return { point, point, point, confidence_level };
+ if ( directCompare( prob_n, 0. ) ) {
+ return { point, point, point, confidence_level };
+ }
double bias = normal_quantile(prob_n);
double z1 = normal_quantile((1. - confidence_level) / 2.);
- auto cumn = [n](double x) -> int {
- return std::lround(normal_cdf(x) * n); };
+ auto cumn = [n]( double x ) -> long {
+ return std::lround( normal_cdf( x ) * n );
+ };
auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
double b1 = bias + z1;
double b2 = bias - z1;
double a1 = a(b1);
double a2 = a(b2);
- auto lo = std::max(cumn(a1), 0);
- auto hi = std::min(cumn(a2), n - 1);
+ auto lo = static_cast<size_t>((std::max)(cumn(a1), 0l));
+ auto hi = static_cast<size_t>((std::min)(cumn(a2), n - 1));
return { point, resample[lo], resample[hi], confidence_level };
}
@@ -2391,7 +2341,7 @@ namespace Catch {
double outlier_variance;
};
- bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
+ bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
@@ -2409,11 +2359,11 @@ namespace Catch {
template <typename Clock>
std::vector<double> resolution(int k) {
std::vector<TimePoint<Clock>> times;
- times.reserve(k + 1);
+ times.reserve(static_cast<size_t>(k + 1));
std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
std::vector<double> deltas;
- deltas.reserve(k);
+ deltas.reserve(static_cast<size_t>(k));
std::transform(std::next(times.begin()), times.end(), times.begin(),
std::back_inserter(deltas),
[](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
@@ -2447,7 +2397,9 @@ namespace Catch {
}
template <typename Clock>
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
- auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit));
+ auto time_limit = (std::min)(
+ resolution * clock_cost_estimation_tick_limit,
+ FloatDuration<Clock>(clock_cost_estimation_time_limit));
auto time_clock = [](int k) {
return Detail::measure<Clock>([k] {
for (int i = 0; i < k; ++i) {
@@ -2461,7 +2413,7 @@ namespace Catch {
auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
std::vector<double> times;
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
- times.reserve(nsamples);
+ times.reserve(static_cast<size_t>(nsamples));
std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
});
@@ -2473,7 +2425,14 @@ namespace Catch {
template <typename Clock>
Environment<FloatDuration<Clock>> measure_environment() {
- static Environment<FloatDuration<Clock>>* env = nullptr;
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+ static Catch::Detail::unique_ptr<Environment<FloatDuration<Clock>>> env;
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
if (env) {
return *env;
}
@@ -2482,7 +2441,7 @@ namespace Catch {
auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
- env = new Environment<FloatDuration<Clock>>{ resolution, cost };
+ env = Catch::Detail::make_unique<Environment<FloatDuration<Clock>>>( Environment<FloatDuration<Clock>>{resolution, cost} );
return *env;
}
} // namespace Detail
@@ -2507,7 +2466,6 @@ namespace Catch {
#include <algorithm>
#include <vector>
-#include <string>
#include <iterator>
namespace Catch {
@@ -2526,7 +2484,7 @@ namespace Catch {
samples2.reserve(samples.size());
std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
return {
- std::move(samples2),
+ CATCH_MOVE(samples2),
mean,
standard_deviation,
outliers,
@@ -2550,7 +2508,7 @@ namespace Catch {
SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
if (!cfg.benchmarkNoAnalysis()) {
std::vector<double> samples;
- samples.reserve(last - first);
+ samples.reserve(static_cast<size_t>(last - first));
std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
@@ -2568,15 +2526,15 @@ namespace Catch {
samples2.reserve(samples.size());
std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
return {
- std::move(samples2),
+ CATCH_MOVE(samples2),
wrap_estimate(analysis.mean),
wrap_estimate(analysis.standard_deviation),
outliers,
analysis.outlier_variance,
};
} else {
- std::vector<Duration> samples;
- samples.reserve(last - first);
+ std::vector<Duration> samples;
+ samples.reserve(static_cast<size_t>(last - first));
Duration mean = Duration(0);
int i = 0;
@@ -2587,7 +2545,7 @@ namespace Catch {
mean /= i;
return {
- std::move(samples),
+ CATCH_MOVE(samples),
Estimate<Duration>{mean, mean, mean, 0.0},
Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
OutlierClassification{},
@@ -2611,11 +2569,11 @@ namespace Catch {
namespace Benchmark {
struct Benchmark {
Benchmark(std::string&& benchmarkName)
- : name(std::move(benchmarkName)) {}
+ : name(CATCH_MOVE(benchmarkName)) {}
template <class FUN>
Benchmark(std::string&& benchmarkName , FUN &&func)
- : fun(std::move(func)), name(std::move(benchmarkName)) {}
+ : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
template <typename Clock>
ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
@@ -2657,19 +2615,24 @@ namespace Catch {
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats);
-
+ } CATCH_CATCH_ANON (TestFailureException) {
+ getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
} CATCH_CATCH_ALL{
- if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow.
- std::rethrow_exception(std::current_exception());
+ getResultCapture().benchmarkFailed(translateActiveException());
+ // We let the exception go further up so that the
+ // test case is marked as failed.
+ std::rethrow_exception(std::current_exception());
}
}
// sets lambda to be used in fun *and* executes benchmark!
- template <typename Fun,
- typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0>
+ template <typename Fun, std::enable_if_t<!Detail::is_related<Fun, Benchmark>::value, int> = 0>
Benchmark & operator=(Fun func) {
- fun = Detail::BenchmarkFunction(func);
- run();
+ auto const* cfg = getCurrentContext().getConfig();
+ if (!cfg->skipBenchmarks()) {
+ fun = Detail::BenchmarkFunction(func);
+ run();
+ }
return *this;
}
@@ -2698,16 +2661,16 @@ namespace Catch {
#if defined(CATCH_CONFIG_PREFIX_ALL)
#define CATCH_BENCHMARK(...) \
- INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
#define CATCH_BENCHMARK_ADVANCED(name) \
- INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)
+ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name)
#else
#define BENCHMARK(...) \
- INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
#define BENCHMARK_ADVANCED(name) \
- INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)
+ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name)
#endif
@@ -2719,6 +2682,7 @@ namespace Catch {
#ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED
#define CATCH_CONSTRUCTOR_HPP_INCLUDED
+
#include <type_traits>
namespace Catch {
@@ -2727,9 +2691,7 @@ namespace Catch {
template <typename T, bool Destruct>
struct ObjectStorage
{
- using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
-
- ObjectStorage() : data() {}
+ ObjectStorage() = default;
ObjectStorage(const ObjectStorage& other)
{
@@ -2738,7 +2700,7 @@ namespace Catch {
ObjectStorage(ObjectStorage&& other)
{
- new(&data) T(std::move(other.stored_object()));
+ new(data) T(CATCH_MOVE(other.stored_object()));
}
~ObjectStorage() { destruct_on_exit<T>(); }
@@ -2746,11 +2708,11 @@ namespace Catch {
template <typename... Args>
void construct(Args&&... args)
{
- new (&data) T(std::forward<Args>(args)...);
+ new (data) T(CATCH_FORWARD(args)...);
}
template <bool AllowManualDestruction = !Destruct>
- typename std::enable_if<AllowManualDestruction>::type destruct()
+ std::enable_if_t<AllowManualDestruction> destruct()
{
stored_object().~T();
}
@@ -2758,21 +2720,21 @@ namespace Catch {
private:
// If this is a constructor benchmark, destruct the underlying object
template <typename U>
- void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
+ void destruct_on_exit(std::enable_if_t<Destruct, U>* = nullptr) { destruct<true>(); }
// Otherwise, don't
template <typename U>
- void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
+ void destruct_on_exit(std::enable_if_t<!Destruct, U>* = nullptr) { }
T& stored_object() {
- return *static_cast<T*>(static_cast<void*>(&data));
+ return *static_cast<T*>(static_cast<void*>(data));
}
T const& stored_object() const {
- return *static_cast<T*>(static_cast<void*>(&data));
+ return *static_cast<T*>(static_cast<void*>(data));
}
- TStorage data;
+ alignas( T ) unsigned char data[sizeof( T )]{};
};
} // namespace Detail
@@ -2802,6 +2764,107 @@ namespace Catch {
#include <cstddef>
#include <type_traits>
#include <string>
+#include <string.h>
+
+
+
+
+/** \file
+ * Wrapper for the WCHAR configuration option
+ *
+ * We want to support platforms that do not provide `wchar_t`, so we
+ * sometimes have to disable providing wchar_t overloads through Catch2,
+ * e.g. the StringMaker specialization for `std::wstring`.
+ */
+
+#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED
+#define CATCH_CONFIG_WCHAR_HPP_INCLUDED
+
+// We assume that WCHAR should be enabled by default, and only disabled
+// for a shortlist (so far only DJGPP) of compilers.
+
+#if defined(__DJGPP__)
+# define CATCH_INTERNAL_CONFIG_NO_WCHAR
+#endif // __DJGPP__
+
+#if !defined( CATCH_INTERNAL_CONFIG_NO_WCHAR ) && \
+ !defined( CATCH_CONFIG_NO_WCHAR ) && \
+ !defined( CATCH_CONFIG_WCHAR )
+# define CATCH_CONFIG_WCHAR
+#endif
+
+#endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED
+
+
+#ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
+#define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
+
+
+#include <iosfwd>
+#include <cstddef>
+#include <ostream>
+#include <string>
+
+namespace Catch {
+
+ class ReusableStringStream : Detail::NonCopyable {
+ std::size_t m_index;
+ std::ostream* m_oss;
+ public:
+ ReusableStringStream();
+ ~ReusableStringStream();
+
+ //! Returns the serialized state
+ std::string str() const;
+ //! Sets internal state to `str`
+ void str(std::string const& str);
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+// Old versions of GCC do not understand -Wnonnull-compare
+#pragma GCC diagnostic ignored "-Wpragmas"
+// Streaming a function pointer triggers Waddress and Wnonnull-compare
+// on GCC, because it implicitly converts it to bool and then decides
+// that the check it uses (a? true : false) is tautological and cannot
+// be null...
+#pragma GCC diagnostic ignored "-Waddress"
+#pragma GCC diagnostic ignored "-Wnonnull-compare"
+#endif
+
+ template<typename T>
+ auto operator << ( T const& value ) -> ReusableStringStream& {
+ *m_oss << value;
+ return *this;
+ }
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+ auto get() -> std::ostream& { return *m_oss; }
+ };
+}
+
+#endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
+
+
+#ifndef CATCH_VOID_TYPE_HPP_INCLUDED
+#define CATCH_VOID_TYPE_HPP_INCLUDED
+
+
+namespace Catch {
+ namespace Detail {
+
+ template <typename...>
+ struct make_void { using type = void; };
+
+ template <typename... Ts>
+ using void_t = typename make_void<Ts...>::type;
+
+ } // namespace Detail
+} // namespace Catch
+
+
+#endif // CATCH_VOID_TYPE_HPP_INCLUDED
#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
@@ -2823,8 +2886,9 @@ namespace Catch {
};
} // namespace Detail
- struct IMutableEnumValuesRegistry {
- virtual ~IMutableEnumValuesRegistry();
+ class IMutableEnumValuesRegistry {
+ public:
+ virtual ~IMutableEnumValuesRegistry(); // = default;
virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
@@ -2852,10 +2916,26 @@ namespace Catch {
#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
#endif
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy{};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
namespace Catch {
+ // Bring in global namespace operator<< for ADL lookup in
+ // `IsStreamInsertable` below.
+ using ::operator<<;
+
namespace Detail {
- extern const std::string unprintableString;
+
+ constexpr StringRef unprintableString = "{?}"_sr;
+
+ //! Encases `string in quotes, and optionally escapes invisibles
+ std::string convertIntoString( StringRef string, bool escapeInvisibles );
+
+ //! Encases `string` in quotes, and escapes invisibles if user requested
+ //! it via CLI
+ std::string convertIntoString( StringRef string );
std::string rawMemoryToString( const void *object, std::size_t size );
@@ -2884,7 +2964,7 @@ namespace Catch {
std::enable_if_t<
!std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
std::string> convertUnstreamable( T const& ) {
- return Detail::unprintableString;
+ return std::string(Detail::unprintableString);
}
template<typename T>
std::enable_if_t<
@@ -2988,7 +3068,7 @@ namespace Catch {
static std::string convert(char * str);
};
-#ifdef CATCH_CONFIG_WCHAR
+#if defined(CATCH_CONFIG_WCHAR)
template<>
struct StringMaker<std::wstring> {
static std::string convert(const std::wstring& wstr);
@@ -3009,26 +3089,33 @@ namespace Catch {
struct StringMaker<wchar_t *> {
static std::string convert(wchar_t * str);
};
-#endif
+#endif // CATCH_CONFIG_WCHAR
- // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
- // while keeping string semantics?
- template<int SZ>
+ template<size_t SZ>
struct StringMaker<char[SZ]> {
static std::string convert(char const* str) {
- return ::Catch::Detail::stringify(std::string{ str });
+ // Note that `strnlen` is not actually part of standard C++,
+ // but both POSIX and Windows cstdlib provide it.
+ return Detail::convertIntoString(
+ StringRef( str, strnlen( str, SZ ) ) );
}
};
- template<int SZ>
+ template<size_t SZ>
struct StringMaker<signed char[SZ]> {
static std::string convert(signed char const* str) {
- return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
+ // See the plain `char const*` overload
+ auto reinterpreted = reinterpret_cast<char const*>(str);
+ return Detail::convertIntoString(
+ StringRef(reinterpreted, strnlen(reinterpreted, SZ)));
}
};
- template<int SZ>
+ template<size_t SZ>
struct StringMaker<unsigned char[SZ]> {
static std::string convert(unsigned char const* str) {
- return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
+ // See the plain `char const*` overload
+ auto reinterpreted = reinterpret_cast<char const*>(str);
+ return Detail::convertIntoString(
+ StringRef(reinterpreted, strnlen(reinterpreted, SZ)));
}
};
@@ -3189,13 +3276,11 @@ namespace Catch {
template<typename T>
struct StringMaker<std::optional<T> > {
static std::string convert(const std::optional<T>& optional) {
- ReusableStringStream rss;
if (optional.has_value()) {
- rss << ::Catch::Detail::stringify(*optional);
+ return ::Catch::Detail::stringify(*optional);
} else {
- rss << "{ }";
+ return "{ }";
}
- return rss.str();
}
};
}
@@ -3276,24 +3361,16 @@ namespace Catch {
using std::begin;
using std::end;
- namespace detail {
- template <typename...>
- struct void_type {
- using type = void;
- };
-
+ namespace Detail {
template <typename T, typename = void>
- struct is_range_impl : std::false_type {
- };
+ struct is_range_impl : std::false_type {};
template <typename T>
- struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type {
- };
- } // namespace detail
+ struct is_range_impl<T, void_t<decltype(begin(std::declval<T>()))>> : std::true_type {};
+ } // namespace Detail
template <typename T>
- struct is_range : detail::is_range_impl<T> {
- };
+ struct is_range : Detail::is_range_impl<T> {};
#if defined(_MANAGED) // Managed types are never ranges
template <typename T>
@@ -3331,7 +3408,7 @@ namespace Catch {
}
};
- template <typename T, int SZ>
+ template <typename T, size_t SZ>
struct StringMaker<T[SZ]> {
static std::string convert(T const(&arr)[SZ]) {
return rangeToString(arr);
@@ -3361,27 +3438,27 @@ struct ratio_string {
template <>
struct ratio_string<std::atto> {
- static std::string symbol() { return "a"; }
+ static char symbol() { return 'a'; }
};
template <>
struct ratio_string<std::femto> {
- static std::string symbol() { return "f"; }
+ static char symbol() { return 'f'; }
};
template <>
struct ratio_string<std::pico> {
- static std::string symbol() { return "p"; }
+ static char symbol() { return 'p'; }
};
template <>
struct ratio_string<std::nano> {
- static std::string symbol() { return "n"; }
+ static char symbol() { return 'n'; }
};
template <>
struct ratio_string<std::micro> {
- static std::string symbol() { return "u"; }
+ static char symbol() { return 'u'; }
};
template <>
struct ratio_string<std::milli> {
- static std::string symbol() { return "m"; }
+ static char symbol() { return 'm'; }
};
////////////
@@ -3450,7 +3527,7 @@ struct ratio_string<std::milli> {
#else
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
#endif
- return std::string(timeStamp);
+ return std::string(timeStamp, timeStampSize - 1);
}
};
}
@@ -3494,7 +3571,7 @@ namespace Catch {
Approx operator-() const;
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- Approx operator()( T const& value ) {
+ Approx operator()( T const& value ) const {
Approx approx( static_cast<double>(value) );
approx.m_epsilon = m_epsilon;
approx.m_margin = m_margin;
@@ -3550,14 +3627,14 @@ namespace Catch {
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
Approx& epsilon( T const& newEpsilon ) {
- double epsilonAsDouble = static_cast<double>(newEpsilon);
+ const auto epsilonAsDouble = static_cast<double>(newEpsilon);
setEpsilon(epsilonAsDouble);
return *this;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
Approx& margin( T const& newMargin ) {
- double marginAsDouble = static_cast<double>(newMargin);
+ const auto marginAsDouble = static_cast<double>(newMargin);
setMargin(marginAsDouble);
return *this;
}
@@ -3578,8 +3655,8 @@ namespace Catch {
};
namespace literals {
- Approx operator "" _a(long double val);
- Approx operator "" _a(unsigned long long val);
+ Approx operator ""_a(long double val);
+ Approx operator ""_a(unsigned long long val);
} // end namespace literals
template<>
@@ -3638,8 +3715,7 @@ namespace Catch
public:
WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity );
- virtual ~WildcardPattern() = default;
- virtual bool matches( std::string const& str ) const;
+ bool matches( std::string const& str ) const;
private:
std::string normaliseString( std::string const& str ) const;
@@ -3656,7 +3732,7 @@ namespace Catch
namespace Catch {
- struct IConfig;
+ class IConfig;
struct TestCaseInfo;
class TestCaseHandle;
@@ -3707,11 +3783,11 @@ namespace Catch {
bool hasFilters() const;
bool matches( TestCaseInfo const& testCase ) const;
Matches matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const;
- const vectorStrings & getInvalidArgs() const;
+ const vectorStrings & getInvalidSpecs() const;
private:
std::vector<Filter> m_filters;
- std::vector<std::string> m_invalidArgs;
+ std::vector<std::string> m_invalidSpecs;
friend class TestSpecParser;
};
}
@@ -3722,17 +3798,366 @@ namespace Catch {
#endif // CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifndef CATCH_OPTIONAL_HPP_INCLUDED
+#define CATCH_OPTIONAL_HPP_INCLUDED
+
+#include <cassert>
+
+namespace Catch {
+
+ // An optional type
+ template<typename T>
+ class Optional {
+ public:
+ Optional() : nullableValue( nullptr ) {}
+ Optional( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Optional( Optional const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
+ {}
+
+ ~Optional() {
+ reset();
+ }
+
+ Optional& operator= ( Optional const& _other ) {
+ if( &_other != this ) {
+ reset();
+ if( _other )
+ nullableValue = new( storage ) T( *_other );
+ }
+ return *this;
+ }
+ Optional& operator = ( T const& _value ) {
+ reset();
+ nullableValue = new( storage ) T( _value );
+ return *this;
+ }
+
+ void reset() {
+ if( nullableValue )
+ nullableValue->~T();
+ nullableValue = nullptr;
+ }
+
+ T& operator*() {
+ assert(nullableValue);
+ return *nullableValue;
+ }
+ T const& operator*() const {
+ assert(nullableValue);
+ return *nullableValue;
+ }
+ T* operator->() {
+ assert(nullableValue);
+ return nullableValue;
+ }
+ const T* operator->() const {
+ assert(nullableValue);
+ return nullableValue;
+ }
+
+ T valueOr( T const& defaultValue ) const {
+ return nullableValue ? *nullableValue : defaultValue;
+ }
+
+ bool some() const { return nullableValue != nullptr; }
+ bool none() const { return nullableValue == nullptr; }
+
+ bool operator !() const { return nullableValue == nullptr; }
+ explicit operator bool() const {
+ return some();
+ }
+
+ friend bool operator==(Optional const& a, Optional const& b) {
+ if (a.none() && b.none()) {
+ return true;
+ } else if (a.some() && b.some()) {
+ return *a == *b;
+ } else {
+ return false;
+ }
+ }
+ friend bool operator!=(Optional const& a, Optional const& b) {
+ return !( a == b );
+ }
+
+ private:
+ T *nullableValue;
+ alignas(alignof(T)) char storage[sizeof(T)];
+ };
+
+} // end namespace Catch
+
+#endif // CATCH_OPTIONAL_HPP_INCLUDED
+
+
+#ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
+#define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
+
+#include <cstdint>
+
+namespace Catch {
+
+ enum class GenerateFrom {
+ Time,
+ RandomDevice,
+ //! Currently equivalent to RandomDevice, but can change at any point
+ Default
+ };
+
+ std::uint32_t generateRandomSeed(GenerateFrom from);
+
+} // end namespace Catch
+
+#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
+#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
+
+
+
+#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+
+#include <iosfwd>
+#include <cstdint>
+
+namespace Catch {
+
+ enum class ColourMode : std::uint8_t;
+ class IStream;
+
+ struct Colour {
+ enum Code {
+ None = 0,
+
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White,
+ BrightYellow = Bright | Yellow,
+
+ // By intention
+ FileName = LightGrey,
+ Warning = BrightYellow,
+ ResultError = BrightRed,
+ ResultSuccess = BrightGreen,
+ ResultExpectedFailure = Warning,
+
+ Error = BrightRed,
+ Success = Green,
+
+ OriginalExpression = Cyan,
+ ReconstructedExpression = BrightYellow,
+
+ SecondaryText = LightGrey,
+ Headers = White
+ };
+ };
+
+ class ColourImpl {
+ protected:
+ //! The associated stream of this ColourImpl instance
+ IStream* m_stream;
+ public:
+ ColourImpl( IStream* stream ): m_stream( stream ) {}
+
+ //! RAII wrapper around writing specific colour of text using specific
+ //! colour impl into a stream.
+ class ColourGuard {
+ ColourImpl const* m_colourImpl;
+ Colour::Code m_code;
+ bool m_engaged = false;
+
+ public:
+ //! Does **not** engage the guard/start the colour
+ ColourGuard( Colour::Code code,
+ ColourImpl const* colour );
+
+ ColourGuard( ColourGuard const& rhs ) = delete;
+ ColourGuard& operator=( ColourGuard const& rhs ) = delete;
+
+ ColourGuard( ColourGuard&& rhs ) noexcept;
+ ColourGuard& operator=( ColourGuard&& rhs ) noexcept;
+
+ //! Removes colour _if_ the guard was engaged
+ ~ColourGuard();
+
+ /**
+ * Explicitly engages colour for given stream.
+ *
+ * The API based on operator<< should be preferred.
+ */
+ ColourGuard& engage( std::ostream& stream ) &;
+ /**
+ * Explicitly engages colour for given stream.
+ *
+ * The API based on operator<< should be preferred.
+ */
+ ColourGuard&& engage( std::ostream& stream ) &&;
+
+ private:
+ //! Engages the guard and starts using colour
+ friend std::ostream& operator<<( std::ostream& lhs,
+ ColourGuard& guard ) {
+ guard.engageImpl( lhs );
+ return lhs;
+ }
+ //! Engages the guard and starts using colour
+ friend std::ostream& operator<<( std::ostream& lhs,
+ ColourGuard&& guard) {
+ guard.engageImpl( lhs );
+ return lhs;
+ }
+
+ void engageImpl( std::ostream& stream );
+
+ };
+
+ virtual ~ColourImpl(); // = default
+ /**
+ * Creates a guard object for given colour and this colour impl
+ *
+ * **Important:**
+ * the guard starts disengaged, and has to be engaged explicitly.
+ */
+ ColourGuard guardColour( Colour::Code colourCode );
+
+ private:
+ virtual void use( Colour::Code colourCode ) const = 0;
+ };
+
+ //! Provides ColourImpl based on global config and target compilation platform
+ Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection,
+ IStream* stream );
+
+ //! Checks if specific colour impl has been compiled into the binary
+ bool isColourImplAvailable( ColourMode colourSelection );
+
+} // end namespace Catch
+
+#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+#include <map>
+#include <string>
#include <vector>
+
+namespace Catch {
+
+ enum class ColourMode : std::uint8_t;
+
+ namespace Detail {
+ //! Splits the reporter spec into reporter name and kv-pair options
+ std::vector<std::string> splitReporterSpec( StringRef reporterSpec );
+
+ Optional<ColourMode> stringToColourMode( StringRef colourMode );
+ }
+
+ /**
+ * Structured reporter spec that a reporter can be created from
+ *
+ * Parsing has been validated, but semantics have not. This means e.g.
+ * that the colour mode is known to Catch2, but it might not be
+ * compiled into the binary, and the output filename might not be
+ * openable.
+ */
+ class ReporterSpec {
+ std::string m_name;
+ Optional<std::string> m_outputFileName;
+ Optional<ColourMode> m_colourMode;
+ std::map<std::string, std::string> m_customOptions;
+
+ friend bool operator==( ReporterSpec const& lhs,
+ ReporterSpec const& rhs );
+ friend bool operator!=( ReporterSpec const& lhs,
+ ReporterSpec const& rhs ) {
+ return !( lhs == rhs );
+ }
+
+ public:
+ ReporterSpec(
+ std::string name,
+ Optional<std::string> outputFileName,
+ Optional<ColourMode> colourMode,
+ std::map<std::string, std::string> customOptions );
+
+ std::string const& name() const { return m_name; }
+
+ Optional<std::string> const& outputFile() const {
+ return m_outputFileName;
+ }
+
+ Optional<ColourMode> const& colourMode() const { return m_colourMode; }
+
+ std::map<std::string, std::string> const& customOptions() const {
+ return m_customOptions;
+ }
+ };
+
+ /**
+ * Parses provided reporter spec string into
+ *
+ * Returns empty optional on errors, e.g.
+ * * field that is not first and not a key+value pair
+ * * duplicated keys in kv pair
+ * * unknown catch reporter option
+ * * empty key/value in an custom kv pair
+ * * ...
+ */
+ Optional<ReporterSpec> parseReporterSpec( StringRef reporterSpec );
+
+}
+
+#endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
+
+#include <chrono>
+#include <map>
#include <string>
+#include <vector>
namespace Catch {
- struct IStream;
+ class IStream;
+
+ /**
+ * `ReporterSpec` but with the defaults filled in.
+ *
+ * Like `ReporterSpec`, the semantics are unchecked.
+ */
+ struct ProcessedReporterSpec {
+ std::string name;
+ std::string outputFilename;
+ ColourMode colourMode;
+ std::map<std::string, std::string> customOptions;
+ friend bool operator==( ProcessedReporterSpec const& lhs,
+ ProcessedReporterSpec const& rhs );
+ friend bool operator!=( ProcessedReporterSpec const& lhs,
+ ProcessedReporterSpec const& rhs ) {
+ return !( lhs == rhs );
+ }
+ };
struct ConfigData {
+
bool listTests = false;
bool listTags = false;
bool listReporters = false;
+ bool listListeners = false;
bool showSuccessfulTests = false;
bool shouldDebugBreak = false;
@@ -3741,10 +4166,15 @@ namespace Catch {
bool showInvisibles = false;
bool filenamesAsTags = false;
bool libIdentify = false;
+ bool allowZeroTests = false;
int abortAfter = -1;
- unsigned int rngSeed = 0;
+ uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default);
+
+ unsigned int shardCount = 1;
+ unsigned int shardIndex = 0;
+ bool skipBenchmarks = false;
bool benchmarkNoAnalysis = false;
unsigned int benchmarkSamples = 100;
double benchmarkConfidenceInterval = 0.95;
@@ -3756,17 +4186,13 @@ namespace Catch {
ShowDurations showDurations = ShowDurations::DefaultForReporter;
double minDuration = -1;
TestRunOrder runOrder = TestRunOrder::Declared;
- UseColour useColour = UseColour::Auto;
+ ColourMode defaultColourMode = ColourMode::PlatformDefault;
WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
- std::string outputFilename;
+ std::string defaultOutputFilename;
std::string name;
std::string processName;
-#ifndef CATCH_CONFIG_DEFAULT_REPORTER
-#define CATCH_CONFIG_DEFAULT_REPORTER "console"
-#endif
- std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER;
-#undef CATCH_CONFIG_DEFAULT_REPORTER
+ std::vector<ReporterSpec> reporterSpecifications;
std::vector<std::string> testsOrTags;
std::vector<std::string> sectionsToRun;
@@ -3780,14 +4206,14 @@ namespace Catch {
Config( ConfigData const& data );
~Config() override; // = default in the cpp file
- std::string const& getFilename() const;
-
bool listTests() const;
bool listTags() const;
bool listReporters() const;
+ bool listListeners() const;
- std::string getProcessName() const;
- std::string const& getReporterName() const;
+ std::vector<ReporterSpec> const& getReporterSpecs() const;
+ std::vector<ProcessedReporterSpec> const&
+ getProcessedReporterSpecs() const;
std::vector<std::string> const& getTestsOrTags() const override;
std::vector<std::string> const& getSectionsToRun() const override;
@@ -3799,36 +4225,35 @@ namespace Catch {
// IConfig interface
bool allowThrows() const override;
- std::ostream& stream() const override;
- std::string name() const override;
+ StringRef name() const override;
bool includeSuccessfulResults() const override;
bool warnAboutMissingAssertions() const override;
- bool warnAboutNoTests() const override;
+ bool warnAboutUnmatchedTestSpecs() const override;
+ bool zeroTestsCountAsSuccess() const override;
ShowDurations showDurations() const override;
double minDuration() const override;
TestRunOrder runOrder() const override;
- unsigned int rngSeed() const override;
- UseColour useColour() const override;
+ uint32_t rngSeed() const override;
+ unsigned int shardCount() const override;
+ unsigned int shardIndex() const override;
+ ColourMode defaultColourMode() const override;
bool shouldDebugBreak() const override;
int abortAfter() const override;
bool showInvisibles() const override;
Verbosity verbosity() const override;
+ bool skipBenchmarks() const override;
bool benchmarkNoAnalysis() const override;
- int benchmarkSamples() const override;
+ unsigned int benchmarkSamples() const override;
double benchmarkConfidenceInterval() const override;
unsigned int benchmarkResamples() const override;
std::chrono::milliseconds benchmarkWarmupTime() const override;
private:
-
- IStream const* openStream();
ConfigData m_data;
-
- Detail::unique_ptr<IStream const> m_stream;
+ std::vector<ProcessedReporterSpec> m_processedReporterSpecs;
TestSpec m_testSpec;
bool m_hasTestFilters = false;
};
-
} // end namespace Catch
#endif // CATCH_CONFIG_HPP_INCLUDED
@@ -3838,11 +4263,37 @@ namespace Catch {
#define CATCH_MESSAGE_HPP_INCLUDED
+
+#ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED
+#define CATCH_STREAM_END_STOP_HPP_INCLUDED
+
+
+namespace Catch {
+
+ // Use this in variadic streaming macros to allow
+ // << +StreamEndStop
+ // as well as
+ // << stuff +StreamEndStop
+ struct StreamEndStop {
+ StringRef operator+() const { return StringRef(); }
+
+ template <typename T>
+ friend T const& operator+( T const& value, StreamEndStop ) {
+ return value;
+ }
+ };
+
+} // namespace Catch
+
+#endif // CATCH_STREAM_END_STOP_HPP_INCLUDED
+
#include <string>
#include <vector>
namespace Catch {
+ struct SourceLineInfo;
+
struct MessageStream {
template<typename T>
@@ -3855,9 +4306,11 @@ namespace Catch {
};
struct MessageBuilder : MessageStream {
- MessageBuilder( StringRef const& macroName,
+ MessageBuilder( StringRef macroName,
SourceLineInfo const& lineInfo,
- ResultWas::OfType type );
+ ResultWas::OfType type ):
+ m_info(macroName, lineInfo, type) {}
+
template<typename T>
MessageBuilder& operator << ( T const& value ) {
@@ -3965,97 +4418,6 @@ namespace Catch {
#endif // CATCH_MESSAGE_HPP_INCLUDED
-#ifndef CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
-#define CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
-
-
-
-#ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
-#define CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
-
-namespace Catch {
-
- struct ReporterConfig;
-
- struct IReporterFactory {
- virtual ~IReporterFactory(); // = default
-
- virtual IStreamingReporterPtr
- create( ReporterConfig const& config ) const = 0;
- virtual std::string getDescription() const = 0;
- };
- using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
-} // namespace Catch
-
-#endif // CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
-
-namespace Catch {
-
- template <typename T>
- class ReporterFactory : public IReporterFactory {
-
- IStreamingReporterPtr create( ReporterConfig const& config ) const override {
- return Detail::make_unique<T>( config );
- }
-
- std::string getDescription() const override {
- return T::getDescription();
- }
- };
-
-
- template<typename T>
- class ReporterRegistrar {
- public:
- explicit ReporterRegistrar( std::string const& name ) {
- getMutableRegistryHub().registerReporter( name, Detail::make_unique<ReporterFactory<T>>() );
- }
- };
-
- template<typename T>
- class ListenerRegistrar {
-
- class ListenerFactory : public IReporterFactory {
-
- IStreamingReporterPtr create( ReporterConfig const& config ) const override {
- return Detail::make_unique<T>(config);
- }
- std::string getDescription() const override {
- return std::string();
- }
- };
-
- public:
-
- ListenerRegistrar() {
- getMutableRegistryHub().registerListener( Detail::make_unique<ListenerFactory>() );
- }
- };
-}
-
-#if !defined(CATCH_CONFIG_DISABLE)
-
-#define CATCH_REGISTER_REPORTER( name, reporterType ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
-#define CATCH_REGISTER_LISTENER( listenerType ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-#else // CATCH_CONFIG_DISABLE
-
-#define CATCH_REGISTER_REPORTER(name, reporterType)
-#define CATCH_REGISTER_LISTENER(listenerType)
-
-#endif // CATCH_CONFIG_DISABLE
-
-#endif // CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
-
-
#ifndef CATCH_SESSION_HPP_INCLUDED
#define CATCH_SESSION_HPP_INCLUDED
@@ -4092,11 +4454,11 @@ namespace Catch {
#include <cassert>
-#include <cctype>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
+#include <type_traits>
#include <vector>
namespace Catch {
@@ -4113,7 +4475,25 @@ namespace Catch {
ShortCircuitSame
};
+ struct accept_many_t {};
+ constexpr accept_many_t accept_many {};
+
namespace Detail {
+ struct fake_arg {
+ template <typename T>
+ operator T();
+ };
+
+ template <typename F, typename = void>
+ struct is_unary_function : std::false_type {};
+
+ template <typename F>
+ struct is_unary_function<
+ F,
+ Catch::Detail::void_t<decltype(
+ std::declval<F>()( fake_arg() ) )
+ >
+ > : std::true_type {};
// Traits for extracting arg and return type of lambdas (for single
// argument lambdas)
@@ -4129,8 +4509,7 @@ namespace Catch {
template <typename ClassT, typename ReturnT, typename ArgT>
struct UnaryLambdaTraits<ReturnT ( ClassT::* )( ArgT ) const> {
static const bool isValid = true;
- using ArgType = typename std::remove_const<
- typename std::remove_reference<ArgT>::type>::type;
+ using ArgType = std::remove_const_t<std::remove_reference_t<ArgT>>;
using ReturnType = ReturnT;
};
@@ -4264,20 +4643,20 @@ namespace Catch {
return { ResultType::Ok, value };
}
static auto ok() -> BasicResult { return { ResultType::Ok }; }
- static auto logicError( std::string const& message )
+ static auto logicError( std::string&& message )
-> BasicResult {
- return { ResultType::LogicError, message };
+ return { ResultType::LogicError, CATCH_MOVE(message) };
}
- static auto runtimeError( std::string const& message )
+ static auto runtimeError( std::string&& message )
-> BasicResult {
- return { ResultType::RuntimeError, message };
+ return { ResultType::RuntimeError, CATCH_MOVE(message) };
}
explicit operator bool() const {
return m_type == ResultType::Ok;
}
auto type() const -> ResultType { return m_type; }
- auto errorMessage() const -> std::string {
+ auto errorMessage() const -> std::string const& {
return m_errorMessage;
}
@@ -4296,8 +4675,8 @@ namespace Catch {
m_errorMessage; // Only populated if resultType is an error
BasicResult( ResultType type,
- std::string const& message ):
- ResultValueBase<T>( type ), m_errorMessage( message ) {
+ std::string&& message ):
+ ResultValueBase<T>( type ), m_errorMessage( CATCH_MOVE(message) ) {
assert( m_type != ResultType::Ok );
}
@@ -4353,7 +4732,7 @@ namespace Catch {
T temp;
auto result = convertInto( source, temp );
if ( result )
- target = std::move( temp );
+ target = CATCH_MOVE( temp );
return result;
}
#endif // CLARA_CONFIG_OPTIONAL_TYPE
@@ -4454,6 +4833,11 @@ namespace Catch {
}
};
+ template <typename L> struct BoundManyLambda : BoundLambda<L> {
+ explicit BoundManyLambda( L const& lambda ): BoundLambda<L>( lambda ) {}
+ bool isContainer() const override { return true; }
+ };
+
template <typename L> struct BoundFlagLambda : BoundFlagRefBase {
L m_lambda;
@@ -4508,12 +4892,23 @@ namespace Catch {
m_ref( ref ) {}
public:
- template <typename T>
+ template <typename LambdaT>
+ ParserRefImpl( accept_many_t,
+ LambdaT const& ref,
+ std::string const& hint ):
+ m_ref( std::make_shared<BoundManyLambda<LambdaT>>( ref ) ),
+ m_hint( hint ) {}
+
+ template <typename T,
+ typename = typename std::enable_if_t<
+ !Detail::is_unary_function<T>::value>>
ParserRefImpl( T& ref, std::string const& hint ):
m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
m_hint( hint ) {}
- template <typename LambdaT>
+ template <typename LambdaT,
+ typename = typename std::enable_if_t<
+ Detail::is_unary_function<LambdaT>::value>>
ParserRefImpl( LambdaT const& ref, std::string const& hint ):
m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
m_hint( hint ) {}
@@ -4554,6 +4949,7 @@ namespace Catch {
class Arg : public Detail::ParserRefImpl<Arg> {
public:
using ParserRefImpl::ParserRefImpl;
+ using ParserBase::parse;
Detail::InternalParseResult
parse(std::string const&,
@@ -4573,13 +4969,21 @@ namespace Catch {
explicit Opt(bool& ref);
+ template <typename LambdaT,
+ typename = typename std::enable_if_t<
+ Detail::is_unary_function<LambdaT>::value>>
+ Opt( LambdaT const& ref, std::string const& hint ):
+ ParserRefImpl( ref, hint ) {}
+
template <typename LambdaT>
- Opt(LambdaT const& ref, std::string const& hint) :
- ParserRefImpl(ref, hint) {}
+ Opt( accept_many_t, LambdaT const& ref, std::string const& hint ):
+ ParserRefImpl( accept_many, ref, hint ) {}
- template <typename T>
- Opt(T& ref, std::string const& hint) :
- ParserRefImpl(ref, hint) {}
+ template <typename T,
+ typename = typename std::enable_if_t<
+ !Detail::is_unary_function<T>::value>>
+ Opt( T& ref, std::string const& hint ):
+ ParserRefImpl( ref, hint ) {}
auto operator[](std::string const& optName) -> Opt& {
m_optNames.push_back(optName);
@@ -4604,12 +5008,6 @@ namespace Catch {
std::shared_ptr<std::string> m_name;
std::shared_ptr<Detail::BoundValueRefBase> m_ref;
- template <typename LambdaT>
- static auto makeRef(LambdaT const& lambda)
- -> std::shared_ptr<Detail::BoundValueRefBase> {
- return std::make_shared<Detail::BoundLambda<LambdaT>>(lambda);
- }
-
public:
ExeName();
explicit ExeName(std::string& ref);
@@ -4879,7 +5277,11 @@ namespace Catch {
namespace Catch {
- struct ITransientExpression {
+ class ITransientExpression {
+ bool m_isBinaryExpression;
+ bool m_result;
+
+ public:
auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
auto getResult() const -> bool { return m_result; }
virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
@@ -4897,8 +5299,6 @@ namespace Catch {
// complain if it's not here :-(
virtual ~ITransientExpression(); // = default;
- bool m_isBinaryExpression;
- bool m_result;
friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) {
expr.streamReconstructedExpression(out);
return out;
@@ -5029,60 +5429,53 @@ namespace Catch {
public:
explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
- template<typename RhsT>
- auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
- return { compareEqual( m_lhs, rhs ), m_lhs, "=="_sr, rhs };
+ template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0>
+ friend auto operator == ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> {
+ return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs };
}
- auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
- return { m_lhs == rhs, m_lhs, "=="_sr, rhs };
+ template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0>
+ friend auto operator == ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> {
+ return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs };
}
- template<typename RhsT>
- auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
- return { compareNotEqual( m_lhs, rhs ), m_lhs, "!="_sr, rhs };
+ template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0>
+ friend auto operator != ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> {
+ return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs };
}
- auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
- return { m_lhs != rhs, m_lhs, "!="_sr, rhs };
+ template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0>
+ friend auto operator != ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> {
+ return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs };
}
- template<typename RhsT>
- auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
- return { static_cast<bool>(m_lhs > rhs), m_lhs, ">"_sr, rhs };
- }
- template<typename RhsT>
- auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
- return { static_cast<bool>(m_lhs < rhs), m_lhs, "<"_sr, rhs };
- }
- template<typename RhsT>
- auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
- return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">="_sr, rhs };
- }
- template<typename RhsT>
- auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
- return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<="_sr, rhs };
- }
- template <typename RhsT>
- auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
- return { static_cast<bool>(m_lhs | rhs), m_lhs, "|"_sr, rhs };
- }
- template <typename RhsT>
- auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
- return { static_cast<bool>(m_lhs & rhs), m_lhs, "&"_sr, rhs };
- }
- template <typename RhsT>
- auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
- return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^"_sr, rhs };
+ #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \
+ template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0> \
+ friend auto operator op ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> { \
+ return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
+ } \
+ template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0> \
+ friend auto operator op ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> { \
+ return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
}
+ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<)
+ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>)
+ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<=)
+ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>=)
+ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|)
+ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&)
+ CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^)
+
+ #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR
+
template<typename RhsT>
- auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+ friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
static_assert(always_false<RhsT>::value,
"operator&& is not supported inside assertions, "
"wrap the expression inside parentheses, or decompose it");
}
template<typename RhsT>
- auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+ friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
static_assert(always_false<RhsT>::value,
"operator|| is not supported inside assertions, "
"wrap the expression inside parentheses, or decompose it");
@@ -5093,21 +5486,15 @@ namespace Catch {
}
};
- void handleExpression( ITransientExpression const& expr );
-
- template<typename T>
- void handleExpression( ExprLhs<T> const& expr ) {
- handleExpression( expr.makeUnaryExpr() );
- }
-
struct Decomposer {
- template<typename T>
- auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
- return ExprLhs<T const&>{ lhs };
+ template<typename T, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<T>>::value, int> = 0>
+ friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {
+ return ExprLhs<const T&>{ lhs };
}
- auto operator <=( bool value ) -> ExprLhs<bool> {
- return ExprLhs<bool>{ value };
+ template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
+ friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {
+ return ExprLhs<T>{ value };
}
};
@@ -5124,12 +5511,11 @@ namespace Catch {
#endif // CATCH_DECOMPOSER_HPP_INCLUDED
+#include <string>
+
namespace Catch {
- struct TestFailureException{};
- struct AssertionResultData;
- struct IResultCapture;
- class RunContext;
+ class IResultCapture;
struct AssertionReaction {
bool shouldDebugBreak = false;
@@ -5144,7 +5530,7 @@ namespace Catch {
public:
AssertionHandler
- ( StringRef const& macroName,
+ ( StringRef macroName,
SourceLineInfo const& lineInfo,
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition );
@@ -5161,7 +5547,7 @@ namespace Catch {
}
void handleExpr( ITransientExpression const& expr );
- void handleMessage(ResultWas::OfType resultType, StringRef const& message);
+ void handleMessage(ResultWas::OfType resultType, StringRef message);
void handleExceptionThrownAsExpected();
void handleUnexpectedExceptionNotThrown();
@@ -5176,7 +5562,7 @@ namespace Catch {
auto allowThrows() const -> bool;
};
- void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString );
+ void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString );
} // namespace Catch
@@ -5215,7 +5601,7 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
- do { \
+ do { /* NOLINT(bugprone-infinite-loop) */ \
/* The expression should not be evaluated, but warnings should hopefully be checked */ \
CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \
Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
@@ -5226,7 +5612,8 @@ namespace Catch {
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
- } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )
+ } while( (void)0, (false) && static_cast<const bool&>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
+ // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
@@ -5314,243 +5701,6 @@ namespace Catch {
#endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED
-#ifndef CATCH_PREPROCESSOR_HPP_INCLUDED
-#define CATCH_PREPROCESSOR_HPP_INCLUDED
-
-
-#if defined(__GNUC__)
-// We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work
-#pragma GCC system_header
-#endif
-
-
-#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
-#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
-
-#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
-// MSVC needs more evaluations
-#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
-#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
-#else
-#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__)
-#endif
-
-#define CATCH_REC_END(...)
-#define CATCH_REC_OUT
-
-#define CATCH_EMPTY()
-#define CATCH_DEFER(id) id CATCH_EMPTY()
-
-#define CATCH_REC_GET_END2() 0, CATCH_REC_END
-#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
-#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
-#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
-#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
-#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
-
-#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
-
-#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-
-// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
-// and passes userdata as the first parameter to each invocation,
-// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
-#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
-#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
-#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
-#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
-#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
-#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
-#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
-#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
-#else
-// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
-#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
-#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
-#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
-#endif
-
-#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
-#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
-
-#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
-#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
-#else
-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
-#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
-#endif
-
-#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
- CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
-
-#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
-#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
-#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
-#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
-#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
-#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
-#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
-#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
-#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
-#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
-#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
-
-#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
-
-#define INTERNAL_CATCH_TYPE_GEN\
- template<typename...> struct TypeList {};\
- template<typename...Ts>\
- constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
- template<template<typename...> class...> struct TemplateTypeList{};\
- template<template<typename...> class...Cs>\
- constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
- template<typename...>\
- struct append;\
- template<typename...>\
- struct rewrap;\
- template<template<typename...> class, typename...>\
- struct create;\
- template<template<typename...> class, typename>\
- struct convert;\
- \
- template<typename T> \
- struct append<T> { using type = T; };\
- template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
- struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
- template< template<typename...> class L1, typename...E1, typename...Rest>\
- struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
- \
- template< template<typename...> class Container, template<typename...> class List, typename...elems>\
- struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
- template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
- struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
- \
- template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
- struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
- template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
- struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
-
-#define INTERNAL_CATCH_NTTP_1(signature, ...)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
- template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
- template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
- constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
- \
- template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
- template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
- struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
- template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
- struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- static void TestName()
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- static void TestName()
-
-#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- static void TestName()
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- static void TestName()
-
-#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
- template<typename Type>\
- void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
- template<typename Type>\
- void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
- }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
- template<typename TestType> \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
- void test();\
- }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
- void test();\
- }
-
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
- template<typename TestType> \
- void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
- template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
- void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_NTTP_0
-#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
-#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
-#else
-#define INTERNAL_CATCH_NTTP_0(signature)
-#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
-#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
-#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
-#endif
-
-#endif // CATCH_PREPROCESSOR_HPP_INCLUDED
-
-
#ifndef CATCH_SECTION_HPP_INCLUDED
#define CATCH_SECTION_HPP_INCLUDED
@@ -5563,9 +5713,6 @@ namespace Catch {
namespace Catch {
- auto getCurrentNanosecondsSinceEpoch() -> uint64_t;
- auto getEstimatedClockResolution() -> uint64_t;
-
class Timer {
uint64_t m_nanoseconds = 0;
public:
@@ -5580,8 +5727,6 @@ namespace Catch {
#endif // CATCH_TIMER_HPP_INCLUDED
-#include <string>
-
namespace Catch {
class Section : Detail::NonCopyable {
@@ -5632,16 +5777,18 @@ namespace Catch {
class TestSpec;
struct TestCaseInfo;
- struct ITestInvoker {
+ class ITestInvoker {
+ public:
virtual void invoke () const = 0;
- virtual ~ITestInvoker();
+ virtual ~ITestInvoker(); // = default
};
class TestCaseHandle;
- struct IConfig;
+ class IConfig;
- struct ITestCaseRegistry {
- virtual ~ITestCaseRegistry();
+ class ITestCaseRegistry {
+ public:
+ virtual ~ITestCaseRegistry(); // = default
// TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later
virtual std::vector<TestCaseInfo* > const& getAllInfos() const = 0;
virtual std::vector<TestCaseHandle> const& getAllTests() const = 0;
@@ -5657,6 +5804,20 @@ namespace Catch {
#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
+
+#ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
+#define CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
+
+#define INTERNAL_CATCH_EXPAND1( param ) INTERNAL_CATCH_EXPAND2( param )
+#define INTERNAL_CATCH_EXPAND2( ... ) INTERNAL_CATCH_NO##__VA_ARGS__
+#define INTERNAL_CATCH_DEF( ... ) INTERNAL_CATCH_DEF __VA_ARGS__
+#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+
+#define INTERNAL_CATCH_REMOVE_PARENS( ... ) \
+ INTERNAL_CATCH_EXPAND1( INTERNAL_CATCH_DEF __VA_ARGS__ )
+
+#endif // CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
+
// GCC 5 and older do not properly handle disabling unused-variable warning
// with a _Pragma. This means that we have to leak the suppression to the
// user code as well :-(
@@ -5684,19 +5845,19 @@ Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() );
template<typename C>
Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) {
- return Detail::unique_ptr<ITestInvoker>( new TestInvokerAsMethod<C>(testAsMethod) );
+ return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod );
}
struct NameAndTags {
- NameAndTags(StringRef const& name_ = StringRef(),
- StringRef const& tags_ = StringRef()) noexcept:
- name(name_), tags(tags_) {}
+ constexpr NameAndTags( StringRef name_ = StringRef(),
+ StringRef tags_ = StringRef() ) noexcept:
+ name( name_ ), tags( tags_ ) {}
StringRef name;
StringRef tags;
};
struct AutoReg : Detail::NonCopyable {
- AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+ AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept;
};
} // end namespace Catch
@@ -5722,7 +5883,7 @@ struct AutoReg : Detail::NonCopyable {
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
static void TestName()
#define INTERNAL_CATCH_TESTCASE( ... ) \
- INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
@@ -5744,7 +5905,7 @@ struct AutoReg : Detail::NonCopyable {
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
void TestName::test()
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
- INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
@@ -5758,6 +5919,7 @@ struct AutoReg : Detail::NonCopyable {
#endif // CATCH_TEST_REGISTRY_HPP_INCLUDED
+
// All of our user-facing macros support configuration toggle, that
// forces them to be defined prefixed with CATCH_. We also like to
// support another toggle that can minimize (disable) their implementation.
@@ -5774,8 +5936,8 @@ struct AutoReg : Detail::NonCopyable {
#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
- #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
- #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+ #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
@@ -5796,9 +5958,13 @@ struct AutoReg : Detail::NonCopyable {
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
#define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ )
#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
+ #define CATCH_STATIC_CHECK( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ )
+ #define CATCH_STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
#else
#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ )
#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ )
+ #define CATCH_STATIC_CHECK( ... ) CATCH_CHECK( __VA_ARGS__ )
+ #define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_FALSE( __VA_ARGS__ )
#endif
@@ -5831,8 +5997,8 @@ struct AutoReg : Detail::NonCopyable {
#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
#define CATCH_CHECK_NOTHROW( ... ) (void)(0)
- #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
- #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+ #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_REGISTER_TEST_CASE( Function, ... ) (void)(0)
#define CATCH_SECTION( ... )
@@ -5843,10 +6009,12 @@ struct AutoReg : Detail::NonCopyable {
#define CATCH_STATIC_REQUIRE( ... ) (void)(0)
#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
+ #define CATCH_STATIC_CHECK( ... ) (void)(0)
+ #define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0)
// "BDD-style" convenience wrappers
- #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
- #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
+ #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
+ #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
#define CATCH_GIVEN( desc )
#define CATCH_AND_GIVEN( desc )
#define CATCH_WHEN( desc )
@@ -5865,8 +6033,8 @@ struct AutoReg : Detail::NonCopyable {
#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
- #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
- #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+ #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
@@ -5887,9 +6055,13 @@ struct AutoReg : Detail::NonCopyable {
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
#define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
+ #define STATIC_CHECK( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
+ #define STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
#else
#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ )
#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ )
+ #define STATIC_CHECK( ... ) CHECK( __VA_ARGS__ )
+ #define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ )
#endif
// "BDD-style" convenience wrappers
@@ -5921,8 +6093,8 @@ struct AutoReg : Detail::NonCopyable {
#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
#define CHECK_NOTHROW( ... ) (void)(0)
- #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__)
- #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+ #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 REGISTER_TEST_CASE( Function, ... ) (void)(0)
#define SECTION( ... )
@@ -5933,10 +6105,12 @@ struct AutoReg : Detail::NonCopyable {
#define STATIC_REQUIRE( ... ) (void)(0)
#define STATIC_REQUIRE_FALSE( ... ) (void)(0)
+ #define STATIC_CHECK( ... ) (void)(0)
+ #define STATIC_CHECK_FALSE( ... ) (void)(0)
// "BDD-style" convenience wrappers
- #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) )
- #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
+ #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) )
+ #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
#define GIVEN( desc )
#define AND_GIVEN( desc )
@@ -5956,6 +6130,238 @@ struct AutoReg : Detail::NonCopyable {
#define CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
+
+#ifndef CATCH_PREPROCESSOR_HPP_INCLUDED
+#define CATCH_PREPROCESSOR_HPP_INCLUDED
+
+
+#if defined(__GNUC__)
+// We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work
+#pragma GCC system_header
+#endif
+
+
+#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
+#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
+
+#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
+// MSVC needs more evaluations
+#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
+#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
+#else
+#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__)
+#endif
+
+#define CATCH_REC_END(...)
+#define CATCH_REC_OUT
+
+#define CATCH_EMPTY()
+#define CATCH_DEFER(id) id CATCH_EMPTY()
+
+#define CATCH_REC_GET_END2() 0, CATCH_REC_END
+#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
+#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
+#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
+#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
+#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
+
+#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+
+#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+
+// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
+// and passes userdata as the first parameter to each invocation,
+// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
+#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
+#else
+// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
+#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
+#endif
+
+#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
+#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
+#else
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#endif
+
+#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
+ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
+
+#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
+#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
+#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
+#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
+#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
+#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
+#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
+#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
+#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
+#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
+#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
+
+#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+
+#define INTERNAL_CATCH_TYPE_GEN\
+ template<typename...> struct TypeList {};\
+ template<typename...Ts>\
+ constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
+ template<template<typename...> class...> struct TemplateTypeList{};\
+ template<template<typename...> class...Cs>\
+ constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
+ template<typename...>\
+ struct append;\
+ template<typename...>\
+ struct rewrap;\
+ template<template<typename...> class, typename...>\
+ struct create;\
+ template<template<typename...> class, typename>\
+ struct convert;\
+ \
+ template<typename T> \
+ struct append<T> { using type = T; };\
+ template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
+ struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
+ template< template<typename...> class L1, typename...E1, typename...Rest>\
+ struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
+ \
+ template< template<typename...> class Container, template<typename...> class List, typename...elems>\
+ struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
+ template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
+ struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
+ \
+ template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
+ struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
+ template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
+ struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
+
+#define INTERNAL_CATCH_NTTP_1(signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
+ template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
+ template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
+ constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
+ \
+ template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
+ template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
+ struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
+ template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
+ struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+
+#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
+ template<typename Type>\
+ void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
+ template<typename Type>\
+ void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
+ template<typename TestType> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
+ template<typename TestType> \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_NTTP_0
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+#else
+#define INTERNAL_CATCH_NTTP_0(signature)
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
+#endif
+
+#endif // CATCH_PREPROCESSOR_HPP_INCLUDED
+
+
// GCC 5 and older do not properly handle disabling unused-variable warning
// with a _Pragma. This means that we have to leak the suppression to the
// user code as well :-(
@@ -5976,34 +6382,34 @@ struct AutoReg : Detail::NonCopyable {
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ ) )
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
#endif
#endif
@@ -6024,9 +6430,9 @@ struct AutoReg : Detail::NonCopyable {
template<typename...Types> \
struct TestName{\
TestName(){\
- int index = 0; \
+ size_t index = 0; \
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
- using expander = int[];\
+ using expander = size_t[];\
(void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
}\
};\
@@ -6041,18 +6447,18 @@ struct AutoReg : Detail::NonCopyable {
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ ) )
#endif
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
@@ -6069,12 +6475,12 @@ struct AutoReg : Detail::NonCopyable {
template<typename... Types> \
struct TestName { \
void reg_tests() { \
- int index = 0; \
- using expander = int[]; \
+ size_t index = 0; \
+ using expander = size_t[]; \
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + std::string(types_list[index % num_types]) + '>', Tags } ), index++)... };/* NOLINT */\
} \
}; \
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
@@ -6091,18 +6497,18 @@ struct AutoReg : Detail::NonCopyable {
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__)
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename T,__VA_ARGS__)
#else
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename T, __VA_ARGS__ ) )
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__)
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__)
#else
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ ) )
#endif
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
@@ -6117,8 +6523,8 @@ struct AutoReg : Detail::NonCopyable {
template<typename... Types> \
struct TestName { \
void reg_tests() { \
- int index = 0; \
- using expander = int[]; \
+ size_t index = 0; \
+ using expander = size_t[]; \
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
} \
};\
@@ -6134,7 +6540,7 @@ struct AutoReg : Detail::NonCopyable {
static void TestFunc()
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
- INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, TmplList )
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, TmplList )
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
@@ -6152,9 +6558,9 @@ struct AutoReg : Detail::NonCopyable {
template<typename...Types> \
struct TestNameClass{\
TestNameClass(){\
- int index = 0; \
+ size_t index = 0; \
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
- using expander = int[];\
+ using expander = size_t[];\
(void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
}\
};\
@@ -6169,18 +6575,18 @@ struct AutoReg : Detail::NonCopyable {
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
#endif
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
@@ -6200,12 +6606,12 @@ struct AutoReg : Detail::NonCopyable {
template<typename...Types>\
struct TestNameClass{\
void reg_tests(){\
- int index = 0;\
- using expander = int[];\
+ std::size_t index = 0;\
+ using expander = std::size_t[];\
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + std::string(types_list[index % num_types]) + '>', Tags } ), index++)... };/* NOLINT */ \
}\
};\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
@@ -6222,18 +6628,18 @@ struct AutoReg : Detail::NonCopyable {
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
#endif
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
@@ -6251,8 +6657,8 @@ struct AutoReg : Detail::NonCopyable {
template<typename...Types>\
struct TestNameClass{\
void reg_tests(){\
- int index = 0;\
- using expander = int[];\
+ size_t index = 0;\
+ using expander = size_t[];\
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
}\
};\
@@ -6268,7 +6674,7 @@ struct AutoReg : Detail::NonCopyable {
void TestName<TestType>::test()
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
- INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, TmplList )
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, TmplList )
#endif // CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
@@ -6391,14 +6797,24 @@ struct AutoReg : Detail::NonCopyable {
namespace Catch {
+ /**
+ * A **view** of a tag string that provides case insensitive comparisons
+ *
+ * Note that in Catch2 internals, the square brackets around tags are
+ * not a part of tag's representation, so e.g. "[cool-tag]" is represented
+ * as "cool-tag" internally.
+ */
struct Tag {
- Tag(StringRef original_, StringRef lowerCased_):
- original(original_), lowerCased(lowerCased_)
+ constexpr Tag(StringRef original_):
+ original(original_)
{}
- StringRef original, lowerCased;
+ StringRef original;
+
+ friend bool operator< ( Tag const& lhs, Tag const& rhs );
+ friend bool operator==( Tag const& lhs, Tag const& rhs );
};
- struct ITestInvoker;
+ class ITestInvoker;
enum class TestCaseProperties : uint8_t {
None = 0,
@@ -6410,10 +6826,18 @@ namespace Catch {
Benchmark = 1 << 6
};
-
+ /**
+ * Various metadata about the test case.
+ *
+ * A test case is uniquely identified by its (class)name and tags
+ * combination, with source location being ignored, and other properties
+ * being determined from tags.
+ *
+ * Tags are kept sorted.
+ */
struct TestCaseInfo : Detail::NonCopyable {
- TestCaseInfo(std::string const& _className,
+ TestCaseInfo(StringRef _className,
NameAndTags const& _tags,
SourceLineInfo const& _lineInfo);
@@ -6425,13 +6849,17 @@ namespace Catch {
// Adds the tag(s) with test's filename (for the -# flag)
void addFilenameTag();
+ //! Orders by name, classname and tags
+ friend bool operator<( TestCaseInfo const& lhs,
+ TestCaseInfo const& rhs );
+
std::string tagsAsString() const;
std::string name;
- std::string className;
+ StringRef className;
private:
- std::string backingTags, backingLCaseTags;
+ std::string backingTags;
// Internally we copy tags to the backing storage and then add
// refs to this storage to the tags vector.
void internalAppendTag(StringRef tagString);
@@ -6441,6 +6869,12 @@ namespace Catch {
TestCaseProperties properties = TestCaseProperties::None;
};
+ /**
+ * Wrapper over the test case information and the test case invoker
+ *
+ * Does not own either, and is specifically made to be cheap
+ * to copy around.
+ */
class TestCaseHandle {
TestCaseInfo* m_info;
ITestInvoker* m_invoker;
@@ -6453,14 +6887,12 @@ namespace Catch {
}
TestCaseInfo const& getTestCaseInfo() const;
-
- bool operator== ( TestCaseHandle const& rhs ) const;
- bool operator < ( TestCaseHandle const& rhs ) const;
};
- Detail::unique_ptr<TestCaseInfo> makeTestCaseInfo( std::string const& className,
- NameAndTags const& nameAndTags,
- SourceLineInfo const& lineInfo );
+ Detail::unique_ptr<TestCaseInfo>
+ makeTestCaseInfo( StringRef className,
+ NameAndTags const& nameAndTags,
+ SourceLineInfo const& lineInfo );
}
#ifdef __clang__
@@ -6485,17 +6917,18 @@ namespace Catch {
namespace Catch {
using exceptionTranslateFunction = std::string(*)();
- struct IExceptionTranslator;
+ class IExceptionTranslator;
using ExceptionTranslators = std::vector<Detail::unique_ptr<IExceptionTranslator const>>;
- struct IExceptionTranslator {
- virtual ~IExceptionTranslator();
+ class IExceptionTranslator {
+ public:
+ virtual ~IExceptionTranslator(); // = default
virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
};
- struct IExceptionTranslatorRegistry {
- virtual ~IExceptionTranslatorRegistry();
-
+ class IExceptionTranslatorRegistry {
+ public:
+ virtual ~IExceptionTranslatorRegistry(); // = default
virtual std::string translateActiveException() const = 0;
};
@@ -6539,8 +6972,9 @@ namespace Catch {
public:
template<typename T>
ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) {
- getMutableRegistryHub().registerTranslator
- ( new ExceptionTranslator<T>( translateFunction ) );
+ getMutableRegistryHub().registerTranslator(
+ Detail::make_unique<ExceptionTranslator<T>>(translateFunction)
+ );
}
};
@@ -6573,6 +7007,7 @@ namespace Catch {
#endif // CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
+
#ifndef CATCH_VERSION_HPP_INCLUDED
#define CATCH_VERSION_HPP_INCLUDED
@@ -6612,7 +7047,7 @@ namespace Catch {
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 0
-#define CATCH_VERSION_PATCH 0
+#define CATCH_VERSION_PATCH 1
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@@ -6670,10 +7105,30 @@ namespace Catch {
#define CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED
+#include <string>
+
namespace Catch {
namespace Generators {
class GeneratorUntypedBase {
+ // Caches result from `toStringImpl`, assume that when it is an
+ // empty string, the cache is invalidated.
+ mutable std::string m_stringReprCache;
+
+ // Counts based on `next` returning true
+ std::size_t m_currentElementIndex = 0;
+
+ /**
+ * Attempts to move the generator to the next element
+ *
+ * Returns true iff the move succeeded (and a valid element
+ * can be retrieved).
+ */
+ virtual bool next() = 0;
+
+ //! Customization point for `currentElementAsString`
+ virtual std::string stringifyImpl() const = 0;
+
public:
GeneratorUntypedBase() = default;
// Generation of copy ops is deprecated (and Clang will complain)
@@ -6683,17 +7138,41 @@ namespace Catch {
virtual ~GeneratorUntypedBase(); // = default;
- // Attempts to move the generator to the next element
- //
- // Returns true iff the move succeeded (and a valid element
- // can be retrieved).
- virtual bool next() = 0;
+ /**
+ * Attempts to move the generator to the next element
+ *
+ * Serves as a non-virtual interface to `next`, so that the
+ * top level interface can provide sanity checking and shared
+ * features.
+ *
+ * As with `next`, returns true iff the move succeeded and
+ * the generator has new valid element to provide.
+ */
+ bool countedNext();
+
+ std::size_t currentElementIndex() const { return m_currentElementIndex; }
+
+ /**
+ * Returns generator's current element as user-friendly string.
+ *
+ * By default returns string equivalent to calling
+ * `Catch::Detail::stringify` on the current element, but generators
+ * can customize their implementation as needed.
+ *
+ * Not thread-safe due to internal caching.
+ *
+ * The returned ref is valid only until the generator instance
+ * is destructed, or it moves onto the next element, whichever
+ * comes first.
+ */
+ StringRef currentElementAsString() const;
};
using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>;
} // namespace Generators
- struct IGeneratorTracker {
+ class IGeneratorTracker {
+ public:
virtual ~IGeneratorTracker(); // = default;
virtual auto hasGenerator() const -> bool = 0;
virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
@@ -6706,7 +7185,6 @@ namespace Catch {
#include <vector>
#include <tuple>
-#include <utility>
namespace Catch {
@@ -6721,7 +7199,12 @@ namespace Detail {
} // end namespace detail
template<typename T>
- struct IGenerator : GeneratorUntypedBase {
+ class IGenerator : public GeneratorUntypedBase {
+ std::string stringifyImpl() const override {
+ return ::Catch::Detail::stringify( get() );
+ }
+
+ public:
~IGenerator() override = default;
IGenerator() = default;
IGenerator(IGenerator const&) = default;
@@ -6747,13 +7230,13 @@ namespace Detail {
GeneratorWrapper(IGenerator<T>* generator):
m_generator(generator) {}
GeneratorWrapper(GeneratorPtr<T> generator):
- m_generator(std::move(generator)) {}
+ m_generator(CATCH_MOVE(generator)) {}
T const& get() const {
return m_generator->get();
}
bool next() {
- return m_generator->next();
+ return m_generator->countedNext();
}
};
@@ -6762,8 +7245,11 @@ namespace Detail {
class SingleValueGenerator final : public IGenerator<T> {
T m_value;
public:
+ SingleValueGenerator(T const& value) :
+ m_value(value)
+ {}
SingleValueGenerator(T&& value):
- m_value(std::forward<T>(value))
+ m_value(CATCH_MOVE(value))
{}
T const& get() const override {
@@ -6793,9 +7279,11 @@ namespace Detail {
}
};
- template <typename T>
- GeneratorWrapper<T> value(T&& value) {
- return GeneratorWrapper<T>(Catch::Detail::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
+ template <typename T, typename DecayedT = std::decay_t<T>>
+ GeneratorWrapper<DecayedT> value( T&& value ) {
+ return GeneratorWrapper<DecayedT>(
+ Catch::Detail::make_unique<SingleValueGenerator<DecayedT>>(
+ CATCH_FORWARD( value ) ) );
}
template <typename T>
GeneratorWrapper<T> values(std::initializer_list<T> values) {
@@ -6807,27 +7295,36 @@ namespace Detail {
std::vector<GeneratorWrapper<T>> m_generators;
size_t m_current = 0;
- void populate(GeneratorWrapper<T>&& generator) {
- m_generators.emplace_back(std::move(generator));
+ void add_generator( GeneratorWrapper<T>&& generator ) {
+ m_generators.emplace_back( CATCH_MOVE( generator ) );
}
- void populate(T&& val) {
- m_generators.emplace_back(value(std::forward<T>(val)));
+ void add_generator( T const& val ) {
+ m_generators.emplace_back( value( val ) );
}
- template<typename U>
- void populate(U&& val) {
- populate(T(std::forward<U>(val)));
+ void add_generator( T&& val ) {
+ m_generators.emplace_back( value( CATCH_MOVE( val ) ) );
}
- template<typename U, typename... Gs>
- void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
- populate(std::forward<U>(valueOrGenerator));
- populate(std::forward<Gs>(moreGenerators)...);
+ template <typename U>
+ std::enable_if_t<!std::is_same<std::decay_t<U>, T>::value>
+ add_generator( U&& val ) {
+ add_generator( T( CATCH_FORWARD( val ) ) );
+ }
+
+ template <typename U> void add_generators( U&& valueOrGenerator ) {
+ add_generator( CATCH_FORWARD( valueOrGenerator ) );
+ }
+
+ template <typename U, typename... Gs>
+ void add_generators( U&& valueOrGenerator, Gs&&... moreGenerators ) {
+ add_generator( CATCH_FORWARD( valueOrGenerator ) );
+ add_generators( CATCH_FORWARD( moreGenerators )... );
}
public:
template <typename... Gs>
Generators(Gs &&... moreGenerators) {
m_generators.reserve(sizeof...(Gs));
- populate(std::forward<Gs>(moreGenerators)...);
+ add_generators(CATCH_FORWARD(moreGenerators)...);
}
T const& get() const override {
@@ -6847,8 +7344,9 @@ namespace Detail {
};
- template<typename... Ts>
- GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<std::decay_t<Ts>...>> tuples ) {
+ template <typename... Ts>
+ GeneratorWrapper<std::tuple<std::decay_t<Ts>...>>
+ table( std::initializer_list<std::tuple<std::decay_t<Ts>...>> tuples ) {
return values<std::tuple<Ts...>>( tuples );
}
@@ -6858,19 +7356,19 @@ namespace Detail {
template<typename T, typename... Gs>
auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
- return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
+ return Generators<T>(CATCH_MOVE(generator), CATCH_FORWARD(moreGenerators)...);
}
template<typename T>
auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
- return Generators<T>(std::move(generator));
+ return Generators<T>(CATCH_MOVE(generator));
}
template<typename T, typename... Gs>
- auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
- return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
+ auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<std::decay_t<T>> {
+ return makeGenerators( value( CATCH_FORWARD( val ) ), CATCH_FORWARD( moreGenerators )... );
}
template<typename T, typename U, typename... Gs>
auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
- return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
+ return makeGenerators( value( T( CATCH_FORWARD( val ) ) ), CATCH_FORWARD( moreGenerators )... );
}
auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
@@ -6914,6 +7412,8 @@ namespace Detail {
#define CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED
+#include <cassert>
+
namespace Catch {
namespace Generators {
@@ -6924,7 +7424,7 @@ namespace Generators {
size_t m_target;
public:
TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
- m_generator(std::move(generator)),
+ m_generator(CATCH_MOVE(generator)),
m_target(target)
{
assert(target != 0 && "Empty generators are not allowed");
@@ -6950,7 +7450,7 @@ namespace Generators {
template <typename T>
GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
- return GeneratorWrapper<T>(Catch::Detail::make_unique<TakeGenerator<T>>(target, std::move(generator)));
+ return GeneratorWrapper<T>(Catch::Detail::make_unique<TakeGenerator<T>>(target, CATCH_MOVE(generator)));
}
@@ -6961,8 +7461,8 @@ namespace Generators {
public:
template <typename P = Predicate>
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
- m_generator(std::move(generator)),
- m_predicate(std::forward<P>(pred))
+ m_generator(CATCH_MOVE(generator)),
+ m_predicate(CATCH_FORWARD(pred))
{
if (!m_predicate(m_generator.get())) {
// It might happen that there are no values that pass the
@@ -6991,7 +7491,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>>(std::forward<Predicate>(pred), std::move(generator)));
+ return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
}
template <typename T>
@@ -7006,7 +7506,7 @@ namespace Generators {
size_t m_repeat_index = 0;
public:
RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
- m_generator(std::move(generator)),
+ m_generator(CATCH_MOVE(generator)),
m_target_repeats(repeats)
{
assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
@@ -7047,7 +7547,7 @@ namespace Generators {
template <typename T>
GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
- return GeneratorWrapper<T>(Catch::Detail::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
+ return GeneratorWrapper<T>(Catch::Detail::make_unique<RepeatGenerator<T>>(repeats, CATCH_MOVE(generator)));
}
template <typename T, typename U, typename Func>
@@ -7060,8 +7560,8 @@ namespace Generators {
public:
template <typename F2 = Func>
MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
- m_generator(std::move(generator)),
- m_function(std::forward<F2>(function)),
+ m_generator(CATCH_MOVE(generator)),
+ m_function(CATCH_FORWARD(function)),
m_cache(m_function(m_generator.get()))
{}
@@ -7080,14 +7580,14 @@ namespace Generators {
template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
return GeneratorWrapper<T>(
- Catch::Detail::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+ Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
);
}
template <typename T, typename U, typename Func>
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
return GeneratorWrapper<T>(
- Catch::Detail::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+ Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
);
}
@@ -7099,7 +7599,7 @@ namespace Generators {
bool m_used_up = false;
public:
ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
- m_chunk_size(size), m_generator(std::move(generator))
+ m_chunk_size(size), m_generator(CATCH_MOVE(generator))
{
m_chunk.reserve(m_chunk_size);
if (m_chunk_size != 0) {
@@ -7130,7 +7630,7 @@ namespace Generators {
template <typename T>
GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<std::vector<T>>(
- Catch::Detail::make_unique<ChunkGenerator<T>>(size, std::move(generator))
+ Catch::Detail::make_unique<ChunkGenerator<T>>(size, CATCH_MOVE(generator))
);
}
@@ -7203,16 +7703,21 @@ namespace Catch {
namespace Catch {
namespace Generators {
+namespace Detail {
+ // Returns a suitable seed for a random floating generator based off
+ // the primary internal rng. It does so by taking current value from
+ // the rng and returning it as the seed.
+ std::uint32_t getSeed();
+}
template <typename Float>
class RandomFloatingGenerator final : public IGenerator<Float> {
- Catch::SimplePcg32& m_rng;
+ Catch::SimplePcg32 m_rng;
std::uniform_real_distribution<Float> m_dist;
Float m_current_number;
public:
-
- RandomFloatingGenerator(Float a, Float b):
- m_rng(rng()),
+ RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ):
+ m_rng(seed),
m_dist(a, b) {
static_cast<void>(next());
}
@@ -7228,13 +7733,12 @@ public:
template <typename Integer>
class RandomIntegerGenerator final : public IGenerator<Integer> {
- Catch::SimplePcg32& m_rng;
+ Catch::SimplePcg32 m_rng;
std::uniform_int_distribution<Integer> m_dist;
Integer m_current_number;
public:
-
- RandomIntegerGenerator(Integer a, Integer b):
- m_rng(rng()),
+ RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ):
+ m_rng(seed),
m_dist(a, b) {
static_cast<void>(next());
}
@@ -7255,7 +7759,7 @@ std::enable_if_t<std::is_integral<T>::value && !std::is_same<T, bool>::value,
GeneratorWrapper<T>>
random(T a, T b) {
return GeneratorWrapper<T>(
- Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b)
+ Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed())
);
}
@@ -7264,7 +7768,7 @@ std::enable_if_t<std::is_floating_point<T>::value,
GeneratorWrapper<T>>
random(T a, T b) {
return GeneratorWrapper<T>(
- Catch::Detail::make_unique<RandomFloatingGenerator<T>>(a, b)
+ Catch::Detail::make_unique<RandomFloatingGenerator<T>>(a, b, Detail::getSeed())
);
}
@@ -7401,29 +7905,94 @@ GeneratorWrapper<ResultType> from_range(Container const& cnt) {
+#ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
+#define CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
+
+
+#include <string>
+
+namespace Catch {
+
+ struct ReporterConfig;
+ class IConfig;
+ class IEventListener;
+ using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
+
+
+ class IReporterFactory {
+ public:
+ virtual ~IReporterFactory(); // = default
+
+ virtual IEventListenerPtr
+ create( ReporterConfig&& config ) const = 0;
+ virtual std::string getDescription() const = 0;
+ };
+ using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
+
+ class EventListenerFactory {
+ public:
+ virtual ~EventListenerFactory(); // = default
+ virtual IEventListenerPtr create( IConfig const* config ) const = 0;
+ //! Return a meaningful name for the listener, e.g. its type name
+ virtual StringRef getName() const = 0;
+ //! Return listener's description if available
+ virtual std::string getDescription() const = 0;
+ };
+} // namespace Catch
+
+#endif // CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
+
+
#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
+
+#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
+#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
+
+
+namespace Catch {
+ namespace Detail {
+ //! Provides case-insensitive `op<` semantics when called
+ struct CaseInsensitiveLess {
+ bool operator()( StringRef lhs,
+ StringRef rhs ) const;
+ };
+
+ //! Provides case-insensitive `op==` semantics when called
+ struct CaseInsensitiveEqualTo {
+ bool operator()( StringRef lhs,
+ StringRef rhs ) const;
+ };
+
+ } // namespace Detail
+} // namespace Catch
+
+#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
+
#include <string>
#include <vector>
#include <map>
namespace Catch {
- struct IConfig;
+ class IConfig;
- struct IStreamingReporter;
- using IStreamingReporterPtr = Detail::unique_ptr<IStreamingReporter>;
- struct IReporterFactory;
+ class IEventListener;
+ using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
+ class IReporterFactory;
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
+ struct ReporterConfig;
+ class EventListenerFactory;
- struct IReporterRegistry {
- using FactoryMap = std::map<std::string, IReporterFactoryPtr>;
- using Listeners = std::vector<IReporterFactoryPtr>;
+ class IReporterRegistry {
+ public:
+ using FactoryMap = std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>;
+ using Listeners = std::vector<Detail::unique_ptr<EventListenerFactory>>;
- virtual ~IReporterRegistry();
- virtual IStreamingReporterPtr create( std::string const& name, IConfig const* config ) const = 0;
+ virtual ~IReporterRegistry(); // = default
+ virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0;
virtual FactoryMap const& getFactories() const = 0;
virtual Listeners const& getListeners() const = 0;
};
@@ -7433,20 +8002,6 @@ namespace Catch {
#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
-#ifndef CATCH_INTERFACES_RUNNER_HPP_INCLUDED
-#define CATCH_INTERFACES_RUNNER_HPP_INCLUDED
-
-namespace Catch {
-
- struct IRunner {
- virtual ~IRunner();
- virtual bool aborting() const = 0;
- };
-}
-
-#endif // CATCH_INTERFACES_RUNNER_HPP_INCLUDED
-
-
#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
@@ -7456,8 +8011,9 @@ namespace Catch {
struct TagAlias;
- struct ITagAliasRegistry {
- virtual ~ITagAliasRegistry();
+ class ITagAliasRegistry {
+ public:
+ virtual ~ITagAliasRegistry(); // = default
// Nullptr if not present
virtual TagAlias const* find( std::string const& alias ) const = 0;
virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
@@ -7474,6 +8030,33 @@ namespace Catch {
/** \file
+ * Wrapper for ANDROID_LOGWRITE configuration option
+ *
+ * We want to default to enabling it when compiled for android, but
+ * users of the library should also be able to disable it if they want
+ * to.
+ */
+
+#ifndef CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
+#define CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
+
+
+#if defined(__ANDROID__)
+# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
+#endif
+
+
+#if defined( CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE ) && \
+ !defined( CATCH_CONFIG_NO_ANDROID_LOGWRITE ) && \
+ !defined( CATCH_CONFIG_ANDROID_LOGWRITE )
+# define CATCH_CONFIG_ANDROID_LOGWRITE
+#endif
+
+#endif // CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
+
+
+
+/** \file
* Wrapper for UNCAUGHT_EXCEPTIONS configuration option
*
* For some functionality, Catch2 requires to know whether there is
@@ -7481,8 +8064,8 @@ namespace Catch {
* in C++17, we want to use `std::uncaught_exceptions` if possible.
*/
-#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
-#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
+#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
#if defined(_MSC_VER)
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
@@ -7508,71 +8091,7 @@ namespace Catch {
#endif
-#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
-
-
-#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED
-#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED
-
-
-namespace Catch {
-
- struct Colour {
- enum Code {
- None = 0,
-
- White,
- Red,
- Green,
- Blue,
- Cyan,
- Yellow,
- Grey,
-
- Bright = 0x10,
-
- BrightRed = Bright | Red,
- BrightGreen = Bright | Green,
- LightGrey = Bright | Grey,
- BrightWhite = Bright | White,
- BrightYellow = Bright | Yellow,
-
- // By intention
- FileName = LightGrey,
- Warning = BrightYellow,
- ResultError = BrightRed,
- ResultSuccess = BrightGreen,
- ResultExpectedFailure = Warning,
-
- Error = BrightRed,
- Success = Green,
-
- OriginalExpression = Cyan,
- ReconstructedExpression = BrightYellow,
-
- SecondaryText = LightGrey,
- Headers = White
- };
-
- // Use constructed object for RAII guard
- Colour( Code _colourCode );
- Colour( Colour&& other ) noexcept;
- Colour& operator=( Colour&& other ) noexcept;
- ~Colour();
-
- // Use static method for one-shot changes
- static void use( Code _colourCode );
-
- private:
- bool m_moved = false;
-
- friend std::ostream& operator << (std::ostream& os, Colour const&);
- };
-
-
-} // end namespace Catch
-
-#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
#ifndef CATCH_CONSOLE_WIDTH_HPP_INCLUDED
@@ -7584,10 +8103,13 @@ namespace Catch {
#endif // CATCH_CONSOLE_WIDTH_HPP_INCLUDED
+
#ifndef CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED
#define CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED
+#include <cstddef>
+#include <initializer_list>
// We want a simple polyfill over `std::empty`, `std::size` and so on
// for C++14 or C++ libraries with incomplete support.
@@ -7674,7 +8196,7 @@ namespace Catch {
#if defined(__i386__) || defined(__x86_64__)
#define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
#elif defined(__aarch64__)
- #define CATCH_TRAP() __asm__(".inst 0xd4200000")
+ #define CATCH_TRAP() __asm__(".inst 0xd43e0000")
#endif
#elif defined(CATCH_PLATFORM_IPHONE)
@@ -7719,6 +8241,52 @@ namespace Catch {
#endif // CATCH_DEBUGGER_HPP_INCLUDED
+#ifndef CATCH_ENFORCE_HPP_INCLUDED
+#define CATCH_ENFORCE_HPP_INCLUDED
+
+
+#include <exception>
+
+namespace Catch {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ template <typename Ex>
+ [[noreturn]]
+ void throw_exception(Ex const& e) {
+ throw e;
+ }
+#else // ^^ Exceptions are enabled // Exceptions are disabled vv
+ [[noreturn]]
+ void throw_exception(std::exception const& e);
+#endif
+
+ [[noreturn]]
+ void throw_logic_error(std::string const& msg);
+ [[noreturn]]
+ void throw_domain_error(std::string const& msg);
+ [[noreturn]]
+ void throw_runtime_error(std::string const& msg);
+
+} // namespace Catch;
+
+#define CATCH_MAKE_MSG(...) \
+ (Catch::ReusableStringStream() << __VA_ARGS__).str()
+
+#define CATCH_INTERNAL_ERROR(...) \
+ Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
+
+#define CATCH_ERROR(...) \
+ Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_RUNTIME_ERROR(...) \
+ Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_ENFORCE( condition, ... ) \
+ do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+
+
+#endif // CATCH_ENFORCE_HPP_INCLUDED
+
+
#ifndef CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#define CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
@@ -7772,6 +8340,7 @@ namespace Catch {
#ifndef CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
#define CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
#include <vector>
#include <string>
@@ -7779,8 +8348,8 @@ namespace Catch {
class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
public:
- ~ExceptionTranslatorRegistry();
- virtual void registerTranslator( const IExceptionTranslator* translator );
+ ~ExceptionTranslatorRegistry() override;
+ void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
std::string translateActiveException() const override;
std::string tryTranslators() const;
@@ -7796,91 +8365,217 @@ namespace Catch {
#define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
+#include <cassert>
-#ifndef CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
-#define CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
+namespace Catch {
+ /**
+ * Wrapper for platform-specific fatal error (signals/SEH) handlers
+ *
+ * Tries to be cooperative with other handlers, and not step over
+ * other handlers. This means that unknown structured exceptions
+ * are passed on, previous signal handlers are called, and so on.
+ *
+ * Can only be instantiated once, and assumes that once a signal
+ * is caught, the binary will end up terminating. Thus, there
+ */
+ class FatalConditionHandler {
+ bool m_started = false;
+
+ // Install/disengage implementation for specific platform.
+ // Should be if-defed to work on current platform, can assume
+ // engage-disengage 1:1 pairing.
+ void engage_platform();
+ void disengage_platform() noexcept;
+ public:
+ // Should also have platform-specific implementations as needed
+ FatalConditionHandler();
+ ~FatalConditionHandler();
-#if defined(CATCH_PLATFORM_WINDOWS)
+ void engage() {
+ assert(!m_started && "Handler cannot be installed twice.");
+ m_started = true;
+ engage_platform();
+ }
-#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
-# define CATCH_DEFINED_NOMINMAX
-# define NOMINMAX
-#endif
-#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
-# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-#endif
+ void disengage() noexcept {
+ assert(m_started && "Handler cannot be uninstalled without being installed first");
+ m_started = false;
+ disengage_platform();
+ }
+ };
-#ifdef __AFXDLL
-#include <AfxWin.h>
-#else
-#include <windows.h>
-#endif
+ //! Simple RAII guard for (dis)engaging the FatalConditionHandler
+ class FatalConditionHandlerGuard {
+ FatalConditionHandler* m_handler;
+ public:
+ FatalConditionHandlerGuard(FatalConditionHandler* handler):
+ m_handler(handler) {
+ m_handler->engage();
+ }
+ ~FatalConditionHandlerGuard() {
+ m_handler->disengage();
+ }
+ };
-#ifdef CATCH_DEFINED_NOMINMAX
-# undef NOMINMAX
-#endif
-#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
-# undef WIN32_LEAN_AND_MEAN
-#endif
+} // end namespace Catch
-#endif // defined(CATCH_PLATFORM_WINDOWS)
+#endif // CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
-#endif // CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
+
+#ifndef CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
+#define CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
-#if defined( CATCH_CONFIG_WINDOWS_SEH )
+
+#ifndef CATCH_POLYFILLS_HPP_INCLUDED
+#define CATCH_POLYFILLS_HPP_INCLUDED
namespace Catch {
+ bool isnan(float f);
+ bool isnan(double d);
+}
- struct FatalConditionHandler {
+#endif // CATCH_POLYFILLS_HPP_INCLUDED
- static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
- FatalConditionHandler();
- static void reset();
- ~FatalConditionHandler() { reset(); }
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <utility>
+#include <limits>
- private:
- static bool isSet;
- static ULONG guaranteeSize;
- static PVOID exceptionHandlerHandle;
- };
+namespace Catch {
+ namespace Detail {
-} // namespace Catch
+ uint32_t convertToBits(float f);
+ uint64_t convertToBits(double d);
-#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
+ } // end namespace Detail
-#include <signal.h>
-namespace Catch {
- struct FatalConditionHandler {
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic push
+ // We do a bunch of direct compensations of floating point numbers,
+ // because we know what we are doing and actually do want the direct
+ // comparison behaviour.
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+
+ /**
+ * Calculates the ULP distance between two floating point numbers
+ *
+ * The ULP distance of two floating point numbers is the count of
+ * valid floating point numbers representable between them.
+ *
+ * There are some exceptions between how this function counts the
+ * distance, and the interpretation of the standard as implemented.
+ * by e.g. `nextafter`. For this function it always holds that:
+ * * `(x == y) => ulpDistance(x, y) == 0` (so `ulpDistance(-0, 0) == 0`)
+ * * `ulpDistance(maxFinite, INF) == 1`
+ * * `ulpDistance(x, -x) == 2 * ulpDistance(x, 0)`
+ *
+ * \pre `!isnan( lhs )`
+ * \pre `!isnan( rhs )`
+ * \pre floating point numbers are represented in IEEE-754 format
+ */
+ template <typename FP>
+ uint64_t ulpDistance( FP lhs, FP rhs ) {
+ assert( std::numeric_limits<FP>::is_iec559 &&
+ "ulpDistance assumes IEEE-754 format for floating point types" );
+ assert( !Catch::isnan( lhs ) &&
+ "Distance between NaN and number is not meaningful" );
+ assert( !Catch::isnan( rhs ) &&
+ "Distance between NaN and number is not meaningful" );
+
+ // We want X == Y to imply 0 ULP distance even if X and Y aren't
+ // bit-equal (-0 and 0), or X - Y != 0 (same sign infinities).
+ if ( lhs == rhs ) { return 0; }
+
+ // We need a properly typed positive zero for type inference.
+ static constexpr FP positive_zero{};
+
+ // We want to ensure that +/- 0 is always represented as positive zero
+ if ( lhs == positive_zero ) { lhs = positive_zero; }
+ if ( rhs == positive_zero ) { rhs = positive_zero; }
+
+ // If arguments have different signs, we can handle them by summing
+ // how far are they from 0 each.
+ if ( std::signbit( lhs ) != std::signbit( rhs ) ) {
+ return ulpDistance( std::abs( lhs ), positive_zero ) +
+ ulpDistance( std::abs( rhs ), positive_zero );
+ }
+
+ // When both lhs and rhs are of the same sign, we can just
+ // read the numbers bitwise as integers, and then subtract them
+ // (assuming IEEE).
+ uint64_t lc = Detail::convertToBits( lhs );
+ uint64_t rc = Detail::convertToBits( rhs );
+
+ // The ulp distance between two numbers is symmetric, so to avoid
+ // dealing with overflows we want the bigger converted number on the lhs
+ if ( lc < rc ) {
+ std::swap( lc, rc );
+ }
+
+ return lc - rc;
+ }
+
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic pop
+#endif
- static bool isSet;
- static struct sigaction oldSigActions[];
- static stack_t oldSigStack;
- static char altStackMem[];
- static void handleSignal( int sig );
+} // end namespace Catch
- FatalConditionHandler();
- ~FatalConditionHandler() { reset(); }
- static void reset();
- };
+#endif // CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
-} // namespace Catch
+#ifndef CATCH_ISTREAM_HPP_INCLUDED
+#define CATCH_ISTREAM_HPP_INCLUDED
-#else
+
+#include <iosfwd>
+#include <cstddef>
+#include <ostream>
+#include <string>
namespace Catch {
- struct FatalConditionHandler {};
-}
-#endif
+ class IStream {
+ public:
+ virtual ~IStream(); // = default
+ virtual std::ostream& stream() = 0;
+ /**
+ * Best guess on whether the instance is writing to a console (e.g. via stdout/stderr)
+ *
+ * This is useful for e.g. Win32 colour support, because the Win32
+ * API manipulates console directly, unlike POSIX escape codes,
+ * that can be written anywhere.
+ *
+ * Due to variety of ways to change where the stdout/stderr is
+ * _actually_ being written, users should always assume that
+ * the answer might be wrong.
+ */
+ virtual bool isConsole() const { return false; }
+ };
-#endif // CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
+ /**
+ * Creates a stream wrapper that writes to specific file.
+ *
+ * Also recognizes 4 special filenames
+ * * `-` for stdout
+ * * `%stdout` for stdout
+ * * `%stderr` for stderr
+ * * `%debug` for platform specific debugging output
+ *
+ * \throws if passed an unrecognized %-prefixed stream
+ */
+ auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream>;
+
+}
+
+#endif // CATCH_STREAM_HPP_INCLUDED
#ifndef CATCH_LEAK_DETECTOR_HPP_INCLUDED
@@ -7907,13 +8602,17 @@ namespace Catch {
namespace Catch {
- struct IStreamingReporter;
+ class IEventListener;
class Config;
struct ReporterDescription {
std::string name, description;
};
+ struct ListenerDescription {
+ StringRef name;
+ std::string description;
+ };
struct TagInfo {
void add(StringRef spelling);
@@ -7923,81 +8622,13 @@ namespace Catch {
std::size_t count = 0;
};
- bool list( IStreamingReporter& reporter, Config const& config );
+ bool list( IEventListener& reporter, Config const& config );
} // end namespace Catch
#endif // CATCH_LIST_HPP_INCLUDED
-#ifndef CATCH_OPTION_HPP_INCLUDED
-#define CATCH_OPTION_HPP_INCLUDED
-
-namespace Catch {
-
- // An optional type
- template<typename T>
- class Option {
- public:
- Option() : nullableValue( nullptr ) {}
- Option( T const& _value )
- : nullableValue( new( storage ) T( _value ) )
- {}
- Option( Option const& _other )
- : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
- {}
-
- ~Option() {
- reset();
- }
-
- Option& operator= ( Option const& _other ) {
- if( &_other != this ) {
- reset();
- if( _other )
- nullableValue = new( storage ) T( *_other );
- }
- return *this;
- }
- Option& operator = ( T const& _value ) {
- reset();
- nullableValue = new( storage ) T( _value );
- return *this;
- }
-
- void reset() {
- if( nullableValue )
- nullableValue->~T();
- nullableValue = nullptr;
- }
-
- T& operator*() { return *nullableValue; }
- T const& operator*() const { return *nullableValue; }
- T* operator->() { return nullableValue; }
- const T* operator->() const { return nullableValue; }
-
- T valueOr( T const& defaultValue ) const {
- return nullableValue ? *nullableValue : defaultValue;
- }
-
- bool some() const { return nullableValue != nullptr; }
- bool none() const { return nullableValue == nullptr; }
-
- bool operator !() const { return nullableValue == nullptr; }
- explicit operator bool() const {
- return some();
- }
-
- private:
- T *nullableValue;
- alignas(alignof(T)) char storage[sizeof(T)];
- };
-
-} // end namespace Catch
-
-#endif // CATCH_OPTION_HPP_INCLUDED
-
-
#ifndef CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
#define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
@@ -8108,17 +8739,6 @@ namespace Catch {
#endif // CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
-#ifndef CATCH_POLYFILLS_HPP_INCLUDED
-#define CATCH_POLYFILLS_HPP_INCLUDED
-
-namespace Catch {
- bool isnan(float f);
- bool isnan(double d);
-}
-
-#endif // CATCH_POLYFILLS_HPP_INCLUDED
-
-
#ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED
#define CATCH_REPORTER_REGISTRY_HPP_INCLUDED
@@ -8133,10 +8753,10 @@ namespace Catch {
ReporterRegistry();
~ReporterRegistry() override; // = default, out of line to allow fwd decl
- IStreamingReporterPtr create( std::string const& name, IConfig const* config ) const override;
+ IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override;
void registerReporter( std::string const& name, IReporterFactoryPtr factory );
- void registerListener( IReporterFactoryPtr factory );
+ void registerListener( Detail::unique_ptr<EventListenerFactory> factory );
FactoryMap const& getFactories() const override;
Listeners const& getListeners() const override;
@@ -8161,7 +8781,6 @@ namespace Catch {
#include <string>
#include <vector>
-#include <memory>
namespace Catch {
namespace TestCaseTracking {
@@ -8179,19 +8798,31 @@ namespace TestCaseTracking {
class ITracker;
- using ITrackerPtr = std::shared_ptr<ITracker>;
+ using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>;
- class ITracker {
+ class ITracker {
NameAndLocation m_nameAndLocation;
using Children = std::vector<ITrackerPtr>;
protected:
+ enum CycleState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ NeedsAnotherRun,
+ CompletedSuccessfully,
+ Failed
+ };
+
+ ITracker* m_parent = nullptr;
Children m_children;
+ CycleState m_runState = NotStarted;
public:
- ITracker(NameAndLocation const& nameAndLoc) :
- m_nameAndLocation(nameAndLoc)
+ ITracker( NameAndLocation const& nameAndLoc, ITracker* parent ):
+ m_nameAndLocation( nameAndLoc ),
+ m_parent( parent )
{}
@@ -8199,42 +8830,60 @@ namespace TestCaseTracking {
NameAndLocation const& nameAndLocation() const {
return m_nameAndLocation;
}
+ ITracker* parent() const {
+ return m_parent;
+ }
- virtual ~ITracker();
+ virtual ~ITracker(); // = default
// dynamic queries
- virtual bool isComplete() const = 0; // Successfully completed or failed
- virtual bool isSuccessfullyCompleted() const = 0;
- virtual bool isOpen() const = 0; // Started but not complete
- virtual bool hasStarted() const = 0;
- virtual ITracker& parent() = 0;
+ //! Returns true if tracker run to completion (successfully or not)
+ virtual bool isComplete() const = 0;
+ //! Returns true if tracker run to completion succesfully
+ bool isSuccessfullyCompleted() const;
+ //! Returns true if tracker has started but hasn't been completed
+ bool isOpen() const;
+ //! Returns true iff tracker has started
+ bool hasStarted() const;
// actions
virtual void close() = 0; // Successfully complete
virtual void fail() = 0;
- virtual void markAsNeedingAnotherRun() = 0;
+ void markAsNeedingAnotherRun();
//! Register a nested ITracker
- void addChild( ITrackerPtr const& child );
+ void addChild( ITrackerPtr&& child );
/**
* Returns ptr to specific child if register with this tracker.
*
* Returns nullptr if not found.
*/
- ITrackerPtr findChild( NameAndLocation const& nameAndLocation );
+ ITracker* findChild( NameAndLocation const& nameAndLocation );
//! Have any children been added?
bool hasChildren() const {
return !m_children.empty();
}
- virtual void openChild() = 0;
+ //! Marks tracker as executing a child, doing se recursively up the tree
+ void openChild();
- // Debug/ checking
- virtual bool isSectionTracker() const = 0;
- virtual bool isGeneratorTracker() const = 0;
+ /**
+ * Returns true if the instance is a section tracker
+ *
+ * Subclasses should override to true if they are, replaces RTTI
+ * for internal debug checks.
+ */
+ virtual bool isSectionTracker() const;
+ /**
+ * Returns true if the instance is a generator tracker
+ *
+ * Subclasses should override to true if they are, replaces RTTI
+ * for internal debug checks.
+ */
+ virtual bool isGeneratorTracker() const;
};
class TrackerContext {
@@ -8264,41 +8913,18 @@ namespace TestCaseTracking {
class TrackerBase : public ITracker {
protected:
- enum CycleState {
- NotStarted,
- Executing,
- ExecutingChildren,
- NeedsAnotherRun,
- CompletedSuccessfully,
- Failed
- };
TrackerContext& m_ctx;
- ITracker* m_parent;
- CycleState m_runState = NotStarted;
public:
TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
bool isComplete() const override;
- bool isSuccessfullyCompleted() const override;
- bool isOpen() const override;
- bool hasStarted() const override {
- return m_runState != NotStarted;
- }
-
- ITracker& parent() override;
-
- void openChild() override;
-
- bool isSectionTracker() const override;
- bool isGeneratorTracker() const override;
void open();
void close() override;
void fail() override;
- void markAsNeedingAnotherRun() override;
private:
void moveToParent();
@@ -8306,7 +8932,7 @@ namespace TestCaseTracking {
};
class SectionTracker : public TrackerBase {
- std::vector<std::string> m_filters;
+ std::vector<StringRef> m_filters;
std::string m_trimmed_name;
public:
SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
@@ -8320,7 +8946,11 @@ namespace TestCaseTracking {
void tryOpen();
void addInitialFilters( std::vector<std::string> const& filters );
- void addNextFilters( std::vector<std::string> const& filters );
+ void addNextFilters( std::vector<StringRef> const& filters );
+ //! Returns filters active in this tracker
+ std::vector<StringRef> const& getFilters() const;
+ //! Returns whitespace-trimmed name of the tracked section
+ StringRef trimmedName() const;
};
} // namespace TestCaseTracking
@@ -8337,25 +8967,22 @@ using TestCaseTracking::SectionTracker;
namespace Catch {
- struct IMutableContext;
- struct IGeneratorTracker;
- struct IConfig;
+ class IMutableContext;
+ class IGeneratorTracker;
+ class IConfig;
///////////////////////////////////////////////////////////////////////////
- class RunContext : public IResultCapture, public IRunner {
+ class RunContext : public IResultCapture {
public:
RunContext( RunContext const& ) = delete;
RunContext& operator =( RunContext const& ) = delete;
- explicit RunContext( IConfig const* _config, IStreamingReporterPtr&& reporter );
+ explicit RunContext( IConfig const* _config, IEventListenerPtr&& reporter );
~RunContext() override;
- void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount );
- void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
-
Totals runTest(TestCaseHandle const& testCase);
public: // IResultCapture
@@ -8368,7 +8995,7 @@ namespace Catch {
void handleMessage
( AssertionInfo const& info,
ResultWas::OfType resultType,
- StringRef const& message,
+ StringRef message,
AssertionReaction& reaction ) override;
void handleUnexpectedExceptionNotThrown
( AssertionInfo const& info,
@@ -8391,10 +9018,10 @@ namespace Catch {
auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
- void benchmarkPreparing( std::string const& name ) override;
+ void benchmarkPreparing( StringRef name ) override;
void benchmarkStarting( BenchmarkInfo const& info ) override;
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
- void benchmarkFailed( std::string const& error ) override;
+ void benchmarkFailed( StringRef error ) override;
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;
@@ -8415,7 +9042,7 @@ namespace Catch {
public:
// !TBD We need to do this another way!
- bool aborting() const override;
+ bool aborting() const;
private:
@@ -8442,17 +9069,18 @@ namespace Catch {
IMutableContext& m_context;
TestCaseHandle const* m_activeTestCase = nullptr;
ITracker* m_testCaseTracker = nullptr;
- Option<AssertionResult> m_lastResult;
+ Optional<AssertionResult> m_lastResult;
IConfig const* m_config;
Totals m_totals;
- IStreamingReporterPtr m_reporter;
+ IEventListenerPtr m_reporter;
std::vector<MessageInfo> m_messages;
std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
AssertionInfo m_lastAssertionInfo;
std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext;
+ FatalConditionHandler m_fatalConditionhandler;
bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true;
bool m_includeSuccessfulResults;
@@ -8465,13 +9093,48 @@ namespace Catch {
#endif // CATCH_RUN_CONTEXT_HPP_INCLUDED
+#ifndef CATCH_SHARDING_HPP_INCLUDED
+#define CATCH_SHARDING_HPP_INCLUDED
+
+
+#include <cmath>
+
+namespace Catch {
+
+ template<typename Container>
+ Container createShard(Container const& container, std::size_t const shardCount, std::size_t const shardIndex) {
+ assert(shardCount > shardIndex);
+
+ if (shardCount == 1) {
+ return container;
+ }
+
+ const std::size_t totalTestCount = container.size();
+
+ const std::size_t shardSize = totalTestCount / shardCount;
+ const std::size_t leftoverTests = totalTestCount % shardCount;
+
+ const std::size_t startIndex = shardIndex * shardSize + (std::min)(shardIndex, leftoverTests);
+ const std::size_t endIndex = (shardIndex + 1) * shardSize + (std::min)(shardIndex + 1, leftoverTests);
+
+ auto startIterator = std::next(container.begin(), static_cast<std::ptrdiff_t>(startIndex));
+ auto endIterator = std::next(container.begin(), static_cast<std::ptrdiff_t>(endIndex));
+
+ return Container(startIterator, endIterator);
+ }
+
+}
+
+#endif // CATCH_SHARDING_HPP_INCLUDED
+
+
#ifndef CATCH_SINGLETONS_HPP_INCLUDED
#define CATCH_SINGLETONS_HPP_INCLUDED
namespace Catch {
struct ISingleton {
- virtual ~ISingleton();
+ virtual ~ISingleton(); // = default
};
@@ -8529,6 +9192,23 @@ namespace Catch {
#endif // CATCH_STARTUP_EXCEPTION_REGISTRY_HPP_INCLUDED
+
+#ifndef CATCH_STDSTREAMS_HPP_INCLUDED
+#define CATCH_STDSTREAMS_HPP_INCLUDED
+
+#include <iosfwd>
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+ std::ostream& clog();
+
+} // namespace Catch
+
+#endif
+
+
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
#define CATCH_STRING_MANIP_HPP_INCLUDED
@@ -8540,12 +9220,13 @@ namespace Catch {
namespace Catch {
bool startsWith( std::string const& s, std::string const& prefix );
- bool startsWith( std::string const& s, char prefix );
+ bool startsWith( StringRef s, char prefix );
bool endsWith( std::string const& s, std::string const& suffix );
bool endsWith( std::string const& s, char suffix );
bool contains( std::string const& s, std::string const& infix );
void toLowerInPlace( std::string& s );
std::string toLower( std::string const& s );
+ char toLower( char c );
//! Returns a new string without whitespace at the start/end
std::string trim( std::string const& str );
//! Returns a substring of the original ref without whitespace. Beware lifetimes!
@@ -8555,13 +9236,27 @@ namespace Catch {
std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
- struct pluralise {
- pluralise( std::size_t count, std::string const& label );
+ /**
+ * Helper for streaming a "count [maybe-plural-of-label]" human-friendly string
+ *
+ * Usage example:
+ * ```cpp
+ * std::cout << "Found " << pluralise(count, "error") << '\n';
+ * ```
+ *
+ * **Important:** The provided string must outlive the instance
+ */
+ class pluralise {
+ std::uint64_t m_count;
+ StringRef m_label;
- friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+ public:
+ constexpr pluralise(std::uint64_t count, StringRef label):
+ m_count(count),
+ m_label(label)
+ {}
- std::size_t m_count;
- std::string m_label;
+ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
};
}
@@ -8576,6 +9271,7 @@ namespace Catch {
#include <string>
namespace Catch {
+ struct SourceLineInfo;
class TagAliasRegistry : public ITagAliasRegistry {
public:
@@ -8593,6 +9289,30 @@ namespace Catch {
#endif // CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+#ifndef CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED
+#define CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED
+
+#include <cstdint>
+
+namespace Catch {
+
+ struct TestCaseInfo;
+
+ class TestCaseInfoHasher {
+ public:
+ using hash_t = std::uint64_t;
+ TestCaseInfoHasher( hash_t seed );
+ uint32_t operator()( TestCaseInfo const& t ) const;
+
+ private:
+ hash_t m_seed;
+ };
+
+} // namespace Catch
+
+#endif /* CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED */
+
+
#ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
#define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
@@ -8602,7 +9322,7 @@ namespace Catch {
namespace Catch {
class TestCaseHandle;
- struct IConfig;
+ class IConfig;
class TestSpec;
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases );
@@ -8649,9 +9369,6 @@ namespace Catch {
void invoke() const override;
};
-
- std::string extractClassName( StringRef const& classOrQualifiedMethodName );
-
///////////////////////////////////////////////////////////////////////////
@@ -8675,7 +9392,7 @@ namespace Catch {
namespace Catch {
- struct ITagAliasRegistry;
+ class ITagAliasRegistry;
class TestSpecParser {
enum Mode{ None, Name, QuotedName, Tag, EscapedName };
@@ -8749,31 +9466,49 @@ namespace Catch {
class Columns;
+ /**
+ * Represents a column of text with specific width and indentation
+ *
+ * When written out to a stream, it will perform linebreaking
+ * of the provided text so that the written lines fit within
+ * target width.
+ */
class Column {
+ // String to be written out
std::string m_string;
+ // Width of the column for linebreaking
size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1;
+ // Indentation of other lines (including first if initial indent is unset)
size_t m_indent = 0;
+ // Indentation of the first line
size_t m_initialIndent = std::string::npos;
public:
- class iterator {
+ /**
+ * Iterates "lines" in `Column` and return sthem
+ */
+ class const_iterator {
friend Column;
struct EndTag {};
Column const& m_column;
- size_t m_pos = 0;
-
- size_t m_len = 0;
- size_t m_end = 0;
- bool m_suffix = false;
-
- iterator( Column const& column, EndTag ):
- m_column( column ), m_pos( m_column.m_string.size() ) {}
-
+ // Where does the current line start?
+ size_t m_lineStart = 0;
+ // How long should the current line be?
+ size_t m_lineLength = 0;
+ // How far have we checked the string to iterate?
+ size_t m_parsedTo = 0;
+ // Should a '-' be appended to the line?
+ bool m_addHyphen = false;
+
+ const_iterator( Column const& column, EndTag ):
+ m_column( column ), m_lineStart( m_column.m_string.size() ) {}
+
+ // Calculates the length of the current line
void calcLength();
// Returns current indention width
- size_t indent() const;
+ size_t indentSize() const;
// Creates an indented and (optionally) suffixed string from
// current iterator position, indentation and length.
@@ -8787,21 +9522,21 @@ namespace Catch {
using reference = value_type&;
using iterator_category = std::forward_iterator_tag;
- explicit iterator( Column const& column );
+ explicit const_iterator( Column const& column );
std::string operator*() const;
- iterator& operator++();
- iterator operator++( int );
+ const_iterator& operator++();
+ const_iterator operator++( int );
- bool operator==( iterator const& other ) const {
- return m_pos == other.m_pos && &m_column == &other.m_column;
+ bool operator==( const_iterator const& other ) const {
+ return m_lineStart == other.m_lineStart && &m_column == &other.m_column;
}
- bool operator!=( iterator const& other ) const {
+ bool operator!=( const_iterator const& other ) const {
return !operator==( other );
}
};
- using const_iterator = iterator;
+ using iterator = const_iterator;
explicit Column( std::string const& text ): m_string( text ) {}
@@ -8820,8 +9555,8 @@ namespace Catch {
}
size_t width() const { return m_width; }
- iterator begin() const { return iterator( *this ); }
- iterator end() const { return { *this, iterator::EndTag{} }; }
+ const_iterator begin() const { return const_iterator( *this ); }
+ const_iterator end() const { return { *this, const_iterator::EndTag{} }; }
friend std::ostream& operator<<( std::ostream& os,
Column const& col );
@@ -8841,7 +9576,7 @@ namespace Catch {
struct EndTag {};
std::vector<Column> const& m_columns;
- std::vector<Column::iterator> m_iterators;
+ std::vector<Column::const_iterator> m_iterators;
size_t m_activeIterators;
iterator( Columns const& columns, EndTag );
@@ -8914,16 +9649,37 @@ namespace Catch {
#endif // CATCH_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
-#ifndef CATCH_XMLWRITER_HPP_INCLUDED
-#define CATCH_XMLWRITER_HPP_INCLUDED
+#ifndef CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
+#define CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
-// FixMe: Without this include (and something inside it), MSVC goes crazy
-// and reports that calls to XmlEncode's op << are ambiguous between
-// the declaration and definition.
-// It also has to be in the header.
+#if defined(CATCH_PLATFORM_WINDOWS)
+// We might end up with the define made globally through the compiler,
+// and we don't want to trigger warnings for this
+#if !defined(NOMINMAX)
+# define NOMINMAX
+#endif
+#if !defined(WIN32_LEAN_AND_MEAN)
+# define WIN32_LEAN_AND_MEAN
+#endif
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#endif // defined(CATCH_PLATFORM_WINDOWS)
+
+#endif // CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
+
+
+#ifndef CATCH_XMLWRITER_HPP_INCLUDED
+#define CATCH_XMLWRITER_HPP_INCLUDED
+
+
+#include <iosfwd>
#include <vector>
namespace Catch {
@@ -8936,18 +9692,24 @@ namespace Catch {
XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
+ /**
+ * Helper for XML-encoding text (escaping angle brackets, quotes, etc)
+ *
+ * Note: doesn't take ownership of passed strings, and thus the
+ * encoded string must outlive the encoding instance.
+ */
class XmlEncode {
public:
enum ForWhat { ForTextNodes, ForAttributes };
- XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
+ XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes );
void encodeTo( std::ostream& os ) const;
friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
private:
- std::string m_str;
+ StringRef m_str;
ForWhat m_forWhat;
};
@@ -8963,20 +9725,32 @@ namespace Catch {
~ScopedElement();
- ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
-
- template<typename T>
- ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+ ScopedElement&
+ writeText( StringRef text,
+ XmlFormatting fmt = XmlFormatting::Newline |
+ XmlFormatting::Indent );
+
+ ScopedElement& writeAttribute( StringRef name,
+ StringRef attribute );
+ template <typename T,
+ // Without this SFINAE, this overload is a better match
+ // for `std::string`, `char const*`, `char const[N]` args.
+ // While it would still work, it would cause code bloat
+ // and multiple iteration over the strings
+ typename = typename std::enable_if_t<
+ !std::is_convertible<T, StringRef>::value>>
+ ScopedElement& writeAttribute( StringRef name,
+ T const& attribute ) {
m_writer->writeAttribute( name, attribute );
return *this;
}
private:
- mutable XmlWriter* m_writer = nullptr;
+ XmlWriter* m_writer = nullptr;
XmlFormatting m_fmt;
};
- XmlWriter( std::ostream& os = Catch::cout() );
+ XmlWriter( std::ostream& os );
~XmlWriter();
XmlWriter( XmlWriter const& ) = delete;
@@ -8988,24 +9762,41 @@ namespace Catch {
XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
- XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
-
- XmlWriter& writeAttribute( std::string const& name, bool attribute );
-
- template<typename T>
- XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ //! The attribute content is XML-encoded
+ XmlWriter& writeAttribute( StringRef name, StringRef attribute );
+
+ //! Writes the attribute as "true/false"
+ XmlWriter& writeAttribute( StringRef name, bool attribute );
+
+ //! The attribute content is XML-encoded
+ XmlWriter& writeAttribute( StringRef name, char const* attribute );
+
+ //! The attribute value must provide op<<(ostream&, T). The resulting
+ //! serialization is XML-encoded
+ template <typename T,
+ // Without this SFINAE, this overload is a better match
+ // for `std::string`, `char const*`, `char const[N]` args.
+ // While it would still work, it would cause code bloat
+ // and multiple iteration over the strings
+ typename = typename std::enable_if_t<
+ !std::is_convertible<T, StringRef>::value>>
+ XmlWriter& writeAttribute( StringRef name, T const& attribute ) {
ReusableStringStream rss;
rss << attribute;
return writeAttribute( name, rss.str() );
}
- XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
-
- XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+ //! Writes escaped `text` in a element
+ XmlWriter& writeText( StringRef text,
+ XmlFormatting fmt = XmlFormatting::Newline |
+ XmlFormatting::Indent );
- void writeStylesheetRef( std::string const& url );
+ //! Writes XML comment as "<!-- text -->"
+ XmlWriter& writeComment( StringRef text,
+ XmlFormatting fmt = XmlFormatting::Newline |
+ XmlFormatting::Indent );
- XmlWriter& writeBlankLine();
+ void writeStylesheetRef( StringRef url );
void ensureTagClosed();
@@ -9063,35 +9854,32 @@ namespace Catch {
MatcherT const& m_matcher;
StringRef m_matcherString;
public:
- MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString )
+ MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef matcherString )
: ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose
- m_arg( std::forward<ArgT>(arg) ),
+ m_arg( CATCH_FORWARD(arg) ),
m_matcher( matcher ),
m_matcherString( matcherString )
{}
- void streamReconstructedExpression( std::ostream &os ) const override {
- auto matcherAsString = m_matcher.toString();
- os << Catch::Detail::stringify( m_arg ) << ' ';
- if( matcherAsString == Detail::unprintableString )
- os << m_matcherString;
- else
- os << matcherAsString;
+ void streamReconstructedExpression( std::ostream& os ) const override {
+ os << Catch::Detail::stringify( m_arg )
+ << ' '
+ << m_matcher.toString();
}
};
namespace Matchers {
template <typename ArgT>
- struct MatcherBase;
+ class MatcherBase;
}
using StringMatcher = Matchers::MatcherBase<std::string>;
- void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString );
+ void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString );
template<typename ArgT, typename MatcherT>
- auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT, MatcherT> {
- return MatchExpr<ArgT, MatcherT>( std::forward<ArgT>(arg), matcher, matcherString );
+ auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef matcherString ) -> MatchExpr<ArgT, MatcherT> {
+ return MatchExpr<ArgT, MatcherT>( CATCH_FORWARD(arg), matcher, matcherString );
}
} // namespace Catch
@@ -9155,27 +9943,20 @@ namespace Matchers {
mutable std::string m_cachedToString;
};
-#ifdef __clang__
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wnon-virtual-dtor"
-#endif
-
- template<typename ObjectT>
- struct MatcherMethod {
- virtual bool match(ObjectT const& arg) const = 0;
- };
-
-#ifdef __clang__
-# pragma clang diagnostic pop
-#endif
template<typename T>
- struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> {};
+ class MatcherBase : public MatcherUntypedBase {
+ public:
+ virtual bool match( T const& arg ) const = 0;
+ };
namespace Detail {
template<typename ArgT>
- struct MatchAllOf final : MatcherBase<ArgT> {
+ class MatchAllOf final : public MatcherBase<ArgT> {
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+
+ public:
MatchAllOf() = default;
MatchAllOf(MatchAllOf const&) = delete;
MatchAllOf& operator=(MatchAllOf const&) = delete;
@@ -9208,15 +9989,12 @@ namespace Matchers {
friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
lhs.m_matchers.push_back(&rhs);
- return std::move(lhs);
+ return CATCH_MOVE(lhs);
}
friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
- return std::move(rhs);
+ return CATCH_MOVE(rhs);
}
-
- private:
- std::vector<MatcherBase<ArgT> const*> m_matchers;
};
//! lvalue overload is intentionally deleted, users should
@@ -9229,7 +10007,9 @@ namespace Matchers {
MatchAllOf<ArgT> operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf<ArgT> const& rhs) = delete;
template<typename ArgT>
- struct MatchAnyOf final : MatcherBase<ArgT> {
+ class MatchAnyOf final : public MatcherBase<ArgT> {
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ public:
MatchAnyOf() = default;
MatchAnyOf(MatchAnyOf const&) = delete;
MatchAnyOf& operator=(MatchAnyOf const&) = delete;
@@ -9261,15 +10041,12 @@ namespace Matchers {
friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
lhs.m_matchers.push_back(&rhs);
- return std::move(lhs);
+ return CATCH_MOVE(lhs);
}
friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
- return std::move(rhs);
+ return CATCH_MOVE(rhs);
}
-
- private:
- std::vector<MatcherBase<ArgT> const*> m_matchers;
};
//! lvalue overload is intentionally deleted, users should
@@ -9282,8 +10059,10 @@ namespace Matchers {
MatchAnyOf<ArgT> operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf<ArgT> const& rhs) = delete;
template<typename ArgT>
- struct MatchNotOf final : MatcherBase<ArgT> {
+ class MatchNotOf final : public MatcherBase<ArgT> {
+ MatcherBase<ArgT> const& m_underlyingMatcher;
+ public:
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
m_underlyingMatcher( underlyingMatcher )
{}
@@ -9295,9 +10074,6 @@ namespace Matchers {
std::string describe() const override {
return "not " + m_underlyingMatcher.toString();
}
-
- private:
- MatcherBase<ArgT> const& m_underlyingMatcher;
};
} // namespace Detail
@@ -9382,13 +10158,13 @@ namespace Matchers {
#include <algorithm>
#include <string>
#include <type_traits>
-#include <utility>
namespace Catch {
namespace Matchers {
- struct MatcherGenericBase : MatcherUntypedBase {
+ class MatcherGenericBase : public MatcherUntypedBase {
+ public:
MatcherGenericBase() = default;
- virtual ~MatcherGenericBase(); // = default;
+ ~MatcherGenericBase() override; // = default;
MatcherGenericBase(MatcherGenericBase&) = default;
MatcherGenericBase(MatcherGenericBase&&) = default;
@@ -9422,11 +10198,11 @@ namespace Matchers {
return arr;
}
- #ifdef CATCH_CPP17_OR_GREATER
+#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510
using std::conjunction;
- #else // CATCH_CPP17_OR_GREATER
+#else // __cpp_lib_logical_traits
template<typename... Cond>
struct conjunction : std::true_type {};
@@ -9434,7 +10210,7 @@ namespace Matchers {
template<typename Cond, typename... Rest>
struct conjunction<Cond, Rest...> : std::integral_constant<bool, Cond::value && conjunction<Rest...>::value> {};
- #endif // CATCH_CPP17_OR_GREATER
+#endif // __cpp_lib_logical_traits
template<typename T>
using is_generic_matcher = std::is_base_of<
@@ -9486,7 +10262,8 @@ namespace Matchers {
template<typename... MatcherTs>
- struct MatchAllOfGeneric final : MatcherGenericBase {
+ class MatchAllOfGeneric final : public MatcherGenericBase {
+ public:
MatchAllOfGeneric(MatchAllOfGeneric const&) = delete;
MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete;
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
@@ -9504,7 +10281,11 @@ namespace Matchers {
return describe_multi_matcher<MatcherTs...>(" and "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
}
- std::array<void const*, sizeof...(MatcherTs)> m_matchers;
+ // Has to be public to enable the concatenating operators
+ // below, because they are not friend of the RHS, only LHS,
+ // and thus cannot access private fields of RHS
+ std::array<void const*, sizeof...( MatcherTs )> m_matchers;
+
//! Avoids type nesting for `GenericAllOf && GenericAllOf` case
template<typename... MatchersRHS>
@@ -9512,7 +10293,7 @@ namespace Matchers {
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs,
MatchAllOfGeneric<MatchersRHS...>&& rhs) {
- return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
+ return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
}
//! Avoids type nesting for `GenericAllOf && some matcher` case
@@ -9521,7 +10302,7 @@ namespace Matchers {
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs,
MatcherRHS const& rhs) {
- return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(&rhs))};
+ return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
}
//! Avoids type nesting for `some matcher && GenericAllOf` case
@@ -9530,13 +10311,14 @@ namespace Matchers {
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
MatcherLHS const& lhs,
MatchAllOfGeneric<MatcherTs...>&& rhs) {
- return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
+ return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
}
};
template<typename... MatcherTs>
- struct MatchAnyOfGeneric final : MatcherGenericBase {
+ class MatchAnyOfGeneric final : public MatcherGenericBase {
+ public:
MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete;
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete;
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
@@ -9554,14 +10336,18 @@ namespace Matchers {
return describe_multi_matcher<MatcherTs...>(" or "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
}
- std::array<void const*, sizeof...(MatcherTs)> m_matchers;
+
+ // Has to be public to enable the concatenating operators
+ // below, because they are not friend of the RHS, only LHS,
+ // and thus cannot access private fields of RHS
+ std::array<void const*, sizeof...( MatcherTs )> m_matchers;
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
template<typename... MatchersRHS>
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs,
MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
- return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
+ return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
}
//! Avoids type nesting for `GenericAnyOf || some matcher` case
@@ -9570,7 +10356,7 @@ namespace Matchers {
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs,
MatcherRHS const& rhs) {
- return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
+ return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
}
//! Avoids type nesting for `some matcher || GenericAnyOf` case
@@ -9579,13 +10365,16 @@ namespace Matchers {
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
MatcherLHS const& lhs,
MatchAnyOfGeneric<MatcherTs...>&& rhs) {
- return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
+ return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
}
};
template<typename MatcherT>
- struct MatchNotOfGeneric final : MatcherGenericBase {
+ class MatchNotOfGeneric final : public MatcherGenericBase {
+ MatcherT const& m_matcher;
+
+ public:
MatchNotOfGeneric(MatchNotOfGeneric const&) = delete;
MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete;
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
@@ -9606,8 +10395,6 @@ namespace Matchers {
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
return matcher.m_matcher;
}
- private:
- MatcherT const& m_matcher;
};
} // namespace Detail
@@ -9707,7 +10494,7 @@ namespace Catch {
Matcher m_matcher;
public:
explicit SizeMatchesMatcher(Matcher m):
- m_matcher(std::move(m))
+ m_matcher(CATCH_MOVE(m))
{}
template <typename RangeLike>
@@ -9733,7 +10520,7 @@ namespace Catch {
template <typename Matcher>
std::enable_if_t<Detail::is_matcher<Matcher>::value,
SizeMatchesMatcher<Matcher>> SizeIs(Matcher&& m) {
- return SizeMatchesMatcher<Matcher>{std::forward<Matcher>(m)};
+ return SizeMatchesMatcher<Matcher>{CATCH_FORWARD(m)};
}
} // end namespace Matchers
@@ -9748,7 +10535,6 @@ namespace Catch {
#include <algorithm>
#include <functional>
-#include <utility>
namespace Catch {
namespace Matchers {
@@ -9760,8 +10546,8 @@ namespace Catch {
public:
template <typename T2, typename Equality2>
ContainsElementMatcher(T2&& target, Equality2&& predicate):
- m_desired(std::forward<T2>(target)),
- m_eq(std::forward<Equality2>(predicate))
+ m_desired(CATCH_FORWARD(target)),
+ m_eq(CATCH_FORWARD(predicate))
{}
std::string describe() const override {
@@ -9788,12 +10574,11 @@ namespace Catch {
// constructor (and also avoid some perfect forwarding failure
// cases)
ContainsMatcherMatcher(Matcher matcher):
- m_matcher(std::move(matcher))
+ m_matcher(CATCH_MOVE(matcher))
{}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
- using std::begin; using std::endl;
for (auto&& elem : rng) {
if (m_matcher.match(elem)) {
return true;
@@ -9815,14 +10600,14 @@ namespace Catch {
template <typename T>
std::enable_if_t<!Detail::is_matcher<T>::value,
ContainsElementMatcher<T, std::equal_to<>>> Contains(T&& elem) {
- return { std::forward<T>(elem), std::equal_to<>{} };
+ return { CATCH_FORWARD(elem), std::equal_to<>{} };
}
//! Creates a matcher that checks whether a range contains element matching a matcher
template <typename Matcher>
std::enable_if_t<Detail::is_matcher<Matcher>::value,
ContainsMatcherMatcher<Matcher>> Contains(Matcher&& matcher) {
- return { std::forward<Matcher>(matcher) };
+ return { CATCH_FORWARD(matcher) };
}
/**
@@ -9832,7 +10617,7 @@ namespace Catch {
*/
template <typename T, typename Equality>
ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
- return { std::forward<T>(elem), std::forward<Equality>(eq) };
+ return { CATCH_FORWARD(elem), CATCH_FORWARD(eq) };
}
}
@@ -9870,8 +10655,8 @@ ExceptionMessageMatcher Message(std::string const& message);
#endif // CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
-#ifndef CATCH_MATCHERS_FLOATING_HPP_INCLUDED
-#define CATCH_MATCHERS_FLOATING_HPP_INCLUDED
+#ifndef CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
+#define CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
namespace Catch {
@@ -9881,7 +10666,8 @@ namespace Matchers {
enum class FloatingPointKind : uint8_t;
}
- struct WithinAbsMatcher final : MatcherBase<double> {
+ class WithinAbsMatcher final : public MatcherBase<double> {
+ public:
WithinAbsMatcher(double target, double margin);
bool match(double const& matchee) const override;
std::string describe() const override;
@@ -9890,8 +10676,11 @@ namespace Matchers {
double m_margin;
};
- struct WithinUlpsMatcher final : MatcherBase<double> {
- WithinUlpsMatcher(double target, uint64_t ulps, Detail::FloatingPointKind baseType);
+ class WithinUlpsMatcher final : public MatcherBase<double> {
+ public:
+ WithinUlpsMatcher( double target,
+ uint64_t ulps,
+ Detail::FloatingPointKind baseType );
bool match(double const& matchee) const override;
std::string describe() const override;
private:
@@ -9906,8 +10695,9 @@ namespace Matchers {
// |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
// the same result if we do this for floats, as if we do this for
// doubles that were promoted from floats.
- struct WithinRelMatcher final : MatcherBase<double> {
- WithinRelMatcher(double target, double epsilon);
+ class WithinRelMatcher final : public MatcherBase<double> {
+ public:
+ WithinRelMatcher( double target, double epsilon );
bool match(double const& matchee) const override;
std::string describe() const override;
private:
@@ -9934,7 +10724,7 @@ namespace Matchers {
} // namespace Matchers
} // namespace Catch
-#endif // CATCH_MATCHERS_FLOATING_HPP_INCLUDED
+#endif // CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
#ifndef CATCH_MATCHERS_PREDICATE_HPP_INCLUDED
@@ -9942,7 +10732,6 @@ namespace Matchers {
#include <string>
-#include <utility>
namespace Catch {
namespace Matchers {
@@ -9958,7 +10747,7 @@ class PredicateMatcher final : public MatcherBase<T> {
public:
PredicateMatcher(Predicate&& elem, std::string const& descr)
- :m_predicate(std::forward<Predicate>(elem)),
+ :m_predicate(CATCH_FORWARD(elem)),
m_description(Detail::finalizeDescription(descr))
{}
@@ -9980,7 +10769,7 @@ public:
PredicateMatcher<T, Pred> Predicate(Pred&& predicate, std::string const& description = "") {
static_assert(is_callable<Pred(T)>::value, "Predicate not callable with argument T");
static_assert(std::is_same<bool, FunctionReturnType<Pred, T>>::value, "Predicate does not return bool");
- return PredicateMatcher<T, Pred>(std::forward<Pred>(predicate), description);
+ return PredicateMatcher<T, Pred>(CATCH_FORWARD(predicate), description);
}
} // namespace Matchers
@@ -9989,6 +10778,107 @@ public:
#endif // CATCH_MATCHERS_PREDICATE_HPP_INCLUDED
+#ifndef CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
+#define CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
+
+
+namespace Catch {
+ namespace Matchers {
+ // Matcher for checking that all elements in range matches a given matcher.
+ template <typename Matcher>
+ class AllMatchMatcher final : public MatcherGenericBase {
+ Matcher m_matcher;
+ public:
+ AllMatchMatcher(Matcher matcher):
+ m_matcher(CATCH_MOVE(matcher))
+ {}
+
+ std::string describe() const override {
+ return "all match " + m_matcher.describe();
+ }
+
+ template <typename RangeLike>
+ bool match(RangeLike&& rng) const {
+ for (auto&& elem : rng) {
+ if (!m_matcher.match(elem)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ // Matcher for checking that no element in range matches a given matcher.
+ template <typename Matcher>
+ class NoneMatchMatcher final : public MatcherGenericBase {
+ Matcher m_matcher;
+ public:
+ NoneMatchMatcher(Matcher matcher):
+ m_matcher(CATCH_MOVE(matcher))
+ {}
+
+ std::string describe() const override {
+ return "none match " + m_matcher.describe();
+ }
+
+ template <typename RangeLike>
+ bool match(RangeLike&& rng) const {
+ for (auto&& elem : rng) {
+ if (m_matcher.match(elem)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ // Matcher for checking that at least one element in range matches a given matcher.
+ template <typename Matcher>
+ class AnyMatchMatcher final : public MatcherGenericBase {
+ Matcher m_matcher;
+ public:
+ AnyMatchMatcher(Matcher matcher):
+ m_matcher(CATCH_MOVE(matcher))
+ {}
+
+ std::string describe() const override {
+ return "any match " + m_matcher.describe();
+ }
+
+ template <typename RangeLike>
+ bool match(RangeLike&& rng) const {
+ for (auto&& elem : rng) {
+ if (m_matcher.match(elem)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // Creates a matcher that checks whether a range contains element matching a matcher
+ template <typename Matcher>
+ AllMatchMatcher<Matcher> AllMatch(Matcher&& matcher) {
+ return { CATCH_FORWARD(matcher) };
+ }
+
+ // Creates a matcher that checks whether no element in a range matches a matcher.
+ template <typename Matcher>
+ NoneMatchMatcher<Matcher> NoneMatch(Matcher&& matcher) {
+ return { CATCH_FORWARD(matcher) };
+ }
+
+ // Creates a matcher that checks whether any element in a range matches a matcher.
+ template <typename Matcher>
+ AnyMatchMatcher<Matcher> AnyMatch(Matcher&& matcher) {
+ return { CATCH_FORWARD(matcher) };
+ }
+ }
+}
+
+#endif // CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
+
+
#ifndef CATCH_MATCHERS_STRING_HPP_INCLUDED
#define CATCH_MATCHERS_STRING_HPP_INCLUDED
@@ -10007,45 +10897,52 @@ namespace Matchers {
std::string m_str;
};
- struct StringMatcherBase : MatcherBase<std::string> {
- StringMatcherBase( std::string const& operation, CasedString const& comparator );
- std::string describe() const override;
-
+ class StringMatcherBase : public MatcherBase<std::string> {
+ protected:
CasedString m_comparator;
- std::string m_operation;
+ StringRef m_operation;
+
+ public:
+ StringMatcherBase( StringRef operation,
+ CasedString const& comparator );
+ std::string describe() const override;
};
- struct StringEqualsMatcher final : StringMatcherBase {
+ class StringEqualsMatcher final : public StringMatcherBase {
+ public:
StringEqualsMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
- struct StringContainsMatcher final : StringMatcherBase {
+ class StringContainsMatcher final : public StringMatcherBase {
+ public:
StringContainsMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
- struct StartsWithMatcher final : StringMatcherBase {
+ class StartsWithMatcher final : public StringMatcherBase {
+ public:
StartsWithMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
- struct EndsWithMatcher final : StringMatcherBase {
+ class EndsWithMatcher final : public StringMatcherBase {
+ public:
EndsWithMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
- struct RegexMatcher final : MatcherBase<std::string> {
+ class RegexMatcher final : public MatcherBase<std::string> {
+ std::string m_regex;
+ CaseSensitive m_caseSensitivity;
+
+ public:
RegexMatcher( std::string regex, CaseSensitive caseSensitivity );
bool match( std::string const& matchee ) const override;
std::string describe() const override;
-
- private:
- std::string m_regex;
- CaseSensitive m_caseSensitivity;
};
//! Creates matcher that accepts strings that are exactly equal to `str`
StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
//! Creates matcher that accepts strings that contain `str`
- StringContainsMatcher Contains( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
+ StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
//! Creates matcher that accepts strings that _end_ with `str`
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
//! Creates matcher that accepts strings that _start_ with `str`
@@ -10069,8 +10966,10 @@ namespace Catch {
namespace Matchers {
template<typename T, typename Alloc>
- struct VectorContainsElementMatcher final : MatcherBase<std::vector<T, Alloc>> {
+ class VectorContainsElementMatcher final : public MatcherBase<std::vector<T, Alloc>> {
+ T const& m_comparator;
+ public:
VectorContainsElementMatcher(T const& comparator):
m_comparator(comparator)
{}
@@ -10087,13 +10986,13 @@ namespace Matchers {
std::string describe() const override {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
-
- T const& m_comparator;
};
template<typename T, typename AllocComp, typename AllocMatch>
- struct ContainsMatcher final : MatcherBase<std::vector<T, AllocMatch>> {
+ class ContainsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
+ std::vector<T, AllocComp> const& m_comparator;
+ public:
ContainsMatcher(std::vector<T, AllocComp> const& comparator):
m_comparator( comparator )
{}
@@ -10119,13 +11018,13 @@ namespace Matchers {
std::string describe() const override {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
-
- std::vector<T, AllocComp> const& m_comparator;
};
template<typename T, typename AllocComp, typename AllocMatch>
- struct EqualsMatcher final : MatcherBase<std::vector<T, AllocMatch>> {
+ class EqualsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
+ std::vector<T, AllocComp> const& m_comparator;
+ public:
EqualsMatcher(std::vector<T, AllocComp> const& comparator):
m_comparator( comparator )
{}
@@ -10145,12 +11044,14 @@ namespace Matchers {
std::string describe() const override {
return "Equals: " + ::Catch::Detail::stringify( m_comparator );
}
- std::vector<T, AllocComp> const& m_comparator;
};
template<typename T, typename AllocComp, typename AllocMatch>
- struct ApproxMatcher final : MatcherBase<std::vector<T, AllocMatch>> {
+ class ApproxMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
+ std::vector<T, AllocComp> const& m_comparator;
+ mutable Catch::Approx approx = Catch::Approx::custom();
+ public:
ApproxMatcher(std::vector<T, AllocComp> const& comparator):
m_comparator( comparator )
{}
@@ -10181,13 +11082,13 @@ namespace Matchers {
approx.scale(static_cast<double>(newScale));
return *this;
}
-
- std::vector<T, AllocComp> const& m_comparator;
- mutable Catch::Approx approx = Catch::Approx::custom();
};
template<typename T, typename AllocComp, typename AllocMatch>
- struct UnorderedEqualsMatcher final : MatcherBase<std::vector<T, AllocMatch>> {
+ class UnorderedEqualsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
+ std::vector<T, AllocComp> const& m_target;
+
+ public:
UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target):
m_target(target)
{}
@@ -10201,15 +11102,12 @@ namespace Matchers {
std::string describe() const override {
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
}
- private:
- std::vector<T, AllocComp> const& m_target;
};
// The following functions create the actual matcher objects.
// This allows the types to be inferred
-
//! Creates a matcher that matches vectors that contain all elements in `comparator`
template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
@@ -10276,58 +11174,116 @@ namespace Matchers {
-#include <iosfwd>
+#ifndef CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
+#define CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
+
+
+#include <map>
#include <string>
-#include <vector>
namespace Catch {
+ class ColourImpl;
- template<typename T>
- struct LazyStat : Option<T> {
- LazyStat& operator=(T const& _value) {
- Option<T>::operator=(_value);
- used = false;
- return *this;
- }
- void reset() {
- Option<T>::reset();
- used = false;
- }
- bool used = false;
- };
+ /**
+ * This is the base class for all reporters.
+ *
+ * If are writing a reporter, you must derive from this type, or one
+ * of the helper reporter bases that are derived from this type.
+ *
+ * ReporterBase centralizes handling of various common tasks in reporters,
+ * like storing the right stream for the reporters to write to, and
+ * providing the default implementation of the different listing events.
+ */
+ class ReporterBase : public IEventListener {
+ protected:
+ //! The stream wrapper as passed to us by outside code
+ Detail::unique_ptr<IStream> m_wrapped_stream;
+ //! Cached output stream from `m_wrapped_stream` to reduce
+ //! number of indirect calls needed to write output.
+ std::ostream& m_stream;
+ //! Colour implementation this reporter was configured for
+ Detail::unique_ptr<ColourImpl> m_colour;
+ //! The custom reporter options user passed down to the reporter
+ std::map<std::string, std::string> m_customOptions;
+ public:
+ ReporterBase( ReporterConfig&& config );
+ ~ReporterBase() override; // = default;
+
+ /**
+ * Provides a simple default listing of reporters.
+ *
+ * Should look roughly like the reporter listing in v2 and earlier
+ * versions of Catch2.
+ */
+ void listReporters(
+ std::vector<ReporterDescription> const& descriptions ) override;
+ /**
+ * Provides a simple default listing of listeners
+ *
+ * Looks similarly to listing of reporters, but with listener type
+ * instead of reporter name.
+ */
+ void listListeners(
+ std::vector<ListenerDescription> const& descriptions ) override;
+ /**
+ * Provides a simple default listing of tests.
+ *
+ * Should look roughly like the test listing in v2 and earlier versions
+ * of Catch2. Especially supports low-verbosity listing that mimics the
+ * old `--list-test-names-only` output.
+ */
+ void listTests( std::vector<TestCaseHandle> const& tests ) override;
+ /**
+ * Provides a simple default listing of tags.
+ *
+ * Should look roughly like the tag listing in v2 and earlier versions
+ * of Catch2.
+ */
+ void listTags( std::vector<TagInfo> const& tags ) override;
+ };
+} // namespace Catch
- struct StreamingReporterBase : IStreamingReporter {
+#endif // CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
- StreamingReporterBase( ReporterConfig const& _config ):
- m_config( _config.fullConfig() ), stream( _config.stream() ) {
- }
+#include <vector>
+namespace Catch {
+ class StreamingReporterBase : public ReporterBase {
+ public:
+ using ReporterBase::ReporterBase;
~StreamingReporterBase() override;
- void noMatchingTestCases(std::string const&) override {}
+ void benchmarkPreparing( StringRef ) override {}
+ void benchmarkStarting( BenchmarkInfo const& ) override {}
+ void benchmarkEnded( BenchmarkStats<> const& ) override {}
+ void benchmarkFailed( StringRef ) override {}
- void reportInvalidArguments(std::string const&) override {}
+ void fatalErrorEncountered( StringRef /*error*/ ) override {}
+ void noMatchingTestCases( StringRef /*unmatchedSpec*/ ) override {}
+ void reportInvalidTestSpec( StringRef /*invalidArgument*/ ) override {}
void testRunStarting( TestRunInfo const& _testRunInfo ) override;
- void testGroupStarting( GroupInfo const& _groupInfo ) override;
-
void testCaseStarting(TestCaseInfo const& _testInfo) override {
currentTestCaseInfo = &_testInfo;
}
+ void testCasePartialStarting( TestCaseInfo const&, uint64_t ) override {}
void sectionStarting(SectionInfo const& _sectionInfo) override {
m_sectionStack.push_back(_sectionInfo);
}
+ void assertionStarting( AssertionInfo const& ) override {}
+ void assertionEnded( AssertionStats const& ) override {}
+
void sectionEnded(SectionStats const& /* _sectionStats */) override {
m_sectionStack.pop_back();
}
+ void testCasePartialEnded( TestCaseStats const&, uint64_t ) override {}
void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override {
currentTestCaseInfo = nullptr;
}
- void testGroupEnded( TestGroupStats const& ) override;
void testRunEnded( TestRunStats const& /* _testRunStats */ ) override;
void skipTest(TestCaseInfo const&) override {
@@ -10335,13 +11291,11 @@ namespace Catch {
// It can optionally be overridden in the derived class.
}
- IConfig const* m_config;
- std::ostream& stream;
-
- LazyStat<TestRunInfo> currentTestRunInfo;
- LazyStat<GroupInfo> currentGroupInfo;
+ protected:
+ TestRunInfo currentTestRunInfo{ "test run has not started yet"_sr };
TestCaseInfo const* currentTestCaseInfo = nullptr;
+ //! Stack of all _active_ sections in the _current_ test case
std::vector<SectionInfo> m_sectionStack;
};
@@ -10349,13 +11303,13 @@ namespace Catch {
#endif // CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
-namespace Catch {
+#include <string>
- struct AutomakeReporter : StreamingReporterBase {
- AutomakeReporter( ReporterConfig const& _config )
- : StreamingReporterBase( _config )
- {}
+namespace Catch {
+ class AutomakeReporter final : public StreamingReporterBase {
+ public:
+ using StreamingReporterBase::StreamingReporterBase;
~AutomakeReporter() override;
static std::string getDescription() {
@@ -10363,14 +11317,8 @@ namespace Catch {
return "Reports test results in the format of Automake .trs files"s;
}
- void assertionStarting( AssertionInfo const& ) override {}
-
- bool assertionEnded( AssertionStats const& /*_assertionStats*/ ) override { return true; }
-
void testCaseEnded(TestCaseStats const& _testCaseStats) override;
-
void skipTest(TestCaseInfo const& testInfo) override;
-
};
} // end namespace Catch
@@ -10386,19 +11334,19 @@ namespace Catch {
namespace Catch {
- struct CompactReporter : StreamingReporterBase {
-
+ class CompactReporter final : public StreamingReporterBase {
+ public:
using StreamingReporterBase::StreamingReporterBase;
~CompactReporter() override;
static std::string getDescription();
- void noMatchingTestCases(std::string const& spec) override;
+ void noMatchingTestCases( StringRef unmatchedSpec ) override;
- void assertionStarting(AssertionInfo const&) override;
+ void testRunStarting( TestRunInfo const& _testInfo ) override;
- bool assertionEnded(AssertionStats const& _assertionStats) override;
+ void assertionEnded(AssertionStats const& _assertionStats) override;
void sectionEnded(SectionStats const& _sectionStats) override;
@@ -10415,53 +11363,43 @@ namespace Catch {
#define CATCH_REPORTER_CONSOLE_HPP_INCLUDED
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
- // Note that 4062 (not all labels are handled
- // and default is missing) is enabled
-#endif
-
-
namespace Catch {
// Fwd decls
struct SummaryColumn;
class TablePrinter;
- struct ConsoleReporter : StreamingReporterBase {
+ class ConsoleReporter final : public StreamingReporterBase {
Detail::unique_ptr<TablePrinter> m_tablePrinter;
- ConsoleReporter(ReporterConfig const& config);
+ public:
+ ConsoleReporter(ReporterConfig&& config);
~ConsoleReporter() override;
static std::string getDescription();
- void noMatchingTestCases(std::string const& spec) override;
-
- void reportInvalidArguments(std::string const&arg) override;
+ void noMatchingTestCases( StringRef unmatchedSpec ) override;
+ void reportInvalidTestSpec( StringRef arg ) override;
void assertionStarting(AssertionInfo const&) override;
- bool assertionEnded(AssertionStats const& _assertionStats) override;
+ void assertionEnded(AssertionStats const& _assertionStats) override;
void sectionStarting(SectionInfo const& _sectionInfo) override;
void sectionEnded(SectionStats const& _sectionStats) override;
- void benchmarkPreparing(std::string const& name) override;
+ void benchmarkPreparing( StringRef name ) override;
void benchmarkStarting(BenchmarkInfo const& info) override;
void benchmarkEnded(BenchmarkStats<> const& stats) override;
- void benchmarkFailed(std::string const& error) override;
+ void benchmarkFailed( StringRef error ) override;
void testCaseEnded(TestCaseStats const& _testCaseStats) override;
- void testGroupEnded(TestGroupStats const& _testGroupStats) override;
void testRunEnded(TestRunStats const& _testRunStats) override;
void testRunStarting(TestRunInfo const& _testRunInfo) override;
- private:
+ private:
void lazyPrint();
void lazyPrintWithoutClosingBenchmarkTable();
void lazyPrintRunInfo();
- void lazyPrintGroupInfo();
void printTestCaseAndSectionHeader();
void printClosedHeader(std::string const& _name);
@@ -10473,22 +11411,17 @@ namespace Catch {
void printTotals(Totals const& totals);
- void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row);
+ void printSummaryRow(StringRef label, std::vector<SummaryColumn> const& cols, std::size_t row);
void printTotalsDivider(Totals const& totals);
void printSummaryDivider();
- void printTestFilters();
- private:
bool m_headerPrinted = false;
+ bool m_testRunInfoPrinted = false;
};
} // end namespace Catch
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
#endif // CATCH_REPORTER_CONSOLE_HPP_INCLUDED
@@ -10496,20 +11429,59 @@ namespace Catch {
#define CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
-#include <iosfwd>
-#include <memory>
#include <string>
#include <vector>
namespace Catch {
- struct CumulativeReporterBase : IStreamingReporter {
+ namespace Detail {
+
+ //! Represents either an assertion or a benchmark result to be handled by cumulative reporter later
+ class AssertionOrBenchmarkResult {
+ // This should really be a variant, but this is much faster
+ // to write and the data layout here is already terrible
+ // enough that we do not have to care about the object size.
+ Optional<AssertionStats> m_assertion;
+ Optional<BenchmarkStats<>> m_benchmark;
+ public:
+ AssertionOrBenchmarkResult(AssertionStats const& assertion);
+ AssertionOrBenchmarkResult(BenchmarkStats<> const& benchmark);
+
+ bool isAssertion() const;
+ bool isBenchmark() const;
+
+ AssertionStats const& asAssertion() const;
+ BenchmarkStats<> const& asBenchmark() const;
+ };
+ }
+
+ /**
+ * Utility base for reporters that need to handle all results at once
+ *
+ * It stores tree of all test cases, sections and assertions, and after the
+ * test run is finished, calls into `testRunEndedCumulative` to pass the
+ * control to the deriving class.
+ *
+ * If you are deriving from this class and override any testing related
+ * member functions, you should first call into the base's implementation to
+ * avoid breaking the tree construction.
+ *
+ * Due to the way this base functions, it has to expand assertions up-front,
+ * even if they are later unused (e.g. because the deriving reporter does
+ * not report successful assertions, or because the deriving reporter does
+ * not use assertion expansion at all). Derived classes can use two
+ * customization points, `m_shouldStoreSuccesfulAssertions` and
+ * `m_shouldStoreFailedAssertions`, to disable the expansion and gain extra
+ * performance. **Accessing the assertion expansions if it wasn't stored is
+ * UB.**
+ */
+ class CumulativeReporterBase : public ReporterBase {
+ public:
template<typename T, typename ChildNodeT>
struct Node {
explicit Node( T const& _value ) : value( _value ) {}
- virtual ~Node() {}
- using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>;
+ using ChildNodes = std::vector<Detail::unique_ptr<ChildNodeT>>;
T value;
ChildNodes children;
};
@@ -10520,54 +11492,70 @@ namespace Catch {
return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
}
+ bool hasAnyAssertions() const;
+
SectionStats stats;
- using ChildSections = std::vector<std::shared_ptr<SectionNode>>;
- using Assertions = std::vector<AssertionStats>;
- ChildSections childSections;
- Assertions assertions;
+ std::vector<Detail::unique_ptr<SectionNode>> childSections;
+ std::vector<Detail::AssertionOrBenchmarkResult> assertionsAndBenchmarks;
std::string stdOut;
std::string stdErr;
};
using TestCaseNode = Node<TestCaseStats, SectionNode>;
- using TestGroupNode = Node<TestGroupStats, TestCaseNode>;
- using TestRunNode = Node<TestRunStats, TestGroupNode>;
+ using TestRunNode = Node<TestRunStats, TestCaseNode>;
- CumulativeReporterBase( ReporterConfig const& _config ):
- m_config( _config.fullConfig() ), stream( _config.stream() ) {}
+ using ReporterBase::ReporterBase;
~CumulativeReporterBase() override;
+ void benchmarkPreparing( StringRef ) override {}
+ void benchmarkStarting( BenchmarkInfo const& ) override {}
+ void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+ void benchmarkFailed( StringRef ) override {}
+
+ void noMatchingTestCases( StringRef ) override {}
+ void reportInvalidTestSpec( StringRef ) override {}
+ void fatalErrorEncountered( StringRef /*error*/ ) override {}
+
void testRunStarting( TestRunInfo const& ) override {}
- void testGroupStarting( GroupInfo const& ) override {}
void testCaseStarting( TestCaseInfo const& ) override {}
-
+ void testCasePartialStarting( TestCaseInfo const&, uint64_t ) override {}
void sectionStarting( SectionInfo const& sectionInfo ) override;
void assertionStarting( AssertionInfo const& ) override {}
- bool assertionEnded( AssertionStats const& assertionStats ) override;
+ void assertionEnded( AssertionStats const& assertionStats ) override;
void sectionEnded( SectionStats const& sectionStats ) override;
+ void testCasePartialEnded( TestCaseStats const&, uint64_t ) override {}
void testCaseEnded( TestCaseStats const& testCaseStats ) override;
- void testGroupEnded( TestGroupStats const& testGroupStats ) override;
void testRunEnded( TestRunStats const& testRunStats ) override;
+ //! Customization point: called after last test finishes (testRunEnded has been handled)
virtual void testRunEndedCumulative() = 0;
void skipTest(TestCaseInfo const&) override {}
- IConfig const* m_config;
- std::ostream& stream;
- std::vector<AssertionStats> m_assertions;
- std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections;
- std::vector<std::shared_ptr<TestCaseNode>> m_testCases;
- std::vector<std::shared_ptr<TestGroupNode>> m_testGroups;
+ protected:
+ //! Should the cumulative base store the assertion expansion for succesful assertions?
+ bool m_shouldStoreSuccesfulAssertions = true;
+ //! Should the cumulative base store the assertion expansion for failed assertions?
+ bool m_shouldStoreFailedAssertions = true;
- std::vector<std::shared_ptr<TestRunNode>> m_testRuns;
+ // We need lazy construction here. We should probably refactor it
+ // later, after the events are redone.
+ //! The root node of the test run tree.
+ Detail::unique_ptr<TestRunNode> m_testRun;
- std::shared_ptr<SectionNode> m_rootSection;
- std::shared_ptr<SectionNode> m_deepestSection;
- std::vector<std::shared_ptr<SectionNode>> m_sectionStack;
+ private:
+ // Note: We rely on pointer identity being stable, which is why
+ // we store pointers to the nodes rather than the values.
+ std::vector<Detail::unique_ptr<TestCaseNode>> m_testCases;
+ // Root section of the _current_ test case
+ Detail::unique_ptr<SectionNode> m_rootSection;
+ // Deepest section of the _current_ test case
+ SectionNode* m_deepestSection = nullptr;
+ // Stack of _active_ sections in the _current_ test case
+ std::vector<SectionNode*> m_sectionStack;
};
} // end namespace Catch
@@ -10582,38 +11570,44 @@ namespace Catch {
namespace Catch {
/**
- * Base class identifying listeners.
+ * Base class to simplify implementing listeners.
*
- * Provides default implementation for all IStreamingReporter member
- * functions, so that listeners implementations can pick which
+ * Provides empty default implementation for all IEventListener member
+ * functions, so that a listener implementation can pick which
* member functions it actually cares about.
*/
- class EventListenerBase : public IStreamingReporter {
- IConfig const* m_config;
-
+ class EventListenerBase : public IEventListener {
public:
- EventListenerBase( ReporterConfig const& config ):
- m_config( config.fullConfig() ) {}
+ using IEventListener::IEventListener;
+
+ void reportInvalidTestSpec( StringRef unmatchedSpec ) override;
+ void fatalErrorEncountered( StringRef error ) override;
+
+ void benchmarkPreparing( StringRef name ) override;
+ void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+ void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+ void benchmarkFailed( StringRef error ) override;
void assertionStarting( AssertionInfo const& assertionInfo ) override;
- bool assertionEnded( AssertionStats const& assertionStats ) override;
+ void assertionEnded( AssertionStats const& assertionStats ) override;
- void
- listReporters( std::vector<ReporterDescription> const& descriptions,
- IConfig const& config ) override;
- void listTests( std::vector<TestCaseHandle> const& tests,
- IConfig const& config ) override;
- void listTags( std::vector<TagInfo> const& tagInfos,
- IConfig const& config ) override;
+ void listReporters(
+ std::vector<ReporterDescription> const& descriptions ) override;
+ void listListeners(
+ std::vector<ListenerDescription> const& descriptions ) override;
+ void listTests( std::vector<TestCaseHandle> const& tests ) override;
+ void listTags( std::vector<TagInfo> const& tagInfos ) override;
- void noMatchingTestCases( std::string const& spec ) override;
+ void noMatchingTestCases( StringRef unmatchedSpec ) override;
void testRunStarting( TestRunInfo const& testRunInfo ) override;
- void testGroupStarting( GroupInfo const& groupInfo ) override;
void testCaseStarting( TestCaseInfo const& testInfo ) override;
+ void testCasePartialStarting( TestCaseInfo const& testInfo,
+ uint64_t partNumber ) override;
void sectionStarting( SectionInfo const& sectionInfo ) override;
void sectionEnded( SectionStats const& sectionStats ) override;
+ void testCasePartialEnded( TestCaseStats const& testCaseStats,
+ uint64_t partNumber ) override;
void testCaseEnded( TestCaseStats const& testCaseStats ) override;
- void testGroupEnded( TestGroupStats const& testGroupStats ) override;
void testRunEnded( TestRunStats const& testRunStats ) override;
void skipTest( TestCaseInfo const& testInfo ) override;
};
@@ -10630,9 +11624,12 @@ namespace Catch {
#include <string>
#include <vector>
+
namespace Catch {
- struct IConfig;
+ class IConfig;
+ class TestCaseHandle;
+ class ColourImpl;
// Returns double formatted as %.3f (format expected on output)
std::string getFormattedDuration( double duration );
@@ -10649,6 +11646,50 @@ namespace Catch {
friend std::ostream& operator<<( std::ostream& out, lineOfChars value );
};
+ /**
+ * Lists reporter descriptions to the provided stream in user-friendly
+ * format
+ *
+ * Used as the default listing implementation by the first party reporter
+ * bases. The output should be backwards compatible with the output of
+ * Catch2 v2 binaries.
+ */
+ void
+ defaultListReporters( std::ostream& out,
+ std::vector<ReporterDescription> const& descriptions,
+ Verbosity verbosity );
+
+ /**
+ * Lists listeners descriptions to the provided stream in user-friendly
+ * format
+ */
+ void defaultListListeners( std::ostream& out,
+ std::vector<ListenerDescription> const& descriptions );
+
+ /**
+ * Lists tag information to the provided stream in user-friendly format
+ *
+ * Used as the default listing implementation by the first party reporter
+ * bases. The output should be backwards compatible with the output of
+ * Catch2 v2 binaries.
+ */
+ void defaultListTags( std::ostream& out, std::vector<TagInfo> const& tags, bool isFiltered );
+
+ /**
+ * Lists test case information to the provided stream in user-friendly
+ * format
+ *
+ * Used as the default listing implementation by the first party reporter
+ * bases. The output is backwards compatible with the output of Catch2
+ * v2 binaries, and also supports the format specific to the old
+ * `--list-test-names-only` option, for people who used it in integrations.
+ */
+ void defaultListTests( std::ostream& out,
+ ColourImpl* streamColour,
+ std::vector<TestCaseHandle> const& tests,
+ bool isFiltered,
+ Verbosity verbosity );
+
} // end namespace Catch
#endif // CATCH_REPORTER_HELPERS_HPP_INCLUDED
@@ -10661,36 +11702,32 @@ namespace Catch {
namespace Catch {
- class JunitReporter : public CumulativeReporterBase {
+ class JunitReporter final : public CumulativeReporterBase {
public:
- JunitReporter(ReporterConfig const& _config);
+ JunitReporter(ReporterConfig&& _config);
- ~JunitReporter() override;
+ ~JunitReporter() override = default;
static std::string getDescription();
- void noMatchingTestCases(std::string const& /*spec*/) override;
-
void testRunStarting(TestRunInfo const& runInfo) override;
- void testGroupStarting(GroupInfo const& groupInfo) override;
-
void testCaseStarting(TestCaseInfo const& testCaseInfo) override;
- bool assertionEnded(AssertionStats const& assertionStats) override;
+ void assertionEnded(AssertionStats const& assertionStats) override;
void testCaseEnded(TestCaseStats const& testCaseStats) override;
- void testGroupEnded(TestGroupStats const& testGroupStats) override;
-
void testRunEndedCumulative() override;
- void writeGroup(TestGroupNode const& groupNode, double suiteTime);
+ private:
+ void writeRun(TestRunNode const& testRunNode, double suiteTime);
void writeTestCase(TestCaseNode const& testCaseNode);
- void writeSection(std::string const& className,
- std::string const& rootName,
- SectionNode const& sectionNode);
+ void writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode,
+ bool testOkToFail );
void writeAssertions(SectionNode const& sectionNode);
void writeAssertion(AssertionStats const& stats);
@@ -10708,59 +11745,189 @@ namespace Catch {
#endif // CATCH_REPORTER_JUNIT_HPP_INCLUDED
-#ifndef CATCH_REPORTER_LISTENING_HPP_INCLUDED
-#define CATCH_REPORTER_LISTENING_HPP_INCLUDED
+#ifndef CATCH_REPORTER_MULTI_HPP_INCLUDED
+#define CATCH_REPORTER_MULTI_HPP_INCLUDED
namespace Catch {
- class ListeningReporter final : public IStreamingReporter {
- using Reporters = std::vector<IStreamingReporterPtr>;
- Reporters m_listeners;
- IStreamingReporterPtr m_reporter = nullptr;
+ class MultiReporter final : public IEventListener {
+ /*
+ * Stores all added reporters and listeners
+ *
+ * All Listeners are stored before all reporters, and individual
+ * listeners/reporters are stored in order of insertion.
+ */
+ std::vector<IEventListenerPtr> m_reporterLikes;
+ bool m_haveNoncapturingReporters = false;
- public:
- ListeningReporter();
+ // Keep track of how many listeners we have already inserted,
+ // so that we can insert them into the main vector at the right place
+ size_t m_insertedListeners = 0;
- void addListener( IStreamingReporterPtr&& listener );
- void addReporter( IStreamingReporterPtr&& reporter );
+ void updatePreferences(IEventListener const& reporterish);
- public: // IStreamingReporter
+ public:
+ using IEventListener::IEventListener;
- void noMatchingTestCases( std::string const& spec ) override;
+ void addListener( IEventListenerPtr&& listener );
+ void addReporter( IEventListenerPtr&& reporter );
- void reportInvalidArguments(std::string const&arg) override;
+ public: // IEventListener
- void benchmarkPreparing(std::string const& name) override;
+ void noMatchingTestCases( StringRef unmatchedSpec ) override;
+ void fatalErrorEncountered( StringRef error ) override;
+ void reportInvalidTestSpec( StringRef arg ) override;
+
+ void benchmarkPreparing( StringRef name ) override;
void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
- void benchmarkFailed(std::string const&) override;
+ void benchmarkFailed( StringRef error ) override;
void testRunStarting( TestRunInfo const& testRunInfo ) override;
- void testGroupStarting( GroupInfo const& groupInfo ) override;
void testCaseStarting( TestCaseInfo const& testInfo ) override;
+ void testCasePartialStarting(TestCaseInfo const& testInfo, uint64_t partNumber) override;
void sectionStarting( SectionInfo const& sectionInfo ) override;
void assertionStarting( AssertionInfo const& assertionInfo ) override;
- // The return value indicates if the messages buffer should be cleared:
- bool assertionEnded( AssertionStats const& assertionStats ) override;
+ void assertionEnded( AssertionStats const& assertionStats ) override;
void sectionEnded( SectionStats const& sectionStats ) override;
+ void testCasePartialEnded(TestCaseStats const& testInfo, uint64_t partNumber) override;
void testCaseEnded( TestCaseStats const& testCaseStats ) override;
- void testGroupEnded( TestGroupStats const& testGroupStats ) override;
void testRunEnded( TestRunStats const& testRunStats ) override;
void skipTest( TestCaseInfo const& testInfo ) override;
- void listReporters(std::vector<ReporterDescription> const& descriptions, IConfig const& config) override;
- void listTests(std::vector<TestCaseHandle> const& tests, IConfig const& config) override;
- void listTags(std::vector<TagInfo> const& tags, IConfig const& config) override;
+ void listReporters(std::vector<ReporterDescription> const& descriptions) override;
+ void listListeners(std::vector<ListenerDescription> const& descriptions) override;
+ void listTests(std::vector<TestCaseHandle> const& tests) override;
+ void listTags(std::vector<TagInfo> const& tags) override;
};
} // end namespace Catch
-#endif // CATCH_REPORTER_LISTENING_HPP_INCLUDED
+#endif // CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+#define CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+
+#include <type_traits>
+
+namespace Catch {
+
+ namespace Detail {
+
+ template <typename T, typename = void>
+ struct has_description : std::false_type {};
+
+ template <typename T>
+ struct has_description<
+ T,
+ void_t<decltype( T::getDescription() )>>
+ : std::true_type {};
+
+ //! Indirection for reporter registration, so that the error handling is
+ //! independent on the reporter's concrete type
+ void registerReporterImpl( std::string const& name,
+ IReporterFactoryPtr reporterPtr );
+
+ } // namespace Detail
+
+ class IEventListener;
+ using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
+
+ template <typename T>
+ class ReporterFactory : public IReporterFactory {
+
+ IEventListenerPtr create( ReporterConfig&& config ) const override {
+ return Detail::make_unique<T>( CATCH_MOVE(config) );
+ }
+
+ std::string getDescription() const override {
+ return T::getDescription();
+ }
+ };
+
+
+ template<typename T>
+ class ReporterRegistrar {
+ public:
+ explicit ReporterRegistrar( std::string const& name ) {
+ registerReporterImpl( name,
+ Detail::make_unique<ReporterFactory<T>>() );
+ }
+ };
+
+ template<typename T>
+ class ListenerRegistrar {
+
+ class TypedListenerFactory : public EventListenerFactory {
+ StringRef m_listenerName;
+
+ std::string getDescriptionImpl( std::true_type ) const {
+ return T::getDescription();
+ }
+
+ std::string getDescriptionImpl( std::false_type ) const {
+ return "(No description provided)";
+ }
+
+ public:
+ TypedListenerFactory( StringRef listenerName ):
+ m_listenerName( listenerName ) {}
+
+ IEventListenerPtr create( IConfig const* config ) const override {
+ return Detail::make_unique<T>( config );
+ }
+
+ StringRef getName() const override {
+ return m_listenerName;
+ }
+
+ std::string getDescription() const override {
+ return getDescriptionImpl( Detail::has_description<T>{} );
+ }
+ };
+
+ public:
+ ListenerRegistrar(StringRef listenerName) {
+ getMutableRegistryHub().registerListener( Detail::make_unique<TypedListenerFactory>(listenerName) );
+ }
+ };
+}
+
+#if !defined(CATCH_CONFIG_DISABLE)
+
+# define CATCH_REGISTER_REPORTER( name, reporterType ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace { \
+ Catch::ReporterRegistrar<reporterType> INTERNAL_CATCH_UNIQUE_NAME( \
+ catch_internal_RegistrarFor )( name ); \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+# define CATCH_REGISTER_LISTENER( listenerType ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace { \
+ Catch::ListenerRegistrar<listenerType> INTERNAL_CATCH_UNIQUE_NAME( \
+ catch_internal_RegistrarFor )( #listenerType ); \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#else // CATCH_CONFIG_DISABLE
+
+#define CATCH_REGISTER_REPORTER(name, reporterType)
+#define CATCH_REGISTER_LISTENER(listenerType)
+
+#endif // CATCH_CONFIG_DISABLE
+
+#endif // CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
#ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
@@ -10770,35 +11937,33 @@ namespace Catch {
namespace Catch {
- struct SonarQubeReporter : CumulativeReporterBase {
-
- SonarQubeReporter(ReporterConfig const& config)
- : CumulativeReporterBase(config)
- , xml(config.stream()) {
+ class SonarQubeReporter final : public CumulativeReporterBase {
+ public:
+ SonarQubeReporter(ReporterConfig&& config)
+ : CumulativeReporterBase(CATCH_MOVE(config))
+ , xml(m_stream) {
m_preferences.shouldRedirectStdOut = true;
m_preferences.shouldReportAllAssertions = true;
+ m_shouldStoreSuccesfulAssertions = false;
}
- ~SonarQubeReporter() override;
+ ~SonarQubeReporter() override = default;
static std::string getDescription() {
using namespace std::string_literals;
return "Reports test results in the Generic Test Data SonarQube XML format"s;
}
- void noMatchingTestCases(std::string const& /*spec*/) override {}
-
- void testRunStarting(TestRunInfo const& testRunInfo) override;
-
- void testGroupEnded(TestGroupStats const& testGroupStats) override;
+ void testRunStarting( TestRunInfo const& testRunInfo ) override;
void testRunEndedCumulative() override {
+ writeRun( *m_testRun );
xml.endElement();
}
- void writeGroup(TestGroupNode const& groupNode);
+ void writeRun( TestRunNode const& groupNode );
- void writeTestFile(std::string const& filename, TestGroupNode::ChildNodes const& testCaseNodes);
+ void writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes);
void writeTestCase(TestCaseNode const& testCaseNode);
@@ -10824,24 +11989,24 @@ namespace Catch {
namespace Catch {
- struct TAPReporter : StreamingReporterBase {
-
- TAPReporter( ReporterConfig const& config ):
- StreamingReporterBase( config ) {
+ class TAPReporter final : public StreamingReporterBase {
+ public:
+ TAPReporter( ReporterConfig&& config ):
+ StreamingReporterBase( CATCH_MOVE(config) ) {
m_preferences.shouldReportAllAssertions = true;
}
- ~TAPReporter() override;
+ ~TAPReporter() override = default;
static std::string getDescription() {
using namespace std::string_literals;
return "Reports test results in TAP format, suitable for test harnesses"s;
}
- void noMatchingTestCases(std::string const& spec) override;
+ void testRunStarting( TestRunInfo const& testInfo ) override;
- void assertionStarting( AssertionInfo const& ) override {}
+ void noMatchingTestCases( StringRef unmatchedSpec ) override;
- bool assertionEnded(AssertionStats const& _assertionStats) override;
+ void assertionEnded(AssertionStats const& _assertionStats) override;
void testRunEnded(TestRunStats const& _testRunStats) override;
@@ -10867,9 +12032,10 @@ namespace Catch {
namespace Catch {
- struct TeamCityReporter : StreamingReporterBase {
- TeamCityReporter( ReporterConfig const& _config )
- : StreamingReporterBase( _config )
+ class TeamCityReporter final : public StreamingReporterBase {
+ public:
+ TeamCityReporter( ReporterConfig&& _config )
+ : StreamingReporterBase( CATCH_MOVE(_config) )
{
m_preferences.shouldRedirectStdOut = true;
}
@@ -10881,17 +12047,11 @@ namespace Catch {
return "Reports test results as TeamCity service messages"s;
}
- void skipTest( TestCaseInfo const& /* testInfo */ ) override {}
+ void testRunStarting( TestRunInfo const& groupInfo ) override;
+ void testRunEnded( TestRunStats const& testGroupStats ) override;
- void noMatchingTestCases( std::string const& /* spec */ ) override {}
- void testGroupStarting(GroupInfo const& groupInfo) override;
- void testGroupEnded(TestGroupStats const& testGroupStats) override;
-
-
- void assertionStarting(AssertionInfo const&) override {}
-
- bool assertionEnded(AssertionStats const& assertionStats) override;
+ void assertionEnded(AssertionStats const& assertionStats) override;
void sectionStarting(SectionInfo const& sectionInfo) override {
m_headerPrintedForThisSection = false;
@@ -10905,7 +12065,6 @@ namespace Catch {
private:
void printSectionHeader(std::ostream& os);
- private:
bool m_headerPrintedForThisSection = false;
Timer m_testTimer;
};
@@ -10928,7 +12087,7 @@ namespace Catch {
namespace Catch {
class XmlReporter : public StreamingReporterBase {
public:
- XmlReporter(ReporterConfig const& _config);
+ XmlReporter(ReporterConfig&& _config);
~XmlReporter() override;
@@ -10940,36 +12099,31 @@ namespace Catch {
public: // StreamingReporterBase
- void noMatchingTestCases(std::string const& s) override;
-
void testRunStarting(TestRunInfo const& testInfo) override;
- void testGroupStarting(GroupInfo const& groupInfo) override;
-
void testCaseStarting(TestCaseInfo const& testInfo) override;
void sectionStarting(SectionInfo const& sectionInfo) override;
void assertionStarting(AssertionInfo const&) override;
- bool assertionEnded(AssertionStats const& assertionStats) override;
+ void assertionEnded(AssertionStats const& assertionStats) override;
void sectionEnded(SectionStats const& sectionStats) override;
void testCaseEnded(TestCaseStats const& testCaseStats) override;
- void testGroupEnded(TestGroupStats const& testGroupStats) override;
-
void testRunEnded(TestRunStats const& testRunStats) override;
- void benchmarkPreparing(std::string const& name) override;
+ void benchmarkPreparing( StringRef name ) override;
void benchmarkStarting(BenchmarkInfo const&) override;
void benchmarkEnded(BenchmarkStats<> const&) override;
- void benchmarkFailed(std::string const&) override;
+ void benchmarkFailed( StringRef error ) override;
- void listReporters(std::vector<ReporterDescription> const& descriptions, IConfig const& config) override;
- void listTests(std::vector<TestCaseHandle> const& tests, IConfig const& config) override;
- void listTags(std::vector<TagInfo> const& tags, IConfig const& config) override;
+ void listReporters(std::vector<ReporterDescription> const& descriptions) override;
+ void listListeners(std::vector<ListenerDescription> const& descriptions) override;
+ void listTests(std::vector<TestCaseHandle> const& tests) override;
+ void listTags(std::vector<TagInfo> const& tags) override;
private:
Timer m_testCaseTimer;
@@ -10984,9 +12138,4 @@ namespace Catch {
#endif // CATCH_REPORTERS_ALL_HPP_INCLUDED
#endif // CATCH_ALL_HPP_INCLUDED
-
-#if defined(CATCH_CONFIG_MAIN)
-#include <catch2/catch_main.cpp>
-#endif /* defined(CATCH_CONFIG_MAIN) */
-
#endif // CATCH_AMALGAMATED_HPP_INCLUDED