diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index e42faf64cb..58a1c7d599 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.0.0-preview.4 -// Generated: 2022-01-03 23:14:23.198909 +// Catch v3.0.0-preview.5 +// Generated: 2022-04-20 23:45:15.004945 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -20,6 +20,7 @@ #include +#include #include #include @@ -127,11 +128,15 @@ using Catch::Benchmark::Detail::sample; double standard_deviation(std::vector::iterator first, std::vector::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 ); } } @@ -491,39 +496,18 @@ namespace Catch { -#include - namespace Catch { - namespace Detail { - namespace { - class RDBufStream : public IStream { - mutable std::ostream m_os; - - public: - //! The streambuf `sb` must outlive the constructed object. - RDBufStream( std::streambuf* sb ): m_os( sb ) {} - ~RDBufStream() override = default; - public: // IStream - std::ostream& stream() const override { return m_os; } - }; - } // unnamed namespace - } // namespace Detail - - std::ostream& operator<<( std::ostream& os, - ConfigData::ReporterAndFile const& reporter ) { - os << "{ " << reporter.reporterName << ", "; - if ( reporter.outputFileName ) { - os << *reporter.outputFileName; - } else { - os << ""; - } - return os << " }"; + 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 ), - m_defaultStream( openStream( data.defaultOutputFilename ) ) { + 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). @@ -535,6 +519,7 @@ namespace Catch { elem = trim(elem); } + TestSpecParser parser(ITagAliasRegistry::get()); if (!m_data.testsOrTags.empty()) { m_hasTestFilters = true; @@ -544,15 +529,62 @@ namespace Catch { } m_testSpec = parser.testSpec(); - m_reporterStreams.reserve( m_data.reporterSpecifications.size() ); - for ( auto const& reporterAndFile : m_data.reporterSpecifications ) { - if ( reporterAndFile.outputFileName.none() ) { - m_reporterStreams.emplace_back( new Detail::RDBufStream( - m_defaultStream->stream().rdbuf() ) ); - } else { - m_reporterStreams.emplace_back( - openStream( *reporterAndFile.outputFileName ) ); + + // 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; } + + m_processedReporterSpecs.push_back( ProcessedReporterSpec{ + reporterSpec.name(), + reporterSpec.outputFile() ? *reporterSpec.outputFile() + : data.defaultOutputFilename, + reporterSpec.colourMode().valueOr( data.defaultColourMode ), + reporterSpec.customOptions() } ); } } @@ -566,12 +598,13 @@ namespace Catch { std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } - std::vector const& Config::getReportersAndOutputFiles() const { + std::vector const& Config::getReporterSpecs() const { return m_data.reporterSpecifications; } - std::ostream& Config::getReporterOutputStream(std::size_t reporterIdx) const { - return m_reporterStreams.at(reporterIdx)->stream(); + std::vector const& + Config::getProcessedReporterSpecs() const { + return m_processedReporterSpecs; } TestSpec const& Config::testSpec() const { return m_testSpec; } @@ -581,7 +614,6 @@ namespace Catch { // IConfig interface bool Config::allowThrows() const { return !m_data.noThrow; } - std::ostream& Config::defaultStream() const { return m_defaultStream->stream(); } 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 { @@ -597,7 +629,7 @@ namespace Catch { 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; } - UseColour Config::useColour() const { return m_data.useColour; } + 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; } @@ -609,10 +641,6 @@ namespace Catch { unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } - Detail::unique_ptr Config::openStream(std::string const& outputFileName) { - return Catch::makeStream(outputFileName); - } - } // end namespace Catch @@ -750,7 +778,7 @@ namespace Catch { void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override { m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) ); } - void registerListener( IReporterFactoryPtr factory ) override { + void registerListener( Detail::unique_ptr factory ) override { m_reporterRegistry.registerListener( CATCH_MOVE(factory) ); } void registerTest( Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker ) override { @@ -814,31 +842,40 @@ namespace Catch { namespace { const int MaxExitCode = 255; - IStreamingReporterPtr createReporter(std::string const& reporterName, ReporterConfig const& config) { - auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + 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) { + IEventListenerPtr prepareReporters(Config const* config) { if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty() - && config->getReportersAndOutputFiles().size() == 1) { - auto& stream = config->getReporterOutputStream(0); - return createReporter(config->getReportersAndOutputFiles()[0].reporterName, ReporterConfig(config, stream)); + && config->getProcessedReporterSpecs().size() == 1) { + auto const& spec = config->getProcessedReporterSpecs()[0]; + return createReporter( + spec.name, + ReporterConfig( config, + makeStream( spec.outputFilename ), + spec.colourMode, + spec.customOptions ) ); } - auto multi = Detail::make_unique(config); + auto multi = Detail::make_unique(config); auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); for (auto const& listener : listeners) { - multi->addListener(listener->create(Catch::ReporterConfig(config, config->defaultStream()))); + multi->addListener(listener->create(config)); } std::size_t reporterIdx = 0; - for (auto const& reporterAndFile : config->getReportersAndOutputFiles()) { - auto& stream = config->getReporterOutputStream(reporterIdx); - multi->addReporter(createReporter(reporterAndFile.reporterName, ReporterConfig(config, stream))); + for ( auto const& reporterSpec : config->getProcessedReporterSpecs() ) { + multi->addReporter( createReporter( + reporterSpec.name, + ReporterConfig( config, + makeStream( reporterSpec.outputFilename ), + reporterSpec.colourMode, + reporterSpec.customOptions ) ) ); reporterIdx++; } @@ -847,7 +884,7 @@ namespace Catch { 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, CATCH_MOVE(reporter)} { @@ -900,7 +937,7 @@ namespace Catch { private: - IStreamingReporter* m_reporter; + IEventListener* m_reporter; Config const* m_config; RunContext m_context; std::set m_tests; @@ -931,14 +968,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'; } } } @@ -953,7 +993,7 @@ namespace Catch { void Session::showHelp() const { Catch::cout() - << "\nCatch v" << libraryVersion() << '\n' + << "\nCatch2 v" << libraryVersion() << '\n' << m_cli << '\n' << "For more detailed usage please see the project docs\n\n" << std::flush; } @@ -961,7 +1001,7 @@ namespace Catch { 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) << "framework: " << "Catch2\n" << std::left << std::setw(16) << "version: " << libraryVersion() << '\n' << std::flush; } @@ -974,12 +1014,15 @@ namespace Catch { 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\n" << std::flush; + errStream->stream() << "Run with -? for usage\n\n" << std::flush; return MaxExitCode; } @@ -1078,7 +1121,7 @@ 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() ) { @@ -1110,7 +1153,7 @@ namespace Catch { // 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(totals.assertions.failed))); + return (std::min) (MaxExitCode, static_cast(totals.assertions.failed)); } #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) catch( std::exception& ex ) { @@ -1817,7 +1860,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 0, 0, "preview", 4 ); + static Version version( 3, 0, 0, "preview", 5 ); return version; } @@ -1953,20 +1996,40 @@ namespace Catch { namespace Catch { IReporterFactory::~IReporterFactory() = default; + EventListenerFactory::~EventListenerFactory() = default; } #include +#include #include namespace Catch { - ReporterConfig::ReporterConfig( IConfig const* _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + ReporterConfig::ReporterConfig( + IConfig const* _fullConfig, + Detail::unique_ptr _stream, + ColourMode colourMode, + std::map customOptions ): + m_stream( CATCH_MOVE(_stream) ), + m_fullConfig( _fullConfig ), + m_colourMode( colourMode ), + m_customOptions( CATCH_MOVE( customOptions ) ) {} - std::ostream& ReporterConfig::stream() const { return *m_stream; } + Detail::unique_ptr 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 const& + ReporterConfig::customOptions() const { + return m_customOptions; + } + + ReporterConfig::~ReporterConfig() = default; AssertionStats::AssertionStats( AssertionResult const& _assertionResult, std::vector const& _infoMessages, @@ -2020,7 +2083,7 @@ namespace Catch { aborting( _aborting ) {} - IStreamingReporter::~IStreamingReporter() = default; + IEventListener::~IEventListener() = default; } // end namespace Catch @@ -2126,7 +2189,10 @@ namespace Catch { } // namespace Catch + + #include +#include namespace { bool isOptPrefix( char c ) { @@ -2778,7 +2844,6 @@ namespace Catch { -#include #include #include @@ -2859,19 +2924,23 @@ namespace Catch { return ParserResult::runtimeError("Could not parse '" + seed + "' as seed"); } }; - 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 ); - }; + auto const setDefaultColourMode = [&]( std::string const& colourMode ) { + Optional 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") @@ -2898,64 +2967,42 @@ namespace Catch { return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + '\'' ); return ParserResult::ok( ParseResultType::Matched ); }; - auto const setReporter = [&]( std::string const& reporterSpec ) { - if ( reporterSpec.empty() ) { + auto const setReporter = [&]( std::string const& userReporterSpec ) { + if ( userReporterSpec.empty() ) { return ParserResult::runtimeError( "Received empty reporter spec." ); } - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - - // clear the default reporter - if (!config._nonDefaultReporterSpecifications) { - config.reporterSpecifications.clear(); - config._nonDefaultReporterSpecifications = true; + Optional parsed = + parseReporterSpec( userReporterSpec ); + if ( !parsed ) { + return ParserResult::runtimeError( + "Could not parse reporter spec '" + userReporterSpec + + "'" ); } + auto const& reporterSpec = *parsed; - // Exactly one of the reporters may be specified without an output - // file, in which case it defaults to the output specified by "-o" - // (or standard output). - static constexpr auto separator = "::"; - static constexpr size_t separatorSize = 2; - auto fileNameSeparatorPos = reporterSpec.find( separator ); - const bool containsFileName = fileNameSeparatorPos != reporterSpec.npos; - if ( containsFileName ) { - auto nextSeparatorPos = reporterSpec.find( - separator, fileNameSeparatorPos + separatorSize ); - if ( nextSeparatorPos != reporterSpec.npos ) { - return ParserResult::runtimeError( - "Too many separators in reporter spec '" + reporterSpec + '\'' ); - } - } - - std::string reporterName; - Optional outputFileName; - reporterName = reporterSpec.substr( 0, fileNameSeparatorPos ); - if ( reporterName.empty() ) { - return ParserResult::runtimeError( "Reporter name cannot be empty." ); - } + IReporterRegistry::FactoryMap const& factories = + getRegistryHub().getReporterRegistry().getFactories(); + auto result = factories.find( reporterSpec.name() ); - if ( containsFileName ) { - outputFileName = reporterSpec.substr( - fileNameSeparatorPos + separatorSize, reporterSpec.size() ); + if ( result == factories.end() ) { + return ParserResult::runtimeError( + "Unrecognized reporter, '" + reporterSpec.name() + + "'. Check available with --list-reporters" ); } - auto result = factories.find( reporterName ); - - if( result == factories.end() ) - return ParserResult::runtimeError( "Unrecognized reporter, '" + reporterName + "'. Check available with --list-reporters" ); - if( containsFileName && outputFileName->empty() ) - return ParserResult::runtimeError( "Reporter '" + reporterName + "' has empty filename specified as its output. Supply a filename or remove the colons to use the default output." ); - config.reporterSpecifications.push_back({ std::move(reporterName), std::move(outputFileName) }); - - // 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 (!containsFileName) { + 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.outputFileName.none()) { + if (spec.outputFile().none()) { n_reporters_without_file++; } } @@ -3021,7 +3068,7 @@ namespace Catch { | Opt( config.defaultOutputFilename, "filename" ) ["-o"]["--out"] ( "default output filename" ) - | Opt( accept_many, setReporter, "name[:output-file]" ) + | Opt( accept_many, setReporter, "name[::key=value]*" ) ["-r"]["--reporter"] ( "reporter to use (defaults to console)" ) | Opt( config.name, "name" ) @@ -3069,9 +3116,9 @@ namespace Catch { | 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" ) @@ -3118,53 +3165,107 @@ namespace Catch { +#include #include +#include namespace Catch { - namespace { - struct IColourImpl { - virtual ~IColourImpl() = default; - virtual void use( Colour::Code _colourCode ) = 0; - }; + ColourImpl::~ColourImpl() = default; - struct NoColourImpl : IColourImpl { - void use( Colour::Code ) override {} + ColourImpl::ColourGuard ColourImpl::guardColour( Colour::Code colourCode ) { + return ColourGuard(colourCode, this ); + } - static IColourImpl* instance() { - static NoColourImpl s_instance; - return &s_instance; - } + 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( stream ); + + m_engaged = true; + m_colourImpl->use( m_code ); + } + + 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; + } + + ColourImpl::ColourGuard&& + ColourImpl::ColourGuard::engage( std::ostream& stream ) && { + engageImpl( stream ); + return CATCH_MOVE(*this); + } + + 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 ) {} + static bool useColourOnPlatform() { return true; } + + private: + void use( Colour::Code ) const override {} }; + } // namespace - } // 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 +} // namespace Catch -#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 useColourOnPlatform(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 ); @@ -3188,45 +3289,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 +#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) +# define CATCH_INTERNAL_HAS_ISATTY +# include +#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 useColourOnPlatform(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" ); @@ -3247,89 +3366,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()->defaultStream() - << '\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 makeColourImpl( ColourMode implSelection, + IStream* stream ) { + if ( implSelection == ColourMode::None ) { + return Detail::make_unique( stream ); + } + if ( implSelection == ColourMode::ANSI ) { + return Detail::make_unique( stream ); + } +#if defined( CATCH_CONFIG_COLOUR_WIN32 ) + if ( implSelection == ColourMode::Win32 ) { + return Detail::make_unique( stream ); + } +#endif -namespace Catch { + // todo: check win32 eligibility under ifdef, otherwise ansi + if ( implSelection == ColourMode::PlatformDefault) { +#if defined (CATCH_CONFIG_COLOUR_WIN32) + if ( Win32ColourImpl::useColourOnPlatform( *stream ) ) { + return Detail::make_unique( stream ); + } else { + return Detail::make_unique( stream ); + } +#endif + if ( ANSIColourImpl::useColourOnPlatform( *stream ) ) { + return Detail::make_unique( stream ); + } + return Detail::make_unique( stream ); + } - Colour::Colour( Code _colourCode ) { use( _colourCode ); } - Colour::Colour( Colour&& other ) noexcept { - m_moved = other.m_moved; - other.m_moved = true; - } - Colour& Colour::operator=( Colour&& other ) noexcept { - m_moved = other.m_moved; - other.m_moved = true; - return *this; + CATCH_ERROR( "Could not create colour impl for selection " << static_cast(implSelection) ); } - 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 @@ -3395,6 +3484,10 @@ namespace Catch { + + +#include + #if defined(CATCH_CONFIG_ANDROID_LOGWRITE) #include @@ -3735,7 +3828,7 @@ 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() {} + void FatalConditionHandler::disengage_platform() noexcept {} FatalConditionHandler::FatalConditionHandler() = default; FatalConditionHandler::~FatalConditionHandler() = default; @@ -3773,10 +3866,10 @@ namespace Catch { // Windows can easily distinguish between SO and SigSegV, // but SigInt, SigTerm, etc are handled differently. static SignalDefs signalDefs[] = { - { static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal" }, - { static_cast(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" }, - { static_cast(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" }, - { static_cast(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" }, }; static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { @@ -3819,9 +3912,11 @@ namespace Catch { previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter); } - void FatalConditionHandler::disengage_platform() { - if (SetUnhandledExceptionFilter(reinterpret_cast(previousTopLevelExceptionFilter)) != topLevelExceptionFilter) { - CATCH_RUNTIME_ERROR("Could not restore previous top level exception filter"); + 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; } @@ -3863,7 +3958,7 @@ namespace Catch { static stack_t oldSigStack{}; static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; - static void restorePreviousSignalHandlers() { + 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 @@ -3926,7 +4021,7 @@ namespace Catch { #endif - void FatalConditionHandler::disengage_platform() { + void FatalConditionHandler::disengage_platform() noexcept { restorePreviousSignalHandlers(); } @@ -3963,84 +4058,232 @@ namespace Catch { +#include +#include +#include +#include namespace Catch { + + Catch::IStream::~IStream() = default; + +namespace Detail { namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; - void listTests(IStreamingReporter& reporter, IConfig const& config) { - auto const& testSpec = config.testSpec(); - auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - reporter.listTests(matchedTestCases); - } + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } - void listTags(IStreamingReporter& reporter, IConfig const& config) { - auto const& testSpec = config.testSpec(); - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } - std::map tagCounts; - for (auto const& testCase : matchedTestCases) { - for (auto const& tagName : testCase.getTestCaseInfo().tags) { - auto it = tagCounts.find(tagName.original); - if (it == tagCounts.end()) - it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first; - it->second.add(tagName.original); + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); } + return 0; } - std::vector infos; infos.reserve(tagCounts.size()); - for (auto& tagc : tagCounts) { - infos.push_back(CATCH_MOVE(tagc.second)); + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; } + }; - reporter.listTags(infos); - } + /////////////////////////////////////////////////////////////////////////// - void listReporters(IStreamingReporter& reporter) { - std::vector descriptions; + struct OutputDebugWriter { - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - descriptions.reserve(factories.size()); - for (auto const& fac : factories) { - descriptions.push_back({ fac.first, fac.second->getDescription() }); + void operator()( std::string const& str ) { + if ( !str.empty() ) { + writeToDebugConsole( str ); + } } + }; - reporter.listReporters(descriptions); - } + /////////////////////////////////////////////////////////////////////////// - } // end anonymous namespace + 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; + } + }; - void TagInfo::add( StringRef spelling ) { - ++count; - spellings.insert( spelling ); - } + /////////////////////////////////////////////////////////////////////////// - std::string TagInfo::all() const { - // 2 per tag for brackets '[' and ']' - size_t size = spellings.size() * 2; - for (auto const& spelling : spellings) { - size += spelling.size(); - } + 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; - std::string out; out.reserve(size); - for (auto const& spelling : spellings) { - out += '['; - out += spelling; - out += ']'; - } - return out; - } + public: // IStream + std::ostream& stream() override { return m_os; } + bool isConsole() const override { return true; } + }; - bool list( IStreamingReporter& reporter, Config const& config ) { - bool listed = false; - if (config.listTests()) { - listed = true; - listTests(reporter, config); - } - if (config.listTags()) { - listed = true; - listTags(reporter, config); - } - if (config.listReporters()) { + 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> m_streamBuf; + std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( Detail::make_unique>() ), + 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 { + if ( filename.empty() || filename == "-" ) { + return Detail::make_unique(); + } + if( filename[0] == '%' ) { + if ( filename == "%debug" ) { + return Detail::make_unique(); + } else if ( filename == "%stderr" ) { + return Detail::make_unique(); + } else if ( filename == "%stdout" ) { + return Detail::make_unique(); + } else { + CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' ); + } + } + return Detail::make_unique( 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); + } + + void listTags(IEventListener& reporter, IConfig const& config) { + auto const& testSpec = config.testSpec(); + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + + std::map tagCounts; + for (auto const& testCase : matchedTestCases) { + for (auto const& tagName : testCase.getTestCaseInfo().tags) { + auto it = tagCounts.find(tagName.original); + if (it == tagCounts.end()) + it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first; + it->second.add(tagName.original); + } + } + + std::vector infos; infos.reserve(tagCounts.size()); + for (auto& tagc : tagCounts) { + infos.push_back(CATCH_MOVE(tagc.second)); + } + + reporter.listTags(infos); + } + + void listReporters(IEventListener& reporter) { + std::vector descriptions; + + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + descriptions.reserve(factories.size()); + for (auto const& fac : factories) { + descriptions.push_back({ fac.first, fac.second->getDescription() }); + } + + reporter.listReporters(descriptions); + } + + } // end anonymous namespace + + void TagInfo::add( StringRef spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + // 2 per tag for brackets '[' and ']' + size_t size = spellings.size() * 2; + for (auto const& spelling : spellings) { + size += spelling.size(); + } + + std::string out; out.reserve(size); + for (auto const& spelling : spellings) { + out += '['; + out += spelling; + out += ']'; + } + return out; + } + + bool list( IEventListener& reporter, Config const& config ) { + bool listed = false; + if (config.listTests()) { + listed = true; + listTests(reporter, config); + } + if (config.listTags()) { + listed = true; + listTags(reporter, config); + } + if (config.listReporters()) { listed = true; listReporters(reporter); } @@ -4328,17 +4571,20 @@ namespace Catch { ReporterRegistry::~ReporterRegistry() = default; - IStreamingReporterPtr ReporterRegistry::create( std::string const& name, ReporterConfig 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( config ); + return it->second->create( CATCH_MOVE(config) ); } void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { + CATCH_ENFORCE( name.find( "::" ) == name.npos, + "'::' is not allowed in reporter name: '" + name + '\'' ); m_factories.emplace(name, CATCH_MOVE(factory)); } - void ReporterRegistry::registerListener( IReporterFactoryPtr factory ) { + void ReporterRegistry::registerListener( + Detail::unique_ptr factory ) { m_listeners.push_back( CATCH_MOVE(factory) ); } @@ -4353,6 +4599,171 @@ namespace Catch { + + +#include + +namespace Catch { + + namespace { + struct kvPair { + StringRef key, value; + }; + + kvPair splitKVPair(StringRef kvString) { + auto splitPos = static_cast( 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 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( -1 ); + }; + + std::vector parts; + + while ( separatorPos < reporterSpec.size() ) { + const auto nextSeparator = findNextSeparator( separatorPos ); + parts.push_back( static_cast( reporterSpec.substr( + separatorPos, nextSeparator - separatorPos ) ) ); + + if ( nextSeparator == static_cast( -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 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 parseReporterSpec( StringRef reporterSpec ) { + auto parts = Detail::splitReporterSpec( reporterSpec ); + + assert( parts.size() > 0 && "Split should never return empty vector" ); + + std::map kvPairs; + Optional outputFileName; + Optional 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( 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 outputFileName, + Optional colourMode, + std::map 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 ) { @@ -4373,6 +4784,60 @@ namespace Catch { +#include +#include +#include + +namespace Catch { + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector 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() ); + 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::getMutable().add() ), + m_oss( Singleton::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + Singleton::getMutable().release( m_index ); + } + + std::string ReusableStringStream::str() const { + return static_cast( m_oss )->str(); + } + + void ReusableStringStream::str( std::string const& str ) { + static_cast( m_oss )->str( str ); + } + + +} + + + #include #include @@ -4514,7 +4979,7 @@ namespace Catch { 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), @@ -4951,290 +5416,125 @@ namespace Catch { CATCH_INTERNAL_ERROR("No result capture instance"); } - void seedRng(IConfig const& config) { - if (config.rngSeed() != 0) { - std::srand(config.rngSeed()); - rng().seed(config.rngSeed()); - } - } - - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } - -} - - - -namespace Catch { - - Section::Section( SectionInfo&& info ): - m_info( CATCH_MOVE( info ) ), - m_sectionIncluded( - getResultCapture().sectionStarted( m_info, m_assertions ) ) { - // Non-"included" sections will not use the timing information - // anyway, so don't bother with the potential syscall. - if (m_sectionIncluded) { - m_timer.start(); - } - } - - Section::~Section() { - if( m_sectionIncluded ) { - SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; - if( uncaught_exceptions() ) - getResultCapture().sectionEndedEarly( endInfo ); - else - getResultCapture().sectionEnded( endInfo ); - } - } - - // This indicates whether the section should be executed or not - Section::operator bool() const { - return m_sectionIncluded; - } - - -} // end namespace Catch - - - -#include - -namespace Catch { - - namespace { - static auto getSingletons() -> std::vector*& { - static std::vector* g_singletons = nullptr; - if( !g_singletons ) - g_singletons = new std::vector(); - return g_singletons; - } - } - - ISingleton::~ISingleton() = default; - - void addSingleton(ISingleton* singleton ) { - getSingletons()->push_back( singleton ); - } - void cleanupSingletons() { - auto& singletons = getSingletons(); - for( auto singleton : *singletons ) - delete singleton; - delete singletons; - singletons = nullptr; - } - -} // namespace Catch - - - -#include -#include - -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)); - } - - 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; - } - -} // end namespace Catch - - - -#include -#include -#include -#include -#include - -namespace Catch { - - Catch::IStream::~IStream() = default; - -namespace Detail { - namespace { - template - 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( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } - - int sync() override { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - struct OutputDebugWriter { - - void operator()( std::string const& str ) { - if ( !str.empty() ) { - writeToDebugConsole( str ); - } - } - }; - - /////////////////////////////////////////////////////////////////////////// - - class FileStream : public IStream { - mutable 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() 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; } - }; - - /////////////////////////////////////////////////////////////////////////// + void seedRng(IConfig const& config) { + if (config.rngSeed() != 0) { + std::srand(config.rngSeed()); + rng().seed(config.rngSeed()); + } + } - class DebugOutStream : public IStream { - Detail::unique_ptr> m_streamBuf; - mutable std::ostream m_os; - public: - DebugOutStream() - : m_streamBuf( Detail::make_unique>() ), - m_os( m_streamBuf.get() ) - {} + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } - ~DebugOutStream() override = default; +} - public: // IStream - std::ostream& stream() const override { return m_os; } - }; - } // unnamed namespace -} // namespace Detail - /////////////////////////////////////////////////////////////////////////// +namespace Catch { - auto makeStream( std::string const& filename ) -> Detail::unique_ptr { - if ( filename.empty() || filename == "-" ) { - return Detail::make_unique(); + Section::Section( SectionInfo&& info ): + m_info( CATCH_MOVE( info ) ), + m_sectionIncluded( + getResultCapture().sectionStarted( m_info, m_assertions ) ) { + // Non-"included" sections will not use the timing information + // anyway, so don't bother with the potential syscall. + if (m_sectionIncluded) { + m_timer.start(); } - else if( filename[0] == '%' ) { - if( filename == "%debug" ) - return Detail::make_unique(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; + if( uncaught_exceptions() ) + getResultCapture().sectionEndedEarly( endInfo ); else - CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' ); + getResultCapture().sectionEnded( endInfo ); } - else - return Detail::make_unique( filename ); } + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } - // This class encapsulates the idea of a pool of ostringstreams that can be reused. - struct StringStreams { - std::vector> m_streams; - std::vector 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() ); - return m_streams.size()-1; - } - else { - auto index = m_unused.back(); - m_unused.pop_back(); - return index; - } - } +} // end namespace Catch - void release( std::size_t index ) { - m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state - m_unused.push_back(index); + + +#include + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector*& { + static std::vector* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector(); + return g_singletons; } - }; + } - ReusableStringStream::ReusableStringStream() - : m_index( Singleton::getMutable().add() ), - m_oss( Singleton::getMutable().m_streams[m_index].get() ) - {} + ISingleton::~ISingleton() = default; - ReusableStringStream::~ReusableStringStream() { - static_cast( m_oss )->str(""); - m_oss->clear(); - Singleton::getMutable().release( m_index ); + void addSingleton(ISingleton* singleton ) { + getSingletons()->push_back( singleton ); + } + void cleanupSingletons() { + auto& singletons = getSingletons(); + for( auto singleton : *singletons ) + delete singleton; + delete singletons; + singletons = nullptr; } - std::string ReusableStringStream::str() const { - return static_cast( m_oss )->str(); +} // namespace Catch + + + +#include +#include + +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)); } - void ReusableStringStream::str( std::string const& str ) { - static_cast( m_oss )->str( str ); + 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; } +} // end namespace Catch + + - /////////////////////////////////////////////////////////////////////////// -#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions +#include + +namespace Catch { + +// 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 @@ -5445,36 +5745,42 @@ namespace Catch { -#include -#include - namespace Catch { - -namespace { - struct TestHasher { - using hash_t = uint64_t; - - explicit TestHasher( hash_t hashSuffix ): - m_hashSuffix( hashSuffix ) {} - - uint64_t m_hashSuffix; - - uint32_t operator()( TestCaseInfo const& t ) const { - // FNV-1a hash with multiplication fold. - const hash_t prime = 1099511628211u; - hash_t hash = 14695981039346656037u; - 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; } - hash ^= m_hashSuffix; - hash *= prime; - const uint32_t low{ static_cast(hash) }; - const uint32_t high{ static_cast(hash >> 32) }; - return low * high; } - }; -} // end anonymous namespace + hash ^= m_seed; + hash *= prime; + const uint32_t low{ static_cast( hash ) }; + const uint32_t high{ static_cast( hash >> 32 ) }; + return low * high; + } +} // namespace Catch + + + + +#include +#include + +namespace Catch { std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { switch (config.runOrder()) { @@ -5494,9 +5800,9 @@ namespace { } case TestRunOrder::Randomized: { seedRng(config); - using TestWithHash = std::pair; + using TestWithHash = std::pair; - TestHasher h{ config.rngSeed() }; + TestCaseInfoHasher h{ config.rngSeed() }; std::vector indexed_tests; indexed_tests.reserve(unsortedTestCases.size()); @@ -6996,7 +7302,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 ) { } @@ -7013,28 +7319,28 @@ namespace Matchers { 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 ); @@ -7422,7 +7728,7 @@ namespace Catch { out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush; } - void defaultListTests(std::ostream& out, std::vector const& tests, bool isFiltered, Verbosity verbosity) { + void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector 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. @@ -7442,9 +7748,9 @@ namespace Catch { Colour::Code colour = testCaseInfo.isHidden() ? Colour::SecondaryText : Colour::None; - Colour colourGuard(colour); + auto colourGuard = streamColour->guardColour( colour ).engage( out ); - out << TextFlow::Column(testCaseInfo.name).initialIndent(2).indent(4) << '\n'; + out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n'; if (verbosity >= Verbosity::High) { out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n'; } @@ -7498,13 +7804,45 @@ 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 const& descriptions ) { + defaultListReporters(m_stream, descriptions, m_config->verbosity()); + } + + void ReporterBase::listTests(std::vector const& tests) { + defaultListTests(m_stream, + m_colour.get(), + tests, + m_config->hasTestFilters(), + m_config->verbosity()); + } + + void ReporterBase::listTags(std::vector const& tags) { + defaultListTags( m_stream, tags, m_config->hasTestFilters() ); + } + +} // namespace Catch + + + + #include namespace { - // Colour::LightGrey - constexpr Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } - constexpr Catch::StringRef bothOrAll( std::uint64_t count ) { switch (count) { case 1: @@ -7522,6 +7860,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; @@ -7536,11 +7877,11 @@ 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{}; @@ -7555,13 +7896,11 @@ void printTotals(std::ostream& out, const Totals& totals) { << pluralise(totals.testCases.total(), "test case"_sr) << " (no assertions)."; } else if (totals.assertions.failed) { - Colour colour(Colour::ResultError); - out << + 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"_sr) << " with " << pluralise(totals.assertions.passed, "assertion"_sr) << '.'; @@ -7573,12 +7912,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(); @@ -7650,16 +7991,13 @@ class AssertionPrinter { 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 << ':'; } } @@ -7672,8 +8010,7 @@ class AssertionPrinter { if (result.hasExpression()) { stream << ';'; { - Colour colour(dimColour()); - stream << " expression was:"; + stream << colourImpl->guardColour(compactDimColour) << " expression was:"; } printOriginalExpression(); } @@ -7687,10 +8024,7 @@ class AssertionPrinter { void printReconstructedExpression() const { if (result.hasExpandedExpression()) { - { - Colour colour(dimColour()); - stream << " for: "; - } + stream << colourImpl->guardColour(compactDimColour) << " for: "; stream << result.getExpandedExpression(); } } @@ -7702,25 +8036,22 @@ class AssertionPrinter { } } - 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::distance(itMessage, itEnd)); - { - Colour colourGuard(colour); - stream << " with " << pluralise(N, "message"_sr) << ':'; - } + 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; } @@ -7734,6 +8065,7 @@ class AssertionPrinter { std::vector messages; std::vector::const_iterator itMessage; bool printInfoMessages; + ColourImpl* colourImpl; }; } // anon namespace @@ -7746,6 +8078,16 @@ class AssertionPrinter { m_stream << "No test cases matched '" << unmatchedSpec << "'\n"; } + 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'; + } + void CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { AssertionResult const& result = _assertionStats.assertionResult; @@ -7758,7 +8100,7 @@ class AssertionPrinter { printInfoMessages = false; } - AssertionPrinter printer( m_stream, _assertionStats, printInfoMessages ); + AssertionPrinter printer( m_stream, _assertionStats, printInfoMessages, m_colour.get() ); printer.print(); m_stream << '\n' << std::flush; @@ -7772,7 +8114,7 @@ class AssertionPrinter { } void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( m_stream, _testRunStats.totals ); + printTotals( m_stream, _testRunStats.totals, m_colour.get() ); m_stream << "\n\n" << std::flush; StreamingReporterBase::testRunEnded( _testRunStats ); } @@ -7809,13 +8151,14 @@ 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: @@ -7898,23 +8241,22 @@ class ConsoleAssertionPrinter { 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 { @@ -7927,8 +8269,8 @@ class ConsoleAssertionPrinter { } } void printSourceInfo() const { - Colour colourGuard(Colour::FileName); - stream << result.getSourceInfo() << ": "; + stream << colourImpl->guardColour( Colour::FileName ) + << result.getSourceInfo() << ": "; } std::ostream& stream; @@ -7939,6 +8281,7 @@ class ConsoleAssertionPrinter { std::string messageLabel; std::string message; std::vector messages; + ColourImpl* colourImpl; bool printInfoMessages; }; @@ -8117,9 +8460,9 @@ class TablePrinter { } }; -ConsoleReporter::ConsoleReporter(ReporterConfig const& config) - : StreamingReporterBase(config), - m_tablePrinter(Detail::make_unique(config.stream(), +ConsoleReporter::ConsoleReporter(ReporterConfig&& config): + StreamingReporterBase( CATCH_MOVE( config ) ), + m_tablePrinter(Detail::make_unique(m_stream, [&config]() -> std::vector { if (config.fullConfig()->benchmarkNoAnalysis()) { @@ -8167,7 +8510,7 @@ void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { lazyPrint(); - ConsoleAssertionPrinter printer(m_stream, _assertionStats, includeResults); + ConsoleAssertionPrinter printer(m_stream, _assertionStats, m_colour.get(), includeResults); printer.print(); m_stream << '\n' << std::flush; } @@ -8181,7 +8524,8 @@ 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) m_stream << "\nNo assertions in section"; else @@ -8239,7 +8583,7 @@ void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { } void ConsoleReporter::benchmarkFailed( StringRef error ) { - Colour colour(Colour::Red); + auto guard = m_colour->guardColour( Colour::Red ).engage( m_stream ); (*m_tablePrinter) << "Benchmark failed (" << error << ')' << ColumnBreak() << RowBreak(); @@ -8258,7 +8602,11 @@ void ConsoleReporter::testRunEnded(TestRunStats const& _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() { @@ -8278,13 +8626,12 @@ void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { } } void ConsoleReporter::lazyPrintRunInfo() { - m_stream << '\n' << lineOfChars('~') << '\n'; - Colour colour(Colour::SecondaryText); - m_stream << currentTestRunInfo.name - << " is a Catch v" << libraryVersion() << " host application.\n" - << "Run with -? for options\n\n"; - - m_stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + 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; } @@ -8293,7 +8640,7 @@ void ConsoleReporter::printTestCaseAndSectionHeader() { 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) @@ -8305,10 +8652,10 @@ void ConsoleReporter::printTestCaseAndSectionHeader() { SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; - m_stream << lineOfChars('-') << '\n'; - Colour colourGuard(Colour::FileName); - m_stream << lineInfo << '\n'; - m_stream << lineOfChars('.') << "\n\n" << std::flush; + m_stream << lineOfChars( '-' ) << '\n' + << m_colour->guardColour( Colour::FileName ) << lineInfo << '\n' + << lineOfChars( '.' ) << "\n\n" + << std::flush; } void ConsoleReporter::printClosedHeader(std::string const& _name) { @@ -8318,7 +8665,7 @@ void ConsoleReporter::printClosedHeader(std::string const& _name) { void ConsoleReporter::printOpenHeader(std::string const& _name) { m_stream << lineOfChars('-') << '\n'; { - Colour colourGuard(Colour::Headers); + auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream ); printHeaderString(_name); } } @@ -8383,9 +8730,11 @@ struct SummaryColumn { void ConsoleReporter::printTotals( Totals const& totals ) { if (totals.testCases.total() == 0) { - m_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()) { - m_stream << Colour(Colour::ResultSuccess) << "All tests passed"; + 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) << ')' @@ -8412,17 +8761,19 @@ void ConsoleReporter::printTotals( Totals const& totals ) { } void ConsoleReporter::printSummaryRow(StringRef label, std::vector 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()) { m_stream << label << ": "; - if (value != "0") + if ( value != "0" ) { m_stream << value; - else - m_stream << Colour(Colour::Warning) << "- none -"; + } else { + m_stream << m_colour->guardColour( Colour::Warning ) + << "- none -"; + } } else if (value != "0") { - m_stream << Colour(Colour::LightGrey) << " | "; - m_stream << Colour(col.colour) - << value << ' ' << col.label; + m_stream << m_colour->guardColour( Colour::LightGrey ) << " | " + << m_colour->guardColour( col.colour ) << value << ' ' + << col.label; } } m_stream << '\n'; @@ -8438,14 +8789,20 @@ void ConsoleReporter::printTotalsDivider(Totals const& totals) { while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) findMax(failedRatio, failedButOkRatio, passedRatio)--; - m_stream << Colour(Colour::Error) << std::string(failedRatio, '='); - m_stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); - if (totals.testCases.allPassed()) - m_stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); - else - m_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 { - m_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, '=' ); } m_stream << '\n'; } @@ -8453,13 +8810,6 @@ void ConsoleReporter::printSummaryDivider() { m_stream << lineOfChars('-') << '\n'; } -void ConsoleReporter::printTestFilters() { - if (m_config->testSpec().hasFilters()) { - Colour guard(Colour::BrightYellow); - m_stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; - } -} - } // end namespace Catch #if defined(_MSC_VER) @@ -8472,6 +8822,7 @@ void ConsoleReporter::printTestFilters() { + #include #include @@ -8609,21 +8960,6 @@ namespace Catch { testRunEndedCumulative(); } - void CumulativeReporterBase::listReporters(std::vector const& descriptions) { - defaultListReporters(m_stream, descriptions, m_config->verbosity()); - } - - void CumulativeReporterBase::listTests(std::vector const& tests) { - defaultListTests(m_stream, - tests, - m_config->hasTestFilters(), - m_config->verbosity()); - } - - void CumulativeReporterBase::listTags(std::vector const& tags) { - defaultListTags( m_stream, tags, m_config->hasTestFilters() ); - } - bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const { return std::any_of( assertionsAndBenchmarks.begin(), @@ -8692,8 +9028,8 @@ namespace Catch { } // anonymous namespace - JunitReporter::JunitReporter( ReporterConfig const& _config ) - : CumulativeReporterBase( _config ), + JunitReporter::JunitReporter( ReporterConfig&& _config ) + : CumulativeReporterBase( CATCH_MOVE(_config) ), xml( m_stream ) { m_preferences.shouldRedirectStdOut = true; @@ -8914,23 +9250,23 @@ namespace Catch { -#include +#include namespace Catch { - void ListeningReporter::updatePreferences(IStreamingReporter const& reporterish) { + void MultiReporter::updatePreferences(IEventListener const& reporterish) { m_preferences.shouldRedirectStdOut |= reporterish.getPreferences().shouldRedirectStdOut; m_preferences.shouldReportAllAssertions |= reporterish.getPreferences().shouldReportAllAssertions; } - void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + void MultiReporter::addListener( IEventListenerPtr&& listener ) { updatePreferences(*listener); m_reporterLikes.insert(m_reporterLikes.begin() + m_insertedListeners, CATCH_MOVE(listener) ); ++m_insertedListeners; } - void ListeningReporter::addReporter( IStreamingReporterPtr&& reporter ) { + void MultiReporter::addReporter( IEventListenerPtr&& reporter ) { updatePreferences(*reporter); // We will need to output the captured stdout if there are reporters @@ -8945,80 +9281,80 @@ namespace Catch { m_reporterLikes.push_back( CATCH_MOVE( reporter ) ); } - void ListeningReporter::noMatchingTestCases( StringRef unmatchedSpec ) { + void MultiReporter::noMatchingTestCases( StringRef unmatchedSpec ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->noMatchingTestCases( unmatchedSpec ); } } - void ListeningReporter::fatalErrorEncountered( StringRef error ) { + void MultiReporter::fatalErrorEncountered( StringRef error ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->fatalErrorEncountered( error ); } } - void ListeningReporter::reportInvalidTestSpec( StringRef arg ) { + void MultiReporter::reportInvalidTestSpec( StringRef arg ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->reportInvalidTestSpec( arg ); } } - void ListeningReporter::benchmarkPreparing( StringRef name ) { + void MultiReporter::benchmarkPreparing( StringRef name ) { for (auto& reporterish : m_reporterLikes) { reporterish->benchmarkPreparing(name); } } - void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + void MultiReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->benchmarkStarting( benchmarkInfo ); } } - void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { + void MultiReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->benchmarkEnded( benchmarkStats ); } } - void ListeningReporter::benchmarkFailed( StringRef error ) { + void MultiReporter::benchmarkFailed( StringRef error ) { for (auto& reporterish : m_reporterLikes) { reporterish->benchmarkFailed(error); } } - void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + void MultiReporter::testRunStarting( TestRunInfo const& testRunInfo ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->testRunStarting( testRunInfo ); } } - void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + void MultiReporter::testCaseStarting( TestCaseInfo const& testInfo ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->testCaseStarting( testInfo ); } } void - ListeningReporter::testCasePartialStarting( TestCaseInfo const& testInfo, + MultiReporter::testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->testCasePartialStarting( testInfo, partNumber ); } } - void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + void MultiReporter::sectionStarting( SectionInfo const& sectionInfo ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->sectionStarting( sectionInfo ); } } - void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + void MultiReporter::assertionStarting( AssertionInfo const& assertionInfo ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->assertionStarting( assertionInfo ); } } // The return value indicates if the messages buffer should be cleared: - void ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) { const bool reportByDefault = assertionStats.assertionResult.getResultType() != ResultWas::Ok || m_config->includeSuccessfulResults(); @@ -9031,13 +9367,13 @@ namespace Catch { } } - void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + void MultiReporter::sectionEnded( SectionStats const& sectionStats ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->sectionEnded( sectionStats ); } } - void ListeningReporter::testCasePartialEnded( TestCaseStats const& testStats, + void MultiReporter::testCasePartialEnded( TestCaseStats const& testStats, uint64_t partNumber ) { if ( m_preferences.shouldRedirectStdOut && m_haveNoncapturingReporters ) { @@ -9054,38 +9390,38 @@ namespace Catch { } } - void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + void MultiReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->testCaseEnded( testCaseStats ); } } - void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + void MultiReporter::testRunEnded( TestRunStats const& testRunStats ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->testRunEnded( testRunStats ); } } - void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + void MultiReporter::skipTest( TestCaseInfo const& testInfo ) { for ( auto& reporterish : m_reporterLikes ) { reporterish->skipTest( testInfo ); } } - void ListeningReporter::listReporters(std::vector const& descriptions) { + void MultiReporter::listReporters(std::vector const& descriptions) { for (auto& reporterish : m_reporterLikes) { reporterish->listReporters(descriptions); } } - void ListeningReporter::listTests(std::vector const& tests) { + void MultiReporter::listTests(std::vector const& tests) { for (auto& reporterish : m_reporterLikes) { reporterish->listTests(tests); } } - void ListeningReporter::listTags(std::vector const& tags) { + void MultiReporter::listTags(std::vector const& tags) { for (auto& reporterish : m_reporterLikes) { reporterish->listTags(tags); } @@ -9100,8 +9436,18 @@ namespace Catch { namespace Catch { + 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"_sr, '1'); } @@ -9242,26 +9588,12 @@ namespace Catch { currentTestCaseInfo = nullptr; } - void StreamingReporterBase::listReporters(std::vector const& descriptions) { - defaultListReporters( m_stream, descriptions, m_config->verbosity() ); - } - - void StreamingReporterBase::listTests(std::vector const& tests) { - defaultListTests(m_stream, - tests, - m_config->hasTestFilters(), - m_config->verbosity()); - } - - void StreamingReporterBase::listTags(std::vector const& tags) { - defaultListTags( m_stream, tags, m_config->hasTestFilters() ); - } - } // end namespace Catch #include +#include #include namespace Catch { @@ -9271,18 +9603,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(); @@ -9355,11 +9689,9 @@ namespace Catch { } private: - static Colour::Code dimColour() { return Colour::FileName; } - void printSourceInfo() const { - Colour colourGuard(dimColour()); - stream << result.getSourceInfo() << ':'; + stream << colourImpl->guardColour( tapDimColour ) + << result.getSourceInfo() << ':'; } void printResultType(StringRef passOrFail) const { @@ -9375,10 +9707,8 @@ namespace Catch { void printExpressionWas() { if (result.hasExpression()) { stream << ';'; - { - Colour colour(dimColour()); - stream << " expression was:"; - } + stream << colourImpl->guardColour( tapDimColour ) + << " expression was:"; printOriginalExpression(); } } @@ -9391,10 +9721,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; @@ -9408,7 +9736,7 @@ namespace Catch { } } - void printRemainingMessages(Colour::Code colour = dimColour()) { + void printRemainingMessages(Colour::Code colour = tapDimColour) { if (itMessage == messages.end()) { return; } @@ -9417,18 +9745,15 @@ namespace Catch { std::vector::const_iterator itEnd = messages.end(); const std::size_t N = static_cast(std::distance(itMessage, itEnd)); - { - Colour colourGuard(colour); - stream << " with " << pluralise(N, "message"_sr) << ':'; - } + 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"; } } } @@ -9441,10 +9766,15 @@ namespace Catch { std::vector::const_iterator itMessage; bool printInfoMessages; std::size_t counter; + ColourImpl* colourImpl; }; } // End anonymous namespace + void TAPReporter::testRunStarting( TestRunInfo const& ) { + m_stream << "# rng-seed: " << m_config->rngSeed() << '\n'; + } + void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) { m_stream << "# No test cases matched '" << unmatchedSpec << "'\n"; } @@ -9453,7 +9783,7 @@ namespace Catch { ++counter; m_stream << "# " << currentTestCaseInfo->name << '\n'; - TapAssertionPrinter printer(m_stream, _assertionStats, counter); + TapAssertionPrinter printer(m_stream, _assertionStats, counter, m_colour.get()); printer.print(); m_stream << '\n' << std::flush; @@ -9477,6 +9807,7 @@ namespace Catch { #include +#include namespace Catch { @@ -9647,9 +9978,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; diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index 9b8c7f93b7..a5f8df0af7 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.0.0-preview.4 -// Generated: 2022-01-03 23:14:22.246211 +// Catch v3.0.0-preview.5 +// Generated: 2022-04-20 23:45:13.582550 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -228,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, @@ -241,13 +246,13 @@ 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& defaultStream() const = 0; virtual StringRef name() const = 0; virtual bool includeSuccessfulResults() const = 0; virtual bool shouldDebugBreak() const = 0; @@ -265,7 +270,7 @@ namespace Catch { virtual uint32_t rngSeed() const = 0; virtual unsigned int shardCount() const = 0; virtual unsigned int shardIndex() const = 0; - virtual UseColour useColour() const = 0; + virtual ColourMode defaultColourMode() const = 0; virtual std::vector const& getSectionsToRun() const = 0; virtual Verbosity verbosity() const = 0; @@ -413,7 +418,6 @@ namespace Catch { #ifdef __OS400__ # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE #endif //////////////////////////////////////////////////////////////////////////////// @@ -461,7 +465,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 @@ -503,7 +507,7 @@ namespace Catch { #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) @@ -585,7 +589,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 @@ -653,6 +659,14 @@ namespace Catch { #define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif +#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 + + #endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -661,19 +675,19 @@ namespace Catch { namespace Catch { - struct IResultCapture; - struct IConfig; + class IResultCapture; + class IConfig; - struct IContext - { + class IContext { + public: virtual ~IContext(); // = default virtual IResultCapture* getResultCapture() = 0; virtual IConfig const* getConfig() const = 0; }; - struct IMutableContext : IContext - { + class IMutableContext : public IContext { + public: virtual ~IMutableContext(); // = default virtual void setResultCapture( IResultCapture* resultCapture ) = 0; virtual void setConfig( IConfig const* config ) = 0; @@ -766,7 +780,6 @@ namespace Catch { #ifndef CATCH_TOTALS_HPP_INCLUDED #define CATCH_TOTALS_HPP_INCLUDED -#include #include namespace Catch { @@ -791,7 +804,6 @@ namespace Catch { Totals delta( Totals const& prevTotals ) const; - int error = 0; Counts assertions; Counts testCases; }; @@ -910,7 +922,7 @@ namespace Catch { namespace Catch { - struct ITransientExpression; + class ITransientExpression; class LazyExpression { friend class AssertionHandler; @@ -1007,15 +1019,15 @@ namespace Catch { struct AssertionReaction; struct SourceLineInfo; - struct ITransientExpression; - struct IGeneratorTracker; + class ITransientExpression; + class IGeneratorTracker; struct BenchmarkInfo; template > struct BenchmarkStats; - struct IResultCapture { - + class IResultCapture { + public: virtual ~IResultCapture(); virtual bool sectionStarted( SectionInfo const& sectionInfo, @@ -1113,25 +1125,15 @@ namespace Catch { #include -#if defined(__clang__) && defined(__has_attribute) -# if __has_attribute(trivial_abi) -# define TRIVIAL_ABI [[clang::trivial_abi]] -# endif -#endif -#if !defined(TRIVIAL_ABI) -# define TRIVIAL_ABI -#endif - namespace Catch { namespace Detail { /** * A reimplementation of `std::unique_ptr` for improved compilation performance * - * Does not support arrays nor custom deleters, but has trivial ABI - * when supposed by the compiler. + * Does not support arrays nor custom deleters. */ template - class TRIVIAL_ABI unique_ptr { + class unique_ptr { T* m_ptr; public: constexpr unique_ptr(std::nullptr_t = nullptr): @@ -1277,6 +1279,7 @@ namespace Catch { #endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED +#include #include #include #include @@ -1287,17 +1290,30 @@ namespace Catch { struct TagInfo; struct TestCaseInfo; class TestCaseHandle; - struct IConfig; + class IConfig; + class IStream; + enum class ColourMode : std::uint8_t; struct ReporterConfig { - ReporterConfig( IConfig const* _fullConfig, std::ostream& _stream ); + ReporterConfig( IConfig const* _fullConfig, + Detail::unique_ptr _stream, + ColourMode colourMode, + std::map customOptions ); + + ReporterConfig( ReporterConfig&& ) = default; + ReporterConfig& operator=( ReporterConfig&& ) = default; + ~ReporterConfig(); // = default - std::ostream& stream() const; + Detail::unique_ptr takeStream() &&; IConfig const* fullConfig() const; + ColourMode colourMode() const; + std::map const& customOptions() const; private: - std::ostream* m_stream; + Detail::unique_ptr m_stream; IConfig const* m_fullConfig; + ColourMode m_colourMode; + std::map m_customOptions; }; struct TestRunInfo { @@ -1407,8 +1423,19 @@ namespace Catch { bool shouldReportAllAssertions = false; }; - //! The common base for all reporters and event listeners - 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; @@ -1416,9 +1443,9 @@ namespace Catch { IConfig const* m_config; public: - IStreamingReporter( IConfig const* config ): m_config( config ) {} + IEventListener( IConfig const* config ): m_config( config ) {} - virtual ~IStreamingReporter(); // = default; + virtual ~IEventListener(); // = default; // Implementing class must also provide the following static methods: // static std::string getDescription(); @@ -1488,7 +1515,7 @@ namespace Catch { virtual void listTags(std::vector const& tags) = 0; }; - using IStreamingReporterPtr = Detail::unique_ptr; + using IEventListenerPtr = Detail::unique_ptr; } // end namespace Catch @@ -1715,21 +1742,23 @@ 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; - struct IRegistryHub { + class IRegistryHub { + public: virtual ~IRegistryHub(); // = default virtual IReporterRegistry const& getReporterRegistry() const = 0; @@ -1741,10 +1770,11 @@ namespace Catch { virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; - struct 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 factory ) = 0; virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; @@ -2280,8 +2310,9 @@ namespace Catch { 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 ) -> int { + 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; @@ -2755,8 +2786,8 @@ namespace Catch { #endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED -#ifndef CATCH_STREAM_HPP_INCLUDED -#define CATCH_STREAM_HPP_INCLUDED +#ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED +#define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED #include @@ -2766,17 +2797,6 @@ namespace Catch { namespace Catch { - std::ostream& cout(); - std::ostream& cerr(); - std::ostream& clog(); - - struct IStream { - virtual ~IStream(); // = default - virtual std::ostream& stream() const = 0; - }; - - auto makeStream( std::string const& filename ) -> Detail::unique_ptr; - class ReusableStringStream : Detail::NonCopyable { std::size_t m_index; std::ostream* m_oss; @@ -2814,7 +2834,7 @@ namespace Catch { }; } -#endif // CATCH_STREAM_HPP_INCLUDED +#endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED #ifndef CATCH_VOID_TYPE_HPP_INCLUDED @@ -2856,7 +2876,8 @@ namespace Catch { }; } // namespace Detail - struct IMutableEnumValuesRegistry { + class IMutableEnumValuesRegistry { + public: virtual ~IMutableEnumValuesRegistry(); // = default; virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; @@ -3596,14 +3617,14 @@ namespace Catch { template ::value>> Approx& epsilon( T const& newEpsilon ) { - double epsilonAsDouble = static_cast(newEpsilon); + const auto epsilonAsDouble = static_cast(newEpsilon); setEpsilon(epsilonAsDouble); return *this; } template ::value>> Approx& margin( T const& newMargin ) { - double marginAsDouble = static_cast(newMargin); + const auto marginAsDouble = static_cast(newMargin); setMargin(marginAsDouble); return *this; } @@ -3701,7 +3722,7 @@ namespace Catch namespace Catch { - struct IConfig; + class IConfig; struct TestCaseInfo; class TestCaseHandle; @@ -3771,6 +3792,8 @@ namespace Catch { #ifndef CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED +#include + namespace Catch { // An optional type @@ -3881,27 +3904,246 @@ 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 -#include -#include +#include namespace Catch { - struct IStream; + enum class ColourMode : std::uint8_t; + class IStream; - struct ConfigData { - struct ReporterAndFile { - std::string reporterName; + 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(); - // If none, the output goes to the default output. - Optional outputFileName; + /** + * 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 ) &&; - friend bool operator==(ReporterAndFile const& lhs, ReporterAndFile const& rhs) { - return lhs.reporterName == rhs.reporterName && lhs.outputFileName == rhs.outputFileName; + 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; } - friend std::ostream& operator<<(std::ostream &os, ReporterAndFile const& reporter); + + 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 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 +#include +#include + +namespace Catch { + + enum class ColourMode : std::uint8_t; + + namespace Detail { + //! Splits the reporter spec into reporter name and kv-pair options + std::vector splitReporterSpec( StringRef reporterSpec ); + + Optional 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 m_outputFileName; + Optional m_colourMode; + std::map 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 outputFileName, + Optional colourMode, + std::map customOptions ); + + std::string const& name() const { return m_name; } + + Optional const& outputFile() const { + return m_outputFileName; + } + + Optional const& colourMode() const { return m_colourMode; } + + std::map 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 parseReporterSpec( StringRef reporterSpec ); + +} + +#endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + 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 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; @@ -3932,21 +4174,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 defaultOutputFilename; std::string name; std::string processName; -#ifndef CATCH_CONFIG_DEFAULT_REPORTER -#define CATCH_CONFIG_DEFAULT_REPORTER "console" -#endif - std::vector reporterSpecifications = { - {CATCH_CONFIG_DEFAULT_REPORTER, {}} - }; - // Internal: used as parser state - bool _nonDefaultReporterSpecifications = false; -#undef CATCH_CONFIG_DEFAULT_REPORTER + std::vector reporterSpecifications; std::vector testsOrTags; std::vector sectionsToRun; @@ -3964,8 +4198,9 @@ namespace Catch { bool listTags() const; bool listReporters() const; - std::vector const& getReportersAndOutputFiles() const; - std::ostream& getReporterOutputStream(std::size_t reporterIdx) const; + std::vector const& getReporterSpecs() const; + std::vector const& + getProcessedReporterSpecs() const; std::vector const& getTestsOrTags() const override; std::vector const& getSectionsToRun() const override; @@ -3977,7 +4212,6 @@ namespace Catch { // IConfig interface bool allowThrows() const override; - std::ostream& defaultStream() const override; StringRef name() const override; bool includeSuccessfulResults() const override; bool warnAboutMissingAssertions() const override; @@ -3989,7 +4223,7 @@ namespace Catch { uint32_t rngSeed() const override; unsigned int shardCount() const override; unsigned int shardIndex() const override; - UseColour useColour() const override; + ColourMode defaultColourMode() const override; bool shouldDebugBreak() const override; int abortAfter() const override; bool showInvisibles() const override; @@ -4001,11 +4235,8 @@ namespace Catch { std::chrono::milliseconds benchmarkWarmupTime() const override; private: - Detail::unique_ptr openStream(std::string const& outputFileName); ConfigData m_data; - - Detail::unique_ptr m_defaultStream; - std::vector> m_reporterStreams; + std::vector m_processedReporterSpecs; TestSpec m_testSpec; bool m_hasTestFilters = false; }; @@ -4209,7 +4440,6 @@ namespace Catch { #include -#include #include #include #include @@ -4764,12 +4994,6 @@ namespace Catch { std::shared_ptr m_name; std::shared_ptr m_ref; - template - static auto makeRef(LambdaT const& lambda) - -> std::shared_ptr { - return std::make_shared>(lambda); - } - public: ExeName(); explicit ExeName(std::string& ref); @@ -5039,7 +5263,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; @@ -5057,8 +5285,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; @@ -5271,11 +5497,11 @@ namespace Catch { #endif // CATCH_DECOMPOSER_HPP_INCLUDED +#include + namespace Catch { - struct AssertionResultData; - struct IResultCapture; - class RunContext; + class IResultCapture; struct AssertionReaction { bool shouldDebugBreak = false; @@ -5461,281 +5687,44 @@ namespace Catch { #endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED -#ifndef CATCH_PREPROCESSOR_HPP_INCLUDED -#define CATCH_PREPROCESSOR_HPP_INCLUDED +#ifndef CATCH_SECTION_HPP_INCLUDED +#define CATCH_SECTION_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 +#ifndef CATCH_TIMER_HPP_INCLUDED +#define CATCH_TIMER_HPP_INCLUDED -#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__))) +#include -#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 +namespace Catch { -#define CATCH_REC_END(...) -#define CATCH_REC_OUT + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() +} // namespace Catch -#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) +#endif // CATCH_TIMER_HPP_INCLUDED -#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__ ) +namespace Catch { -#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__ ) + class Section : Detail::NonCopyable { + public: + Section( SectionInfo&& info ); + ~Section(); -// 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)) + // This indicates whether the section should be executed or not + explicit operator bool() const; -#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()) -#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())) -#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 struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template