More method bodies moved out of line

This commit is contained in:
Martin Hořeňovský 2017-07-19 10:13:47 +02:00
parent d7ff62430a
commit 4a1e898eae
27 changed files with 1199 additions and 795 deletions

View File

@ -197,6 +197,7 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_assertionresult.cpp ${HEADER_DIR}/internal/catch_assertionresult.cpp
${HEADER_DIR}/internal/catch_commandline.cpp ${HEADER_DIR}/internal/catch_commandline.cpp
${HEADER_DIR}/internal/catch_common.cpp ${HEADER_DIR}/internal/catch_common.cpp
${HEADER_DIR}/internal/catch_config.cpp
${HEADER_DIR}/internal/catch_console_colour.cpp ${HEADER_DIR}/internal/catch_console_colour.cpp
${HEADER_DIR}/internal/catch_context.cpp ${HEADER_DIR}/internal/catch_context.cpp
${HEADER_DIR}/internal/catch_debugger.cpp ${HEADER_DIR}/internal/catch_debugger.cpp
@ -209,6 +210,7 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_message.cpp ${HEADER_DIR}/internal/catch_message.cpp
${HEADER_DIR}/internal/catch_notimplemented_exception.cpp ${HEADER_DIR}/internal/catch_notimplemented_exception.cpp
${HEADER_DIR}/internal/catch_registry_hub.cpp ${HEADER_DIR}/internal/catch_registry_hub.cpp
${HEADER_DIR}/internal/catch_interfaces_reporter.cpp
${HEADER_DIR}/internal/catch_result_builder.cpp ${HEADER_DIR}/internal/catch_result_builder.cpp
${HEADER_DIR}/internal/catch_result_type.cpp ${HEADER_DIR}/internal/catch_result_type.cpp
${HEADER_DIR}/internal/catch_run_context.cpp ${HEADER_DIR}/internal/catch_run_context.cpp
@ -221,9 +223,15 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_stringref.cpp ${HEADER_DIR}/internal/catch_stringref.cpp
${HEADER_DIR}/internal/catch_tag_alias_registry.cpp ${HEADER_DIR}/internal/catch_tag_alias_registry.cpp
${HEADER_DIR}/internal/catch_test_case_info.cpp ${HEADER_DIR}/internal/catch_test_case_info.cpp
${HEADER_DIR}/internal/catch_test_case_tracker.cpp
${HEADER_DIR}/internal/catch_test_spec.cpp
${HEADER_DIR}/internal/catch_test_spec_parser.cpp
${HEADER_DIR}/internal/catch_timer.cpp ${HEADER_DIR}/internal/catch_timer.cpp
${HEADER_DIR}/internal/catch_tostring.cpp ${HEADER_DIR}/internal/catch_tostring.cpp
${HEADER_DIR}/internal/catch_totals.cpp
${HEADER_DIR}/internal/catch_version.cpp ${HEADER_DIR}/internal/catch_version.cpp
${HEADER_DIR}/internal/catch_wildcard_pattern.cpp
${HEADER_DIR}/internal/catch_xmlwriter.cpp
) )
set(INTERNAL_FILES ${IMPL_SOURCES} ${INTERNAL_HEADERS}) set(INTERNAL_FILES ${IMPL_SOURCES} ${INTERNAL_HEADERS})
CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal) CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal)
@ -232,6 +240,7 @@ CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal)
set(REPORTER_HEADERS set(REPORTER_HEADERS
${HEADER_DIR}/reporters/catch_reporter_automake.hpp ${HEADER_DIR}/reporters/catch_reporter_automake.hpp
${HEADER_DIR}/reporters/catch_reporter_bases.hpp ${HEADER_DIR}/reporters/catch_reporter_bases.hpp
${HEADER_DIR}/reporters/catch_reporter_multi.h
${HEADER_DIR}/reporters/catch_reporter_tap.hpp ${HEADER_DIR}/reporters/catch_reporter_tap.hpp
${HEADER_DIR}/reporters/catch_reporter_teamcity.hpp ${HEADER_DIR}/reporters/catch_reporter_teamcity.hpp
) )

View File

