Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/extras/catch_amalgamated.cpp
diff options
context:
space:
mode:
authorMartin Hořeňovský2022-04-20 23:46:07 +0200
committerMartin Hořeňovský2022-04-20 23:46:07 +0200
commitf526ff0fc37ae00ff2c0dc8f6550bf8209c30afc (patch)
treee3e57df2fe5e860d97044e19ba8b241417b7b012 /extras/catch_amalgamated.cpp
parent17a04f88d90d3687bd561e89dab40e4ba6a55b83 (diff)
downloadcatch3-3.0.0-preview5.tar.gz
Bump version to v3.0.0 preview 5v3.0.0-preview5
Diffstat (limited to 'extras/catch_amalgamated.cpp')
-rw-r--r--extras/catch_amalgamated.cpp1645
1 files changed, 988 insertions, 657 deletions
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp
index e42faf64..58a1c7d5 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 <cassert>
+#include <cstddef>
#include <iterator>
#include <random>
@@ -127,11 +128,15 @@ using Catch::Benchmark::Detail::sample;
double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
auto m = Catch::Benchmark::Detail::mean(first, last);
- double variance = std::accumulate(first, last, 0., [m](double a, double b) {
- double diff = b - m;
- return a + diff * diff;
- }) / (last - first);
- return std::sqrt(variance);
+ double variance = std::accumulate( first,
+ last,
+ 0.,
+ [m]( double a, double b ) {
+ double diff = b - m;
+ return a + diff * diff;
+ } ) /
+ ( last - first );
+ return std::sqrt( variance );
}
}
@@ -491,39 +496,18 @@ namespace Catch {
-#include <ostream>
-
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 << "<default-output>";
- }
- 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<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
- std::vector<ConfigData::ReporterAndFile> const& Config::getReportersAndOutputFiles() const {
+ std::vector<ReporterSpec> 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<ProcessedReporterSpec> 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<IStream const> 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<EventListenerFactory> factory ) override {
m_reporterRegistry.registerListener( CATCH_MOVE(factory) );
}
void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& 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<ListeningReporter>(config);
+ auto multi = Detail::make_unique<MultiReporter>(config);
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
for (auto const& listener : listeners) {
- multi->addListener(listener->create(Catch::ReporterConfig(config, 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<TestCaseHandle const*> 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<int>(totals.assertions.failed)));
+ return (std::min) (MaxExitCode, static_cast<int>(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 <algorithm>
+#include <cassert>
#include <iomanip>
namespace Catch {
- ReporterConfig::ReporterConfig( IConfig const* _fullConfig, std::ostream& _stream )
- : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+ ReporterConfig::ReporterConfig(
+ IConfig const* _fullConfig,
+ Detail::unique_ptr<IStream> _stream,
+ ColourMode colourMode,
+ std::map<std::string, std::string> customOptions ):
+ m_stream( CATCH_MOVE(_stream) ),
+ m_fullConfig( _fullConfig ),
+ m_colourMode( colourMode ),
+ m_customOptions( CATCH_MOVE( customOptions ) ) {}
- std::ostream& ReporterConfig::stream() const { return *m_stream; }
+ Detail::unique_ptr<IStream> ReporterConfig::takeStream() && {
+ assert( m_stream );
+ return CATCH_MOVE( m_stream );
+ }
IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; }
+ ColourMode ReporterConfig::colourMode() const { return m_colourMode; }
+
+ std::map<std::string, std::string> const&
+ ReporterConfig::customOptions() const {
+ return m_customOptions;
+ }
+
+ ReporterConfig::~ReporterConfig() = default;
AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
std::vector<MessageInfo> 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 <algorithm>
+#include <ostream>
namespace {
bool isOptPrefix( char c ) {
@@ -2778,7 +2844,6 @@ namespace Catch {
-#include <algorithm>
#include <fstream>
#include <string>
@@ -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<ColourMode> maybeMode = Catch::Detail::stringToColourMode(toLower( colourMode ));
+ if ( !maybeMode ) {
+ return ParserResult::runtimeError(
+ "colour mode must be one of: default, ansi, win32, "
+ "or none. '" +
+ colourMode + "' is not recognised" );
+ }
+ auto mode = *maybeMode;
+ if ( !isColourImplAvailable( mode ) ) {
+ return ParserResult::runtimeError(
+ "colour mode '" + colourMode +
+ "' is not supported in this binary" );
+ }
+ config.defaultColourMode = mode;
+ return ParserResult::ok( ParseResultType::Matched );
+ };
auto const setWaitForKeypress = [&]( std::string const& keypress ) {
auto keypressLc = toLower( keypress );
if (keypressLc == "never")
@@ -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<ReporterSpec> 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 + '\'' );
- }
- }
+ IReporterRegistry::FactoryMap const& factories =
+ getRegistryHub().getReporterRegistry().getFactories();
+ auto result = factories.find( reporterSpec.name() );
- std::string reporterName;
- Optional<std::string> outputFileName;
- reporterName = reporterSpec.substr( 0, fileNameSeparatorPos );
- if ( reporterName.empty() ) {
- return ParserResult::runtimeError( "Reporter name cannot be empty." );
+ if ( result == factories.end() ) {
+ return ParserResult::runtimeError(
+ "Unrecognized reporter, '" + reporterSpec.name() +
+ "'. Check available with --list-reporters" );
}
- if ( containsFileName ) {
- outputFileName = reporterSpec.substr(
- fileNameSeparatorPos + separatorSize, reporterSpec.size() );
- }
- 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 <cassert>
#include <ostream>
+#include <utility>
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<void>( 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 <unistd.h>
+#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC )
+# define CATCH_INTERNAL_HAS_ISATTY
+# include <unistd.h>
+#endif
namespace Catch {
namespace {
- // use POSIX/ ANSI console terminal codes
- // Thanks to Adam Strzelecki for original contribution
- // (http://github.com/nanoant)
- // https://github.com/philsquared/Catch/pull/131
- class PosixColourImpl : public IColourImpl {
+ class ANSIColourImpl : public ColourImpl {
public:
- void use( Colour::Code _colourCode ) override {
+ ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {}
+
+ static bool 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()