@ -11,6 +11,10 @@
namespace Catch { namespace Catch {
bool DecomposedExpression::isBinaryExpression() const {
return false;
}
AssertionInfo::AssertionInfo( char const * _macroName, AssertionInfo::AssertionInfo( char const * _macroName,
SourceLineInfo const& _lineInfo, SourceLineInfo const& _lineInfo,
char const * _capturedExpression, char const * _capturedExpression,
@ -21,6 +25,30 @@ namespace Catch {
resultDisposition( _resultDisposition ) resultDisposition( _resultDisposition )
{} {}
void AssertionResultData::negate( bool parenthesize ) {
negated = !negated;
parenthesized = parenthesize;
if( resultType == ResultWas::Ok )
resultType = ResultWas::ExpressionFailed;
else if( resultType == ResultWas::ExpressionFailed )
resultType = ResultWas::Ok;
}
std::string const& AssertionResultData::reconstructExpression() const {
if( decomposedExpression != nullptr ) {
decomposedExpression->reconstructExpression( reconstructedExpression );
if( parenthesized ) {
reconstructedExpression.insert( 0, 1, '(' );
reconstructedExpression.append( 1, ')' );
}
if( negated ) {
reconstructedExpression.insert( 0, 1, '!' );
}
decomposedExpression = nullptr;
}
return reconstructedExpression;
}
AssertionResult::AssertionResult() {} AssertionResult::AssertionResult() {}
AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )

View File

@ -18,10 +18,12 @@ namespace Catch {
struct DecomposedExpression struct DecomposedExpression
{ {
virtual ~DecomposedExpression() {} DecomposedExpression() = default;
virtual bool isBinaryExpression() const { DecomposedExpression( DecomposedExpression const& ) = default;
return false; DecomposedExpression& operator = ( DecomposedExpression const& ) = delete;
}
virtual ~DecomposedExpression() = default;
virtual bool isBinaryExpression() const;
virtual void reconstructExpression( std::string& dest ) const = 0; virtual void reconstructExpression( std::string& dest ) const = 0;
// Only simple binary comparisons can be decomposed. // Only simple binary comparisons can be decomposed.
@ -33,50 +35,26 @@ namespace Catch {
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
private:
DecomposedExpression& operator = (DecomposedExpression const&);
}; };
struct AssertionInfo struct AssertionInfo
{ {
AssertionInfo() {} AssertionInfo() = default;
AssertionInfo( char const * _macroName, AssertionInfo( char const * _macroName,
SourceLineInfo const& _lineInfo, SourceLineInfo const& _lineInfo,
char const * _capturedExpression, char const * _capturedExpression,
ResultDisposition::Flags _resultDisposition); ResultDisposition::Flags _resultDisposition);
char const * macroName; char const * macroName = nullptr;
SourceLineInfo lineInfo; SourceLineInfo lineInfo;
char const * capturedExpression; char const * capturedExpression = nullptr;
ResultDisposition::Flags resultDisposition; ResultDisposition::Flags resultDisposition;
}; };
struct AssertionResultData struct AssertionResultData
{ {
void negate( bool parenthesize ) { void negate( bool parenthesize );
negated = !negated; std::string const& reconstructExpression() const;
parenthesized = parenthesize;
if( resultType == ResultWas::Ok )
resultType = ResultWas::ExpressionFailed;
else if( resultType == ResultWas::ExpressionFailed )
resultType = ResultWas::Ok;
}
std::string const& reconstructExpression() const {
if( decomposedExpression != nullptr ) {
decomposedExpression->reconstructExpression( reconstructedExpression );
if( parenthesized ) {
reconstructedExpression.insert( 0, 1, '(' );
reconstructedExpression.append( 1, ')' );
}
if( negated ) {
reconstructedExpression.insert( 0, 1, '!' );
}
decomposedExpression = nullptr;
}
return reconstructedExpression;
}
mutable DecomposedExpression const* decomposedExpression = nullptr; mutable DecomposedExpression const* decomposedExpression = nullptr;
mutable std::string reconstructedExpression; mutable std::string reconstructedExpression;

View File

@ -0,0 +1,71 @@
/*
* Created by Martin on 19/07/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_config.hpp"
namespace Catch {
Config::Config( ConfigData const& data )
: m_data( data ),
m_stream( openStream() )
{
if( !data.testsOrTags.empty() ) {
TestSpecParser parser( ITagAliasRegistry::get() );
for( auto const& testOrTags : data.testsOrTags )
parser.parse( testOrTags );
m_testSpec = parser.testSpec();
}
}
std::string const& Config::getFilename() const {
return m_data.outputFilename ;
}
bool Config::listTests() const { return m_data.listTests; }
bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; }
bool Config::listTags() const { return m_data.listTags; }
bool Config::listReporters() const { return m_data.listReporters; }
Verbosity Config::verbosity() const { return m_data.verbosity; }
std::string Config::getProcessName() const { return m_data.processName; }
std::vector<std::string> const& Config::getReporterNames() const { return m_data.reporterNames; }
std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
TestSpec const& Config::testSpec() const { return m_testSpec; }
bool Config::showHelp() const { return m_data.showHelp; }
// IConfig interface
bool Config::allowThrows() const { return !m_data.noThrow; }
std::ostream& Config::stream() const { return m_stream->stream(); }
std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
bool Config::warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; }
unsigned int Config::rngSeed() const { return m_data.rngSeed; }
UseColour::YesOrNo Config::useColour() const { return m_data.useColour; }
bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; }
int Config::abortAfter() const { return m_data.abortAfter; }
bool Config::showInvisibles() const { return m_data.showInvisibles; }
IStream const* Config::openStream() {
if( m_data.outputFilename.empty() )
return new CoutStream();
else if( m_data.outputFilename[0] == '%' ) {
if( m_data.outputFilename == "%debug" )
return new DebugOutStream();
else
CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" );
}
else
return new FileStream( m_data.outputFilename );
}
} // end namespace Catch

View File

@ -16,7 +16,6 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <string> #include <string>
#include <stdexcept>
#ifndef CATCH_CONFIG_CONSOLE_WIDTH #ifndef CATCH_CONFIG_CONSOLE_WIDTH
#define CATCH_CONFIG_CONSOLE_WIDTH 80 #define CATCH_CONFIG_CONSOLE_WIDTH 80
@ -60,71 +59,45 @@ namespace Catch {
virtual void dummy(); virtual void dummy();
public: public:
Config() Config() = default;
{} Config( ConfigData const& data );
virtual ~Config() = default;
Config( ConfigData const& data ) std::string const& getFilename() const;
: m_data( data ),
m_stream( openStream() )
{
if( !data.testsOrTags.empty() ) {
TestSpecParser parser( ITagAliasRegistry::get() );
for( auto const& testOrTags : data.testsOrTags )
parser.parse( testOrTags );
m_testSpec = parser.testSpec();
}
}
virtual ~Config() {} bool listTests() const;
bool listTestNamesOnly() const;
bool listTags() const;
bool listReporters() const;
std::string const& getFilename() const { Verbosity verbosity() const;
return m_data.outputFilename ;
}
bool listTests() const { return m_data.listTests; } std::string getProcessName() const;
bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
bool listTags() const { return m_data.listTags; }
bool listReporters() const { return m_data.listReporters; }
Verbosity verbosity() const { return m_data.verbosity; } std::vector<std::string> const& getReporterNames() const;
std::vector<std::string> const& getSectionsToRun() const override;
std::string getProcessName() const { return m_data.processName; } virtual TestSpec const& testSpec() const override;
std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; } bool showHelp() const;
std::vector<std::string> const& getSectionsToRun() const override { return m_data.sectionsToRun; }
virtual TestSpec const& testSpec() const override { return m_testSpec; }
bool showHelp() const { return m_data.showHelp; }
// IConfig interface // IConfig interface
virtual bool allowThrows() const override { return !m_data.noThrow; } virtual bool allowThrows() const override;
virtual std::ostream& stream() const override { return m_stream->stream(); } virtual std::ostream& stream() const override;
virtual std::string name() const override { return m_data.name.empty() ? m_data.processName : m_data.name; } virtual std::string name() const override;
virtual bool includeSuccessfulResults() const override { return m_data.showSuccessfulTests; } virtual bool includeSuccessfulResults() const override;
virtual bool warnAboutMissingAssertions() const override { return m_data.warnings & WarnAbout::NoAssertions; } virtual bool warnAboutMissingAssertions() const override;
virtual ShowDurations::OrNot showDurations() const override { return m_data.showDurations; } virtual ShowDurations::OrNot showDurations() const override;
virtual RunTests::InWhatOrder runOrder() const override { return m_data.runOrder; } virtual RunTests::InWhatOrder runOrder() const override;
virtual unsigned int rngSeed() const override { return m_data.rngSeed; } virtual unsigned int rngSeed() const override;
virtual UseColour::YesOrNo useColour() const override { return m_data.useColour; } virtual UseColour::YesOrNo useColour() const override;
virtual bool shouldDebugBreak() const override { return m_data.shouldDebugBreak; } virtual bool shouldDebugBreak() const override;
virtual int abortAfter() const override { return m_data.abortAfter; } virtual int abortAfter() const override;
virtual bool showInvisibles() const override { return m_data.showInvisibles; } virtual bool showInvisibles() const override;
private: private:
IStream const* openStream() { IStream const* openStream();
if( m_data.outputFilename.empty() )
return new CoutStream();
else if( m_data.outputFilename[0] == '%' ) {
if( m_data.outputFilename == "%debug" )
return new DebugOutStream();
else
CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" );
}
else
return new FileStream( m_data.outputFilename );
}
ConfigData m_data; ConfigData m_data;
std::unique_ptr<IStream const> m_stream; std::unique_ptr<IStream const> m_stream;

View File

@ -55,33 +55,13 @@ namespace Catch {
IMutableRegistryHub::~IMutableRegistryHub() {} IMutableRegistryHub::~IMutableRegistryHub() {}
IExceptionTranslator::~IExceptionTranslator() {} IExceptionTranslator::~IExceptionTranslator() {}
IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
IReporterFactory::~IReporterFactory() {}
IReporterRegistry::~IReporterRegistry() {}
IStreamingReporter::~IStreamingReporter() {}
AssertionStats::~AssertionStats() {}
SectionStats::~SectionStats() {}
TestCaseStats::~TestCaseStats() {}
TestGroupStats::~TestGroupStats() {}
TestRunStats::~TestRunStats() {}
IRunner::~IRunner() {} IRunner::~IRunner() {}
IMutableContext::~IMutableContext() {} IMutableContext::~IMutableContext() {}
IConfig::~IConfig() {} IConfig::~IConfig() {}
WildcardPattern::~WildcardPattern() {}
TestSpec::Pattern::~Pattern() {}
TestSpec::NamePattern::~NamePattern() {}
TestSpec::TagPattern::~TagPattern() {}
TestSpec::ExcludedPattern::~ExcludedPattern() {}
Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {}
void Config::dummy() {} void Config::dummy() {}
namespace TestCaseTracking {
ITracker::~ITracker() {}
TrackerBase::~TrackerBase() {}
SectionTracker::~SectionTracker() {}
IndexTracker::~IndexTracker() {}
}
} }
#ifdef __clang__ #ifdef __clang__

View File

@ -0,0 +1,120 @@
/*
* Created by Martin on 19/07/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_interfaces_reporter.h"
#include "../reporters/catch_reporter_multi.h"
namespace Catch {
ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig )
: m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream )
: m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
std::ostream& ReporterConfig::stream() const { return *m_stream; }
IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; }
TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {}
GroupInfo::GroupInfo( std::string const& _name,
std::size_t _groupIndex,
std::size_t _groupsCount )
: name( _name ),
groupIndex( _groupIndex ),
groupsCounts( _groupsCount )
{}
AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
std::vector<MessageInfo> const& _infoMessages,
Totals const& _totals )
: assertionResult( _assertionResult ),
infoMessages( _infoMessages ),
totals( _totals )
{
if( assertionResult.hasMessage() ) {
// Copy message into messages list.
// !TBD This should have been done earlier, somewhere
MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
builder << assertionResult.getMessage();
builder.m_info.message = builder.m_stream.str();
infoMessages.push_back( builder.m_info );
}
}
SectionStats::SectionStats( SectionInfo const& _sectionInfo,
Counts const& _assertions,
double _durationInSeconds,
bool _missingAssertions )
: sectionInfo( _sectionInfo ),
assertions( _assertions ),
durationInSeconds( _durationInSeconds ),
missingAssertions( _missingAssertions )
{}
TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals,
std::string const& _stdOut,
std::string const& _stdErr,
bool _aborting )
: testInfo( _testInfo ),
totals( _totals ),
stdOut( _stdOut ),
stdErr( _stdErr ),
aborting( _aborting )
{}
TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo,
Totals const& _totals,
bool _aborting )
: groupInfo( _groupInfo ),
totals( _totals ),
aborting( _aborting )
{}
TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo )
: groupInfo( _groupInfo ),
aborting( false )
{}
TestRunStats::TestRunStats( TestRunInfo const& _runInfo,
Totals const& _totals,
bool _aborting )
: runInfo( _runInfo ),
totals( _totals ),
aborting( _aborting )
{}
bool IStreamingReporter::isMulti() const { return false; }
void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) {
if( !existingReporter ) {
existingReporter = std::move( additionalReporter );
return;
}
MultipleReporters* multi = nullptr;
if( existingReporter->isMulti() ) {
multi = static_cast<MultipleReporters*>( existingReporter.get() );
}
else {
auto newMulti = std::unique_ptr<MultipleReporters>( new MultipleReporters );
newMulti->add( std::move( existingReporter ) );
multi = newMulti.get();
existingReporter = std::move( newMulti );
}
multi->add( std::move( additionalReporter ) );
}
} // end namespace Catch

View File

@ -10,29 +10,29 @@
#include "catch_section_info.h" #include "catch_section_info.h"
#include "catch_common.h" #include "catch_common.h"
#include "catch_totals.hpp"
#include "catch_config.hpp" #include "catch_config.hpp"
#include "catch_context.h"
#include "catch_totals.hpp"
#include "catch_test_case_info.h" #include "catch_test_case_info.h"
#include "catch_assertionresult.h" #include "catch_assertionresult.h"
#include "catch_message.h" #include "catch_message.h"
#include "catch_option.hpp" #include "catch_option.hpp"
#include <string> #include <string>
#include <ostream> #include <iosfwd>
#include <map> #include <map>
#include <memory> #include <memory>
namespace Catch namespace Catch {
{
struct ReporterConfig { struct ReporterConfig {
explicit ReporterConfig( IConfigPtr const& _fullConfig ) explicit ReporterConfig( IConfigPtr const& _fullConfig );
: m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream );
: m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
std::ostream& stream() const { return *m_stream; } std::ostream& stream() const;
IConfigPtr fullConfig() const { return m_fullConfig; } IConfigPtr fullConfig() const;
private: private:
std::ostream* m_stream; std::ostream* m_stream;
@ -40,16 +40,11 @@ namespace Catch
}; };
struct ReporterPreferences { struct ReporterPreferences {
ReporterPreferences() bool shouldRedirectStdOut = false;
: shouldRedirectStdOut( false )
{}
bool shouldRedirectStdOut;
}; };
template<typename T> template<typename T>
struct LazyStat : Option<T> { struct LazyStat : Option<T> {
LazyStat() : used( false ) {}
LazyStat& operator=( T const& _value ) { LazyStat& operator=( T const& _value ) {
Option<T>::operator=( _value ); Option<T>::operator=( _value );
used = false; used = false;
@ -59,21 +54,17 @@ namespace Catch
Option<T>::reset(); Option<T>::reset();
used = false; used = false;
} }
bool used; bool used = false;
}; };
struct TestRunInfo { struct TestRunInfo {
TestRunInfo( std::string const& _name ) : name( _name ) {} TestRunInfo( std::string const& _name );
std::string name; std::string name;
}; };
struct GroupInfo { struct GroupInfo {
GroupInfo( std::string const& _name, GroupInfo( std::string const& _name,
std::size_t _groupIndex, std::size_t _groupIndex,
std::size_t _groupsCount ) std::size_t _groupsCount );
: name( _name ),
groupIndex( _groupIndex ),
groupsCounts( _groupsCount )
{}
std::string name; std::string name;
std::size_t groupIndex; std::size_t groupIndex;
@ -83,27 +74,13 @@ namespace Catch
struct AssertionStats { struct AssertionStats {
AssertionStats( AssertionResult const& _assertionResult, AssertionStats( AssertionResult const& _assertionResult,
std::vector<MessageInfo> const& _infoMessages, std::vector<MessageInfo> const& _infoMessages,
Totals const& _totals ) Totals const& _totals );
: assertionResult( _assertionResult ),
infoMessages( _infoMessages ),
totals( _totals )
{
if( assertionResult.hasMessage() ) {
// Copy message into messages list.
// !TBD This should have been done earlier, somewhere
MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
builder << assertionResult.getMessage();
builder.m_info.message = builder.m_stream.str();
infoMessages.push_back( builder.m_info );
}
}
virtual ~AssertionStats();
AssertionStats( AssertionStats const& ) = default; AssertionStats( AssertionStats const& ) = default;
AssertionStats( AssertionStats && ) = default; AssertionStats( AssertionStats && ) = default;
AssertionStats& operator = ( AssertionStats const& ) = default; AssertionStats& operator = ( AssertionStats const& ) = default;
AssertionStats& operator = ( AssertionStats && ) = default; AssertionStats& operator = ( AssertionStats && ) = default;
virtual ~AssertionStats() = default;
AssertionResult assertionResult; AssertionResult assertionResult;
std::vector<MessageInfo> infoMessages; std::vector<MessageInfo> infoMessages;
@ -114,17 +91,12 @@ namespace Catch
SectionStats( SectionInfo const& _sectionInfo, SectionStats( SectionInfo const& _sectionInfo,
Counts const& _assertions, Counts const& _assertions,
double _durationInSeconds, double _durationInSeconds,
bool _missingAssertions ) bool _missingAssertions );
: sectionInfo( _sectionInfo ),
assertions( _assertions ),
durationInSeconds( _durationInSeconds ),
missingAssertions( _missingAssertions )
{}
virtual ~SectionStats();
SectionStats( SectionStats const& ) = default; SectionStats( SectionStats const& ) = default;
SectionStats( SectionStats && ) = default; SectionStats( SectionStats && ) = default;
SectionStats& operator = ( SectionStats const& ) = default; SectionStats& operator = ( SectionStats const& ) = default;
SectionStats& operator = ( SectionStats && ) = default; SectionStats& operator = ( SectionStats && ) = default;
virtual ~SectionStats() = default;
SectionInfo sectionInfo; SectionInfo sectionInfo;
Counts assertions; Counts assertions;
@ -137,19 +109,13 @@ namespace Catch
Totals const& _totals, Totals const& _totals,
std::string const& _stdOut, std::string const& _stdOut,
std::string const& _stdErr, std::string const& _stdErr,
bool _aborting ) bool _aborting );
: testInfo( _testInfo ),
totals( _totals ),
stdOut( _stdOut ),
stdErr( _stdErr ),
aborting( _aborting )
{}
virtual ~TestCaseStats();
TestCaseStats( TestCaseStats const& ) = default; TestCaseStats( TestCaseStats const& ) = default;
TestCaseStats( TestCaseStats && ) = default; TestCaseStats( TestCaseStats && ) = default;
TestCaseStats& operator = ( TestCaseStats const& ) = default; TestCaseStats& operator = ( TestCaseStats const& ) = default;
TestCaseStats& operator = ( TestCaseStats && ) = default; TestCaseStats& operator = ( TestCaseStats && ) = default;
virtual ~TestCaseStats() = default;
TestCaseInfo testInfo; TestCaseInfo testInfo;
Totals totals; Totals totals;
@ -161,21 +127,14 @@ namespace Catch
struct TestGroupStats { struct TestGroupStats {
TestGroupStats( GroupInfo const& _groupInfo, TestGroupStats( GroupInfo const& _groupInfo,
Totals const& _totals, Totals const& _totals,
bool _aborting ) bool _aborting );
: groupInfo( _groupInfo ), TestGroupStats( GroupInfo const& _groupInfo );
totals( _totals ),
aborting( _aborting )
{}
TestGroupStats( GroupInfo const& _groupInfo )
: groupInfo( _groupInfo ),
aborting( false )
{}
virtual ~TestGroupStats();
TestGroupStats( TestGroupStats const& ) = default; TestGroupStats( TestGroupStats const& ) = default;
TestGroupStats( TestGroupStats && ) = default; TestGroupStats( TestGroupStats && ) = default;
TestGroupStats& operator = ( TestGroupStats const& ) = default; TestGroupStats& operator = ( TestGroupStats const& ) = default;
TestGroupStats& operator = ( TestGroupStats && ) = default; TestGroupStats& operator = ( TestGroupStats && ) = default;
virtual ~TestGroupStats() = default;
GroupInfo groupInfo; GroupInfo groupInfo;
Totals totals; Totals totals;
@ -185,17 +144,13 @@ namespace Catch
struct TestRunStats { struct TestRunStats {
TestRunStats( TestRunInfo const& _runInfo, TestRunStats( TestRunInfo const& _runInfo,
Totals const& _totals, Totals const& _totals,
bool _aborting ) bool _aborting );
: runInfo( _runInfo ),
totals( _totals ),
aborting( _aborting )
{}
virtual ~TestRunStats();
TestRunStats( TestRunStats const& ) = default; TestRunStats( TestRunStats const& ) = default;
TestRunStats( TestRunStats && ) = default; TestRunStats( TestRunStats && ) = default;
TestRunStats& operator = ( TestRunStats const& ) = default; TestRunStats& operator = ( TestRunStats const& ) = default;
TestRunStats& operator = ( TestRunStats && ) = default; TestRunStats& operator = ( TestRunStats && ) = default;
virtual ~TestRunStats() = default;
TestRunInfo runInfo; TestRunInfo runInfo;
Totals totals; Totals totals;
@ -205,7 +160,7 @@ namespace Catch
class MultipleReporters; class MultipleReporters;
struct IStreamingReporter { struct IStreamingReporter {
virtual ~IStreamingReporter(); virtual ~IStreamingReporter() = default;
// Implementing class must also provide the following static method: // Implementing class must also provide the following static method:
// static std::string getDescription(); // static std::string getDescription();
@ -232,12 +187,12 @@ namespace Catch
virtual void skipTest( TestCaseInfo const& testInfo ) = 0; virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
virtual bool isMulti() const { return false; } virtual bool isMulti() const;
}; };
using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>; using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>;
struct IReporterFactory { struct IReporterFactory {
virtual ~IReporterFactory(); virtual ~IReporterFactory() = default;
virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0;
virtual std::string getDescription() const = 0; virtual std::string getDescription() const = 0;
}; };
@ -247,13 +202,14 @@ namespace Catch
using FactoryMap = std::map<std::string, IReporterFactoryPtr>; using FactoryMap = std::map<std::string, IReporterFactoryPtr>;
using Listeners = std::vector<IReporterFactoryPtr>; using Listeners = std::vector<IReporterFactoryPtr>;
virtual ~IReporterRegistry(); virtual ~IReporterRegistry() = default;
virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0;
virtual FactoryMap const& getFactories() const = 0; virtual FactoryMap const& getFactories() const = 0;
virtual Listeners const& getListeners() const = 0; virtual Listeners const& getListeners() const = 0;
}; };
void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter );
}
} // end namespace Catch
#endif // TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED #endif // TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED

View File

@ -19,6 +19,16 @@
namespace Catch { namespace Catch {
CopyableStream::CopyableStream( CopyableStream const& other ) {
oss << other.oss.str();
}
CopyableStream& CopyableStream::operator=( CopyableStream const& other ) {
oss.str(std::string());
oss << other.oss.str();
return *this;
}
ResultBuilder::ResultBuilder( char const* macroName, ResultBuilder::ResultBuilder( char const* macroName,
SourceLineInfo const& lineInfo, SourceLineInfo const& lineInfo,
char const* capturedExpression, char const* capturedExpression,

View File

@ -21,14 +21,9 @@ namespace Catch {
struct CopyableStream { struct CopyableStream {
CopyableStream() = default; CopyableStream() = default;
CopyableStream( CopyableStream const& other ) { CopyableStream( CopyableStream const& other );
oss << other.oss.str(); CopyableStream& operator=( CopyableStream const& other );
}
CopyableStream& operator=( CopyableStream const& other ) {
oss.str(std::string());
oss << other.oss.str();
return *this;
}
std::ostringstream oss; std::ostringstream oss;
}; };

View File

@ -1,5 +1,7 @@
#include "catch_run_context.hpp" #include "catch_run_context.hpp"
#include <cassert>
namespace Catch { namespace Catch {
StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString)

View File

@ -22,7 +22,6 @@
#include "catch_result_builder.h" #include "catch_result_builder.h"
#include "catch_fatal_condition.h" #include "catch_fatal_condition.h"
#include <set>
#include <string> #include <string>
namespace Catch { namespace Catch {

View File

@ -0,0 +1,277 @@
/*
* Created by Martin on 19/07/2017
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_test_case_tracker.hpp"
#include <algorithm>
#include <assert.h>
#include <stdexcept>
#include <memory>
CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
namespace Catch {
namespace TestCaseTracking {
NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
: name( _name ),
location( _location )
{}
TrackerContext& TrackerContext::instance() {
static TrackerContext s_instance;
return s_instance;
}
ITracker& TrackerContext::startRun() {
m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
m_currentTracker = nullptr;
m_runState = Executing;
return *m_rootTracker;
}
void TrackerContext::endRun() {
m_rootTracker.reset();
m_currentTracker = nullptr;
m_runState = NotStarted;
}
void TrackerContext::startCycle() {
m_currentTracker = m_rootTracker.get();
m_runState = Executing;
}
void TrackerContext::completeCycle() {
m_runState = CompletedCycle;
}
bool TrackerContext::completedCycle() const {
return m_runState == CompletedCycle;
}
ITracker& TrackerContext::currentTracker() {
return *m_currentTracker;
}
void TrackerContext::setCurrentTracker( ITracker* tracker ) {
m_currentTracker = tracker;
}
TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) {
return
tracker->nameAndLocation().name == m_nameAndLocation.name &&
tracker->nameAndLocation().location == m_nameAndLocation.location;
}
TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: m_nameAndLocation( nameAndLocation ),
m_ctx( ctx ),
m_parent( parent )
{}
NameAndLocation const& TrackerBase::nameAndLocation() const {
return m_nameAndLocation;
}
bool TrackerBase::isComplete() const {
return m_runState == CompletedSuccessfully || m_runState == Failed;
}
bool TrackerBase::isSuccessfullyCompleted() const {
return m_runState == CompletedSuccessfully;
}
bool TrackerBase::isOpen() const {
return m_runState != NotStarted && !isComplete();
}
bool TrackerBase::hasChildren() const {
return !m_children.empty();
}
void TrackerBase::addChild( ITrackerPtr const& child ) {
m_children.push_back( child );
}
ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) {
auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
return( it != m_children.end() )
? *it
: nullptr;
}
ITracker& TrackerBase::parent() {
assert( m_parent ); // Should always be non-null except for root
return *m_parent;
}
void TrackerBase::openChild() {
if( m_runState != ExecutingChildren ) {
m_runState = ExecutingChildren;
if( m_parent )
m_parent->openChild();
}
}
bool TrackerBase::isSectionTracker() const { return false; }
bool TrackerBase::isIndexTracker() const { return false; }
void TrackerBase::open() {
m_runState = Executing;
moveToThis();
if( m_parent )
m_parent->openChild();
}
void TrackerBase::close() {
// Close any still open children (e.g. generators)
while( &m_ctx.currentTracker() != this )
m_ctx.currentTracker().close();
switch( m_runState ) {
case NeedsAnotherRun:
break;;
case Executing:
m_runState = CompletedSuccessfully;
break;
case ExecutingChildren:
if( m_children.empty() || m_children.back()->isComplete() )
m_runState = CompletedSuccessfully;
break;
case NotStarted:
case CompletedSuccessfully:
case Failed:
CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
default:
CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
}
moveToParent();
m_ctx.completeCycle();
}
void TrackerBase::fail() {
m_runState = Failed;
if( m_parent )
m_parent->markAsNeedingAnotherRun();
moveToParent();
m_ctx.completeCycle();
}
void TrackerBase::markAsNeedingAnotherRun() {
m_runState = NeedsAnotherRun;
}
void TrackerBase::moveToParent() {
assert( m_parent );
m_ctx.setCurrentTracker( m_parent );
}
void TrackerBase::moveToThis() {
m_ctx.setCurrentTracker( this );
}
SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: TrackerBase( nameAndLocation, ctx, parent )
{
if( parent ) {
while( !parent->isSectionTracker() )
parent = &parent->parent();
SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
addNextFilters( parentSection.m_filters );
}
}
bool SectionTracker::isSectionTracker() const { return true; }
SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
std::shared_ptr<SectionTracker> section;
ITracker& currentTracker = ctx.currentTracker();
if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isSectionTracker() );
section = std::static_pointer_cast<SectionTracker>( childTracker );
}
else {
section = std::make_shared<SectionTracker>( nameAndLocation, ctx, &currentTracker );
currentTracker.addChild( section );
}
if( !ctx.completedCycle() )
section->tryOpen();
return *section;
}
void SectionTracker::tryOpen() {
if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
open();
}
void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
if( !filters.empty() ) {
m_filters.push_back(""); // Root - should never be consulted
m_filters.push_back(""); // Test Case - not a section filter
m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
}
}
void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
if( filters.size() > 1 )
m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
}
IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
: TrackerBase( nameAndLocation, ctx, parent ),
m_size( size )
{}
bool IndexTracker::isIndexTracker() const { return true; }
IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
std::shared_ptr<IndexTracker> tracker;
ITracker& currentTracker = ctx.currentTracker();
if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isIndexTracker() );
tracker = std::static_pointer_cast<IndexTracker>( childTracker );
}
else {
tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, &currentTracker, size );
currentTracker.addChild( tracker );
}
if( !ctx.completedCycle() && !tracker->isComplete() ) {
if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
tracker->moveNext();
tracker->open();
}
return *tracker;
}
int IndexTracker::index() const { return m_index; }
void IndexTracker::moveNext() {
m_index++;
m_children.clear();
}
void IndexTracker::close() {
TrackerBase::close();
if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
m_runState = Executing;
}
} // namespace TestCaseTracking
using TestCaseTracking::ITracker;
using TestCaseTracking::TrackerContext;
using TestCaseTracking::SectionTracker;
using TestCaseTracking::IndexTracker;
} // namespace Catch
CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS

View File

@ -11,11 +11,8 @@
#include "catch_compiler_capabilities.h" #include "catch_compiler_capabilities.h"
#include "catch_common.h" #include "catch_common.h"
#include <algorithm>
#include <string> #include <string>
#include <assert.h>
#include <vector> #include <vector>
#include <stdexcept>
#include <memory> #include <memory>
CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
@ -27,10 +24,7 @@ namespace TestCaseTracking {
std::string name; std::string name;
SourceLineInfo location; SourceLineInfo location;
NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
: name( _name ),
location( _location )
{}
}; };
struct ITracker; struct ITracker;
@ -38,7 +32,7 @@ namespace TestCaseTracking {
using ITrackerPtr = std::shared_ptr<ITracker>; using ITrackerPtr = std::shared_ptr<ITracker>;
struct ITracker { struct ITracker {
virtual ~ITracker(); virtual ~ITracker() = default;
// static queries // static queries
virtual NameAndLocation const& nameAndLocation() const = 0; virtual NameAndLocation const& nameAndLocation() const = 0;
@ -79,36 +73,17 @@ namespace TestCaseTracking {
public: public:
static TrackerContext& instance() { static TrackerContext& instance();
static TrackerContext s_instance;
return s_instance;
}
ITracker& startRun(); ITracker& startRun();
void endRun();
void endRun() { void startCycle();
m_rootTracker.reset(); void completeCycle();
m_currentTracker = nullptr;
m_runState = NotStarted;
}
void startCycle() { bool completedCycle() const;
m_currentTracker = m_rootTracker.get(); ITracker& currentTracker();
m_runState = Executing; void setCurrentTracker( ITracker* tracker );
}
void completeCycle() {
m_runState = CompletedCycle;
}
bool completedCycle() const {
return m_runState == CompletedCycle;
}
ITracker& currentTracker() {
return *m_currentTracker;
}
void setCurrentTracker( ITracker* tracker ) {
m_currentTracker = tracker;
}
}; };
class TrackerBase : public ITracker { class TrackerBase : public ITracker {
@ -121,239 +96,87 @@ namespace TestCaseTracking {
CompletedSuccessfully, CompletedSuccessfully,
Failed Failed
}; };
class TrackerHasName { class TrackerHasName {
NameAndLocation m_nameAndLocation; NameAndLocation m_nameAndLocation;
public: public:
TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} TrackerHasName( NameAndLocation const& nameAndLocation );
bool operator ()( ITrackerPtr const& tracker ) { bool operator ()( ITrackerPtr const& tracker );
return
tracker->nameAndLocation().name == m_nameAndLocation.name &&
tracker->nameAndLocation().location == m_nameAndLocation.location;
}
}; };
typedef std::vector<ITrackerPtr> Children; typedef std::vector<ITrackerPtr> Children;
NameAndLocation m_nameAndLocation; NameAndLocation m_nameAndLocation;
TrackerContext& m_ctx; TrackerContext& m_ctx;
ITracker* m_parent; ITracker* m_parent;
Children m_children; Children m_children;
CycleState m_runState = NotStarted; CycleState m_runState = NotStarted;
public: public:
TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
: m_nameAndLocation( nameAndLocation ), virtual ~TrackerBase() = default;
m_ctx( ctx ),
m_parent( parent )
{}
virtual ~TrackerBase();
virtual NameAndLocation const& nameAndLocation() const override { virtual NameAndLocation const& nameAndLocation() const override;
return m_nameAndLocation; virtual bool isComplete() const override;
} virtual bool isSuccessfullyCompleted() const override;
virtual bool isComplete() const override { virtual bool isOpen() const override;
return m_runState == CompletedSuccessfully || m_runState == Failed; virtual bool hasChildren() const override;
}
virtual bool isSuccessfullyCompleted() const override {
return m_runState == CompletedSuccessfully;
}
virtual bool isOpen() const override {
return m_runState != NotStarted && !isComplete();
}
virtual bool hasChildren() const override {
return !m_children.empty();
}
virtual void addChild( ITrackerPtr const& child ) override { virtual void addChild( ITrackerPtr const& child ) override;
m_children.push_back( child );
}
virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override { virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override;
auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); virtual ITracker& parent() override;
return( it != m_children.end() )
? *it
: nullptr;
}
virtual ITracker& parent() override {
assert( m_parent ); // Should always be non-null except for root
return *m_parent;
}
virtual void openChild() override { virtual void openChild() override;
if( m_runState != ExecutingChildren ) {
m_runState = ExecutingChildren;
if( m_parent )
m_parent->openChild();
}
}
virtual bool isSectionTracker() const override { return false; } virtual bool isSectionTracker() const override;
virtual bool isIndexTracker() const override { return false; } virtual bool isIndexTracker() const override;
void open() { void open();
m_runState = Executing;
moveToThis();
if( m_parent )
m_parent->openChild();
}
virtual void close() override { virtual void close() override;
virtual void fail() override;
virtual void markAsNeedingAnotherRun() override;
// Close any still open children (e.g. generators)
while( &m_ctx.currentTracker() != this )
m_ctx.currentTracker().close();
switch( m_runState ) {
case NeedsAnotherRun:
break;;
case Executing:
m_runState = CompletedSuccessfully;
break;
case ExecutingChildren:
if( m_children.empty() || m_children.back()->isComplete() )
m_runState = CompletedSuccessfully;
break;
case NotStarted:
case CompletedSuccessfully:
case Failed:
CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
default:
CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
}
moveToParent();
m_ctx.completeCycle();
}
virtual void fail() override {
m_runState = Failed;
if( m_parent )
m_parent->markAsNeedingAnotherRun();
moveToParent();
m_ctx.completeCycle();
}
virtual void markAsNeedingAnotherRun() override {
m_runState = NeedsAnotherRun;
}
private: private:
void moveToParent() { void moveToParent();
assert( m_parent ); void moveToThis();
m_ctx.setCurrentTracker( m_parent );
}
void moveToThis() {
m_ctx.setCurrentTracker( this );
}
}; };
class SectionTracker : public TrackerBase { class SectionTracker : public TrackerBase {
std::vector<std::string> m_filters; std::vector<std::string> m_filters;
public: public:
SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
: TrackerBase( nameAndLocation, ctx, parent ) virtual ~SectionTracker() = default;
{
if( parent ) {
while( !parent->isSectionTracker() )
parent = &parent->parent();
SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); virtual bool isSectionTracker() const override;
addNextFilters( parentSection.m_filters );
}
}
virtual ~SectionTracker();
virtual bool isSectionTracker() const override { return true; } static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { void tryOpen();
std::shared_ptr<SectionTracker> section;
ITracker& currentTracker = ctx.currentTracker(); void addInitialFilters( std::vector<std::string> const& filters );
if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { void addNextFilters( std::vector<std::string> const& filters );
assert( childTracker );
assert( childTracker->isSectionTracker() );
section = std::static_pointer_cast<SectionTracker>( childTracker );
}
else {
section = std::make_shared<SectionTracker>( nameAndLocation, ctx, &currentTracker );
currentTracker.addChild( section );
}
if( !ctx.completedCycle() )
section->tryOpen();
return *section;
}
void tryOpen() {
if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
open();
}
void addInitialFilters( std::vector<std::string> const& filters ) {
if( !filters.empty() ) {
m_filters.push_back(""); // Root - should never be consulted
m_filters.push_back(""); // Test Case - not a section filter
m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
}
}
void addNextFilters( std::vector<std::string> const& filters ) {
if( filters.size() > 1 )
m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
}
}; };
class IndexTracker : public TrackerBase { class IndexTracker : public TrackerBase {
int m_size; int m_size;
int m_index = -1; int m_index = -1;
public: public:
IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size );
: TrackerBase( nameAndLocation, ctx, parent ), virtual ~IndexTracker() = default;
m_size( size )
{}
virtual ~IndexTracker();
virtual bool isIndexTracker() const override { return true; } virtual bool isIndexTracker() const override;
static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size );
std::shared_ptr<IndexTracker> tracker;
ITracker& currentTracker = ctx.currentTracker(); int index() const;
if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isIndexTracker() );
tracker = std::static_pointer_cast<IndexTracker>( childTracker );
}
else {
tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, &currentTracker, size );
currentTracker.addChild( tracker );
}
if( !ctx.completedCycle() && !tracker->isComplete() ) { void moveNext();
if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
tracker->moveNext();
tracker->open();
}
return *tracker; virtual void close() override;
}
int index() const { return m_index; }
void moveNext() {
m_index++;
m_children.clear();
}
virtual void close() override {
TrackerBase::close();
if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
m_runState = Executing;
}
}; };
inline ITracker& TrackerContext::startRun() {
m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
m_currentTracker = nullptr;
m_runState = Executing;
return *m_rootTracker;
}
} // namespace TestCaseTracking } // namespace TestCaseTracking
using TestCaseTracking::ITracker; using TestCaseTracking::ITracker;

View File

@ -0,0 +1,50 @@
/*
* Created by Martin on 19/07/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_test_spec.hpp"
#include <string>
#include <vector>
#include <memory>
namespace Catch {
TestSpec::NamePattern::NamePattern( std::string const& name )
: m_wildcardPattern( toLower( name ), CaseSensitive::No )
{}
bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
return m_wildcardPattern.matches( toLower( testCase.name ) );
}
TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
}
TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
// All patterns in a filter must match for the filter to be a match
for( auto const& pattern : m_patterns ) {
if( !pattern->matches( testCase ) )
return false;
}
return true;
}
bool TestSpec::hasFilters() const {
return !m_filters.empty();
}
bool TestSpec::matches( TestCaseInfo const& testCase ) const {
// A TestSpec matches if any filter matches
for( auto const& filter : m_filters )
if( filter.matches( testCase ) )
return true;
return false;
}
}

View File

@ -24,40 +24,34 @@ namespace Catch {
class TestSpec { class TestSpec {
struct Pattern { struct Pattern {
virtual ~Pattern(); virtual ~Pattern() = default;
virtual bool matches( TestCaseInfo const& testCase ) const = 0; virtual bool matches( TestCaseInfo const& testCase ) const = 0;
}; };
using PatternPtr = std::shared_ptr<Pattern>; using PatternPtr = std::shared_ptr<Pattern>;
class NamePattern : public Pattern { class NamePattern : public Pattern {
public: public:
NamePattern( std::string const& name ) NamePattern( std::string const& name );
: m_wildcardPattern( toLower( name ), CaseSensitive::No ) virtual ~NamePattern() = default;
{} virtual bool matches( TestCaseInfo const& testCase ) const;
virtual ~NamePattern();
virtual bool matches( TestCaseInfo const& testCase ) const {
return m_wildcardPattern.matches( toLower( testCase.name ) );
}
private: private:
WildcardPattern m_wildcardPattern; WildcardPattern m_wildcardPattern;
}; };
class TagPattern : public Pattern { class TagPattern : public Pattern {
public: public:
TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} TagPattern( std::string const& tag );
virtual ~TagPattern(); virtual ~TagPattern() = default;
virtual bool matches( TestCaseInfo const& testCase ) const { virtual bool matches( TestCaseInfo const& testCase ) const;
return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
}
private: private:
std::string m_tag; std::string m_tag;
}; };
class ExcludedPattern : public Pattern { class ExcludedPattern : public Pattern {
public: public:
ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} ExcludedPattern( PatternPtr const& underlyingPattern );
virtual ~ExcludedPattern(); virtual ~ExcludedPattern() = default;
virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } virtual bool matches( TestCaseInfo const& testCase ) const;
private: private:
PatternPtr m_underlyingPattern; PatternPtr m_underlyingPattern;
}; };
@ -65,27 +59,12 @@ namespace Catch {
struct Filter { struct Filter {
std::vector<PatternPtr> m_patterns; std::vector<PatternPtr> m_patterns;
bool matches( TestCaseInfo const& testCase ) const { bool matches( TestCaseInfo const& testCase ) const;
// All patterns in a filter must match for the filter to be a match
for( auto const& pattern : m_patterns ) {
if( !pattern->matches( testCase ) )
return false;
}
return true;
}
}; };
public: public:
bool hasFilters() const { bool hasFilters() const;
return !m_filters.empty(); bool matches( TestCaseInfo const& testCase ) const;
}
bool matches( TestCaseInfo const& testCase ) const {
// A TestSpec matches if any filter matches
for( auto const& filter : m_filters )
if( filter.matches( testCase ) )
return true;
return false;
}
private: private:
std::vector<Filter> m_filters; std::vector<Filter> m_filters;

View File

@ -0,0 +1,87 @@
/*
* Created by Martin on 19/07/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_test_spec_parser.hpp"
namespace Catch {
TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
m_mode = None;
m_exclusion = false;
m_start = std::string::npos;
m_arg = m_tagAliases->expandAliases( arg );
m_escapeChars.clear();
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
visitChar( m_arg[m_pos] );
if( m_mode == Name )
addPattern<TestSpec::NamePattern>();
return *this;
}
TestSpec TestSpecParser::testSpec() {
addFilter();
return m_testSpec;
}
void TestSpecParser::visitChar( char c ) {
if( m_mode == None ) {
switch( c ) {
case ' ': return;
case '~': m_exclusion = true; return;
case '[': return startNewMode( Tag, ++m_pos );
case '"': return startNewMode( QuotedName, ++m_pos );
case '\\': return escape();
default: startNewMode( Name, m_pos ); break;
}
}
if( m_mode == Name ) {
if( c == ',' ) {
addPattern<TestSpec::NamePattern>();
addFilter();
}
else if( c == '[' ) {
if( subString() == "exclude:" )
m_exclusion = true;
else
addPattern<TestSpec::NamePattern>();
startNewMode( Tag, ++m_pos );
}
else if( c == '\\' )
escape();
}
else if( m_mode == EscapedName )
m_mode = Name;
else if( m_mode == QuotedName && c == '"' )
addPattern<TestSpec::NamePattern>();
else if( m_mode == Tag && c == ']' )
addPattern<TestSpec::TagPattern>();
}
void TestSpecParser::startNewMode( Mode mode, std::size_t start ) {
m_mode = mode;
m_start = start;
}
void TestSpecParser::escape() {
if( m_mode == None )
m_start = m_pos;
m_mode = EscapedName;
m_escapeChars.push_back( m_pos );
}
std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
void TestSpecParser::addFilter() {
if( !m_currentFilter.m_patterns.empty() ) {
m_testSpec.m_filters.push_back( m_currentFilter );
m_currentFilter = TestSpec::Filter();
}
}
TestSpec parseTestSpec( std::string const& arg ) {
return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
}
} // namespace Catch

View File

@ -30,69 +30,17 @@ namespace Catch {
ITagAliasRegistry const* m_tagAliases; ITagAliasRegistry const* m_tagAliases;
public: public:
TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} TestSpecParser( ITagAliasRegistry const& tagAliases );
TestSpecParser& parse( std::string const& arg );
TestSpec testSpec();
TestSpecParser& parse( std::string const& arg ) {
m_mode = None;
m_exclusion = false;
m_start = std::string::npos;
m_arg = m_tagAliases->expandAliases( arg );
m_escapeChars.clear();
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
visitChar( m_arg[m_pos] );
if( m_mode == Name )
addPattern<TestSpec::NamePattern>();
return *this;
}
TestSpec testSpec() {
addFilter();
return m_testSpec;
}
private: private:
void visitChar( char c ) { void visitChar( char c );
if( m_mode == None ) { void startNewMode( Mode mode, std::size_t start );
switch( c ) { void escape();
case ' ': return; std::string subString() const;
case '~': m_exclusion = true; return;
case '[': return startNewMode( Tag, ++m_pos );
case '"': return startNewMode( QuotedName, ++m_pos );
case '\\': return escape();
default: startNewMode( Name, m_pos ); break;
}
}
if( m_mode == Name ) {
if( c == ',' ) {
addPattern<TestSpec::NamePattern>();
addFilter();
}
else if( c == '[' ) {
if( subString() == "exclude:" )
m_exclusion = true;
else
addPattern<TestSpec::NamePattern>();
startNewMode( Tag, ++m_pos );
}
else if( c == '\\' )
escape();
}
else if( m_mode == EscapedName )
m_mode = Name;
else if( m_mode == QuotedName && c == '"' )
addPattern<TestSpec::NamePattern>();
else if( m_mode == Tag && c == ']' )
addPattern<TestSpec::TagPattern>();
}
void startNewMode( Mode mode, std::size_t start ) {
m_mode = mode;
m_start = start;
}
void escape() {
if( m_mode == None )
m_start = m_pos;
m_mode = EscapedName;
m_escapeChars.push_back( m_pos );
}
std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
template<typename T> template<typename T>
void addPattern() { void addPattern() {
std::string token = subString(); std::string token = subString();
@ -112,16 +60,10 @@ namespace Catch {
m_exclusion = false; m_exclusion = false;
m_mode = None; m_mode = None;
} }
void addFilter() {
if( !m_currentFilter.m_patterns.empty() ) { void addFilter();
m_testSpec.m_filters.push_back( m_currentFilter );
m_currentFilter = TestSpec::Filter();
}
}
}; };
inline TestSpec parseTestSpec( std::string const& arg ) { TestSpec parseTestSpec( std::string const& arg );
return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
}
} // namespace Catch } // namespace Catch

View File

@ -0,0 +1,61 @@
/*
* Created by Martin on 19/07/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_totals.hpp"
namespace Catch {
Counts Counts::operator - ( Counts const& other ) const {
Counts diff;
diff.passed = passed - other.passed;
diff.failed = failed - other.failed;
diff.failedButOk = failedButOk - other.failedButOk;
return diff;
}
Counts& Counts::operator += ( Counts const& other ) {
passed += other.passed;
failed += other.failed;
failedButOk += other.failedButOk;
return *this;
}
std::size_t Counts::total() const {
return passed + failed + failedButOk;
}
bool Counts::allPassed() const {
return failed == 0 && failedButOk == 0;
}
bool Counts::allOk() const {
return failed == 0;
}
Totals Totals::operator - ( Totals const& other ) const {
Totals diff;
diff.assertions = assertions - other.assertions;
diff.testCases = testCases - other.testCases;
return diff;
}
Totals& Totals::operator += ( Totals const& other ) {
assertions += other.assertions;
testCases += other.testCases;
return *this;
}
Totals Totals::delta( Totals const& prevTotals ) const {
Totals diff = *this - prevTotals;
if( diff.assertions.failed > 0 )
++diff.testCases.failed;
else if( diff.assertions.failedButOk > 0 )
++diff.testCases.failedButOk;
else
++diff.testCases.passed;
return diff;
}
}

View File

@ -13,29 +13,12 @@
namespace Catch { namespace Catch {
struct Counts { struct Counts {
Counts operator - ( Counts const& other ) const { Counts operator - ( Counts const& other ) const;
Counts diff; Counts& operator += ( Counts const& other );
diff.passed = passed - other.passed;
diff.failed = failed - other.failed;
diff.failedButOk = failedButOk - other.failedButOk;
return diff;
}
Counts& operator += ( Counts const& other ) {
passed += other.passed;
failed += other.failed;
failedButOk += other.failedButOk;
return *this;
}
std::size_t total() const { std::size_t total() const;
return passed + failed + failedButOk; bool allPassed() const;
} bool allOk() const;
bool allPassed() const {
return failed == 0 && failedButOk == 0;
}
bool allOk() const {
return failed == 0;
}
std::size_t passed = 0; std::size_t passed = 0;
std::size_t failed = 0; std::size_t failed = 0;
@ -44,29 +27,11 @@ namespace Catch {
struct Totals { struct Totals {
Totals operator - ( Totals const& other ) const { Totals operator - ( Totals const& other ) const;
Totals diff; Totals& operator += ( Totals const& other );
diff.assertions = assertions - other.assertions;
diff.testCases = testCases - other.testCases;
return diff;
}
Totals delta( Totals const& prevTotals ) const { Totals delta( Totals const& prevTotals ) const;
Totals diff = *this - prevTotals;
if( diff.assertions.failed > 0 )
++diff.testCases.failed;
else if( diff.assertions.failedButOk > 0 )
++diff.testCases.failedButOk;
else
++diff.testCases.passed;
return diff;
}
Totals& operator += ( Totals const& other ) {
assertions += other.assertions;
testCases += other.testCases;
return *this;
}
Counts assertions; Counts assertions;
Counts testCases; Counts testCases;

View File

@ -0,0 +1,46 @@
/*
* Created by Martin on 19/07/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_wildcard_pattern.hpp"
namespace Catch {
WildcardPattern::WildcardPattern( std::string const& pattern,
CaseSensitive::Choice caseSensitivity )
: m_caseSensitivity( caseSensitivity ),
m_pattern( adjustCase( pattern ) )
{
if( startsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 1 );
m_wildcard = WildcardAtStart;
}
if( endsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
}
}
bool WildcardPattern::matches( std::string const& str ) const {
switch( m_wildcard ) {
case NoWildcard:
return m_pattern == adjustCase( str );
case WildcardAtStart:
return endsWith( adjustCase( str ), m_pattern );
case WildcardAtEnd:
return startsWith( adjustCase( str ), m_pattern );
case WildcardAtBothEnds:
return contains( adjustCase( str ), m_pattern );
default:
CATCH_INTERNAL_ERROR( "Unknown enum" );
}
}
std::string WildcardPattern::adjustCase( std::string const& str ) const {
return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
}
}

View File

@ -25,38 +25,12 @@ namespace Catch
public: public:
WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity );
: m_caseSensitivity( caseSensitivity ), virtual ~WildcardPattern() = default;
m_pattern( adjustCase( pattern ) ) virtual bool matches( std::string const& str ) const;
{
if( startsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 1 );
m_wildcard = WildcardAtStart;
}
if( endsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
}
}
virtual ~WildcardPattern();
virtual bool matches( std::string const& str ) const {
switch( m_wildcard ) {
case NoWildcard:
return m_pattern == adjustCase( str );
case WildcardAtStart:
return endsWith( adjustCase( str ), m_pattern );
case WildcardAtEnd:
return startsWith( adjustCase( str ), m_pattern );
case WildcardAtBothEnds:
return contains( adjustCase( str ), m_pattern );
default:
CATCH_INTERNAL_ERROR( "Unknown enum" );
}
}
private: private:
std::string adjustCase( std::string const& str ) const { std::string adjustCase( std::string const& str ) const;
return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
}
CaseSensitive::Choice m_caseSensitivity; CaseSensitive::Choice m_caseSensitivity;
WildcardPosition m_wildcard = NoWildcard; WildcardPosition m_wildcard = NoWildcard;
std::string m_pattern; std::string m_pattern;

View File

@ -0,0 +1,181 @@
/*
* Created by Phil on 19/07/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_xmlwriter.hpp"
#include <iomanip>
namespace Catch {
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
: m_str( str ),
m_forWhat( forWhat )
{}
void XmlEncode::encodeTo( std::ostream& os ) const {
// Apostrophe escaping not necessary if we always use " to write attributes
// (see: http://www.w3.org/TR/xml/#syntax)
for( std::size_t i = 0; i < m_str.size(); ++ i ) {
char c = m_str[i];
switch( c ) {
case '<': os << "&lt;"; break;
case '&': os << "&amp;"; break;
case '>':
// See: http://www.w3.org/TR/xml/#syntax
if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
os << "&gt;";
else
os << c;
break;
case '\"':
if( m_forWhat == ForAttributes )
os << "&quot;";
else
os << c;
break;
default:
// Escape control chars - based on contribution by @espenalb in PR #465 and
// by @mrpi PR #588
if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
// see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<int>( c );
}
else
os << c;
}
}
}
std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
xmlEncode.encodeTo( os );
return os;
}
XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
: m_writer( writer )
{}
XmlWriter::ScopedElement::ScopedElement( ScopedElement const& other )
: m_writer( other.m_writer ){
other.m_writer = nullptr;
}
XmlWriter::ScopedElement::~ScopedElement() {
if( m_writer )
m_writer->endElement();
}
XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
m_writer->writeText( text, indent );
return *this;
}
XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
{
writeDeclaration();
}
XmlWriter::~XmlWriter() {
while( !m_tags.empty() )
endElement();
}
XmlWriter& XmlWriter::startElement( std::string const& name ) {
ensureTagClosed();
newlineIfNecessary();
m_os << m_indent << '<' << name;
m_tags.push_back( name );
m_indent += " ";
m_tagIsOpen = true;
return *this;
}
XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
ScopedElement scoped( this );
startElement( name );
return scoped;
}
XmlWriter& XmlWriter::endElement() {
newlineIfNecessary();
m_indent = m_indent.substr( 0, m_indent.size()-2 );
if( m_tagIsOpen ) {
m_os << "/>";
m_tagIsOpen = false;
}
else {
m_os << m_indent << "</" << m_tags.back() << ">";
}
m_os << std::endl;
m_tags.pop_back();
return *this;
}
XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
if( !name.empty() && !attribute.empty() )
m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
return *this;
}
XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
return *this;
}
XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
if( !text.empty() ){
bool tagWasOpen = m_tagIsOpen;
ensureTagClosed();
if( tagWasOpen && indent )
m_os << m_indent;
m_os << XmlEncode( text );
m_needsNewline = true;
}
return *this;
}
XmlWriter& XmlWriter::writeComment( std::string const& text ) {
ensureTagClosed();
m_os << m_indent << "<!--" << text << "-->";
m_needsNewline = true;
return *this;
}
void XmlWriter::writeStylesheetRef( std::string const& url ) {
m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
}
XmlWriter& XmlWriter::writeBlankLine() {
ensureTagClosed();
m_os << '\n';
return *this;
}
void XmlWriter::ensureTagClosed() {
if( m_tagIsOpen ) {
m_os << ">" << std::endl;
m_tagIsOpen = false;
}
}
void XmlWriter::writeDeclaration() {
m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
}
void XmlWriter::newlineIfNecessary() {
if( m_needsNewline ) {
m_os << std::endl;
m_needsNewline = false;
}
}
}

View File

@ -12,9 +12,7 @@
#include "catch_compiler_capabilities.h" #include "catch_compiler_capabilities.h"
#include <sstream> #include <sstream>
#include <string>
#include <vector> #include <vector>
#include <iomanip>
namespace Catch { namespace Catch {
@ -22,55 +20,11 @@ namespace Catch {
public: public:
enum ForWhat { ForTextNodes, ForAttributes }; enum ForWhat { ForTextNodes, ForAttributes };
XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
: m_str( str ),
m_forWhat( forWhat )
{}
void encodeTo( std::ostream& os ) const { void encodeTo( std::ostream& os ) const;
// Apostrophe escaping not necessary if we always use " to write attributes friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
// (see: http://www.w3.org/TR/xml/#syntax)
for( std::size_t i = 0; i < m_str.size(); ++ i ) {
char c = m_str[i];
switch( c ) {
case '<': os << "&lt;"; break;
case '&': os << "&amp;"; break;
case '>':
// See: http://www.w3.org/TR/xml/#syntax
if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
os << "&gt;";
else
os << c;
break;
case '\"':
if( m_forWhat == ForAttributes )
os << "&quot;";
else
os << c;
break;
default:
// Escape control chars - based on contribution by @espenalb in PR #465 and
// by @mrpi PR #588
if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
// see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<int>( c );
}
else
os << c;
}
}
}
friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
xmlEncode.encodeTo( os );
return os;
}
private: private:
std::string m_str; std::string m_str;
@ -82,24 +36,13 @@ namespace Catch {
class ScopedElement { class ScopedElement {
public: public:
ScopedElement( XmlWriter* writer ) ScopedElement( XmlWriter* writer );
: m_writer( writer )
{}
ScopedElement( ScopedElement const& other ) ScopedElement( ScopedElement const& other );
: m_writer( other.m_writer ){
other.m_writer = nullptr;
}
~ScopedElement() { ~ScopedElement();
if( m_writer )
m_writer->endElement();
}
ScopedElement& writeText( std::string const& text, bool indent = true ) { ScopedElement& writeText( std::string const& text, bool indent = true );
m_writer->writeText( text, indent );
return *this;
}
template<typename T> template<typename T>
ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
@ -111,57 +54,21 @@ namespace Catch {
mutable XmlWriter* m_writer; mutable XmlWriter* m_writer;
}; };
XmlWriter( std::ostream& os = Catch::cout() ) : m_os( os ) XmlWriter( std::ostream& os = Catch::cout() );
{ ~XmlWriter();
writeDeclaration();
}
~XmlWriter() { XmlWriter( XmlWriter const& ) = delete;
while( !m_tags.empty() ) XmlWriter& operator=( XmlWriter const& ) = delete;
endElement();
}
XmlWriter& startElement( std::string const& name ) { XmlWriter& startElement( std::string const& name );
ensureTagClosed();
newlineIfNecessary();
m_os << m_indent << '<' << name;
m_tags.push_back( name );
m_indent += " ";
m_tagIsOpen = true;
return *this;
}
ScopedElement scopedElement( std::string const& name ) { ScopedElement scopedElement( std::string const& name );
ScopedElement scoped( this );
startElement( name );
return scoped;
}
XmlWriter& endElement() { XmlWriter& endElement();
newlineIfNecessary();
m_indent = m_indent.substr( 0, m_indent.size()-2 );
if( m_tagIsOpen ) {
m_os << "/>";
m_tagIsOpen = false;
}
else {
m_os << m_indent << "</" << m_tags.back() << ">";
}
m_os << std::endl;
m_tags.pop_back();
return *this;
}
XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
if( !name.empty() && !attribute.empty() )
m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
return *this;
}
XmlWriter& writeAttribute( std::string const& name, bool attribute ) { XmlWriter& writeAttribute( std::string const& name, bool attribute );
m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
return *this;
}
template<typename T> template<typename T>
XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
@ -171,56 +78,21 @@ namespace Catch {
return writeAttribute( name, m_oss.str() ); return writeAttribute( name, m_oss.str() );
} }
XmlWriter& writeText( std::string const& text, bool indent = true ) { XmlWriter& writeText( std::string const& text, bool indent = true );
if( !text.empty() ){
bool tagWasOpen = m_tagIsOpen;
ensureTagClosed();
if( tagWasOpen && indent )
m_os << m_indent;
m_os << XmlEncode( text );
m_needsNewline = true;
}
return *this;
}
XmlWriter& writeComment( std::string const& text ) { XmlWriter& writeComment( std::string const& text );
ensureTagClosed();
m_os << m_indent << "<!--" << text << "-->";
m_needsNewline = true;
return *this;
}
void writeStylesheetRef( std::string const& url ) { void writeStylesheetRef( std::string const& url );
m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
}
XmlWriter& writeBlankLine() { XmlWriter& writeBlankLine();
ensureTagClosed();
m_os << '\n';
return *this;
}
void ensureTagClosed() { void ensureTagClosed();
if( m_tagIsOpen ) {
m_os << ">" << std::endl;
m_tagIsOpen = false;
}
}
private: private:
XmlWriter( XmlWriter const& );
void operator=( XmlWriter const& );
void writeDeclaration() { void writeDeclaration();
m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
}
void newlineIfNecessary() { void newlineIfNecessary();
if( m_needsNewline ) {
m_os << std::endl;
m_needsNewline = false;
}
}
bool m_tagIsOpen = false; bool m_tagIsOpen = false;
bool m_needsNewline = false; bool m_needsNewline = false;

View File

@ -15,6 +15,8 @@
#include <assert.h> #include <assert.h>
#include <ctime>
namespace Catch { namespace Catch {
namespace { namespace {

View File

@ -6,119 +6,86 @@
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/ */
#include "../internal/catch_interfaces_reporter.h" #include "catch_reporter_multi.h"
namespace Catch { namespace Catch {
class MultipleReporters : public IStreamingReporter { void MultipleReporters::add( IStreamingReporterPtr&& reporter ) {
typedef std::vector<IStreamingReporterPtr> Reporters;
Reporters m_reporters;
public:
void add( IStreamingReporterPtr&& reporter ) {
m_reporters.push_back( std::move( reporter ) ); m_reporters.push_back( std::move( reporter ) );
} }
public: // IStreamingReporter ReporterPreferences MultipleReporters::getPreferences() const {
virtual ReporterPreferences getPreferences() const override {
return m_reporters[0]->getPreferences(); return m_reporters[0]->getPreferences();
} }
virtual void noMatchingTestCases( std::string const& spec ) override { void MultipleReporters::noMatchingTestCases( std::string const& spec ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->noMatchingTestCases( spec ); reporter->noMatchingTestCases( spec );
} }
virtual void testRunStarting( TestRunInfo const& testRunInfo ) override { void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->testRunStarting( testRunInfo ); reporter->testRunStarting( testRunInfo );
} }
virtual void testGroupStarting( GroupInfo const& groupInfo ) override { void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->testGroupStarting( groupInfo ); reporter->testGroupStarting( groupInfo );
} }
virtual void testCaseStarting( TestCaseInfo const& testInfo ) override { void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->testCaseStarting( testInfo ); reporter->testCaseStarting( testInfo );
} }
virtual void sectionStarting( SectionInfo const& sectionInfo ) override { void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->sectionStarting( sectionInfo ); reporter->sectionStarting( sectionInfo );
} }
void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) {
virtual void assertionStarting( AssertionInfo const& assertionInfo ) override {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->assertionStarting( assertionInfo ); reporter->assertionStarting( assertionInfo );
} }
// The return value indicates if the messages buffer should be cleared: // The return value indicates if the messages buffer should be cleared:
virtual bool assertionEnded( AssertionStats const& assertionStats ) override { bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) {
bool clearBuffer = false; bool clearBuffer = false;
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
clearBuffer |= reporter->assertionEnded( assertionStats ); clearBuffer |= reporter->assertionEnded( assertionStats );
return clearBuffer; return clearBuffer;
} }
virtual void sectionEnded( SectionStats const& sectionStats ) override { void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->sectionEnded( sectionStats ); reporter->sectionEnded( sectionStats );
} }
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) override { void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->testCaseEnded( testCaseStats ); reporter->testCaseEnded( testCaseStats );
} }
virtual void testGroupEnded( TestGroupStats const& testGroupStats ) override { void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->testGroupEnded( testGroupStats ); reporter->testGroupEnded( testGroupStats );
} }
virtual void testRunEnded( TestRunStats const& testRunStats ) override { void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->testRunEnded( testRunStats ); reporter->testRunEnded( testRunStats );
} }
virtual void skipTest( TestCaseInfo const& testInfo ) override { void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) {
for( auto const& reporter : m_reporters ) for( auto const& reporter : m_reporters )
reporter->skipTest( testInfo ); reporter->skipTest( testInfo );
} }
virtual bool isMulti() const override { bool MultipleReporters::isMulti() const {
return true; return true;
} }
};
void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) {
if( !existingReporter ) {
existingReporter = std::move( additionalReporter );
return;
}
MultipleReporters* multi = nullptr;
if( existingReporter->isMulti() ) {
multi = static_cast<MultipleReporters*>( existingReporter.get() );
}
else {
auto newMulti = std::unique_ptr<MultipleReporters>( new MultipleReporters );
newMulti->add( std::move( existingReporter ) );
multi = newMulti.get();
existingReporter = std::move( newMulti );
}
multi->add( std::move( additionalReporter ) );
}
} // end namespace Catch } // end namespace Catch

View File

@ -0,0 +1,57 @@
/*
* Created by Martin on 19/07/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "../internal/catch_interfaces_reporter.h"
namespace Catch {
class MultipleReporters : public IStreamingReporter {
typedef std::vector<IStreamingReporterPtr> Reporters;
Reporters m_reporters;
public:
void add( IStreamingReporterPtr&& reporter );
public: // IStreamingReporter
virtual ReporterPreferences getPreferences() const override;
virtual void noMatchingTestCases( std::string const& spec ) override;
virtual void testRunStarting( TestRunInfo const& testRunInfo ) override;
virtual void testGroupStarting( GroupInfo const& groupInfo ) override;
virtual void testCaseStarting( TestCaseInfo const& testInfo ) override;
virtual void sectionStarting( SectionInfo const& sectionInfo ) override;
virtual void assertionStarting( AssertionInfo const& assertionInfo ) override;
// The return value indicates if the messages buffer should be cleared:
virtual bool assertionEnded( AssertionStats const& assertionStats ) override;
virtual void sectionEnded( SectionStats const& sectionStats ) override;
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) override;
virtual void testGroupEnded( TestGroupStats const& testGroupStats ) override;
virtual void testRunEnded( TestRunStats const& testRunStats ) override;
virtual void skipTest( TestCaseInfo const& testInfo ) override;
virtual bool isMulti() const override;
};
} // end namespace Catch