diff --git a/CMakeLists.txt b/CMakeLists.txt index ff300ca7..f427977c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,7 @@ set(IMPL_SOURCES ${HEADER_DIR}/internal/catch_assertionresult.cpp ${HEADER_DIR}/internal/catch_commandline.cpp ${HEADER_DIR}/internal/catch_common.cpp + ${HEADER_DIR}/internal/catch_config.cpp ${HEADER_DIR}/internal/catch_console_colour.cpp ${HEADER_DIR}/internal/catch_context.cpp ${HEADER_DIR}/internal/catch_debugger.cpp @@ -209,6 +210,7 @@ set(IMPL_SOURCES ${HEADER_DIR}/internal/catch_message.cpp ${HEADER_DIR}/internal/catch_notimplemented_exception.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_type.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_tag_alias_registry.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_tostring.cpp + ${HEADER_DIR}/internal/catch_totals.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}) CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal) @@ -232,6 +240,7 @@ CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal) set(REPORTER_HEADERS ${HEADER_DIR}/reporters/catch_reporter_automake.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_teamcity.hpp ) diff --git a/include/internal/catch_assertionresult.cpp b/include/internal/catch_assertionresult.cpp index 77c69281..9a4b0c65 100644 --- a/include/internal/catch_assertionresult.cpp +++ b/include/internal/catch_assertionresult.cpp @@ -11,6 +11,10 @@ namespace Catch { + bool DecomposedExpression::isBinaryExpression() const { + return false; + } + AssertionInfo::AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, char const * _capturedExpression, @@ -21,6 +25,30 @@ namespace Catch { 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( AssertionInfo const& info, AssertionResultData const& data ) diff --git a/include/internal/catch_assertionresult.h b/include/internal/catch_assertionresult.h index 26546d38..c9a08b9a 100644 --- a/include/internal/catch_assertionresult.h +++ b/include/internal/catch_assertionresult.h @@ -18,10 +18,12 @@ namespace Catch { struct DecomposedExpression { - virtual ~DecomposedExpression() {} - virtual bool isBinaryExpression() const { - return false; - } + DecomposedExpression() = default; + DecomposedExpression( DecomposedExpression const& ) = default; + DecomposedExpression& operator = ( DecomposedExpression const& ) = delete; + + virtual ~DecomposedExpression() = default; + virtual bool isBinaryExpression() const; virtual void reconstructExpression( std::string& dest ) const = 0; // Only simple binary comparisons can be decomposed. @@ -33,50 +35,26 @@ namespace Catch { template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); - - private: - DecomposedExpression& operator = (DecomposedExpression const&); }; struct AssertionInfo { - AssertionInfo() {} + AssertionInfo() = default; AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, char const * _capturedExpression, ResultDisposition::Flags _resultDisposition); - char const * macroName; + char const * macroName = nullptr; SourceLineInfo lineInfo; - char const * capturedExpression; + char const * capturedExpression = nullptr; ResultDisposition::Flags resultDisposition; }; struct AssertionResultData { - void 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& 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; - } + void negate( bool parenthesize ); + std::string const& reconstructExpression() const; mutable DecomposedExpression const* decomposedExpression = nullptr; mutable std::string reconstructedExpression; diff --git a/include/internal/catch_config.cpp b/include/internal/catch_config.cpp new file mode 100644 index 00000000..cf7c0c6e --- /dev/null +++ b/include/internal/catch_config.cpp @@ -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 const& Config::getReporterNames() const { return m_data.reporterNames; } + std::vector 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 diff --git a/include/internal/catch_config.hpp b/include/internal/catch_config.hpp index bc1f5b26..add50100 100644 --- a/include/internal/catch_config.hpp +++ b/include/internal/catch_config.hpp @@ -16,7 +16,6 @@ #include #include #include -#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -60,71 +59,45 @@ namespace Catch { virtual void dummy(); public: - Config() - {} + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; - 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& getFilename() const; - virtual ~Config() {} + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; - std::string const& getFilename() const { - return m_data.outputFilename ; - } + Verbosity verbosity() const; - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } + std::string getProcessName() const; - Verbosity verbosity() const { return m_data.verbosity; } + std::vector const& getReporterNames() const; + std::vector const& getSectionsToRun() const override; - std::string getProcessName() const { return m_data.processName; } + virtual TestSpec const& testSpec() const override; - std::vector const& getReporterNames() const { return m_data.reporterNames; } - std::vector const& getSectionsToRun() const override { return m_data.sectionsToRun; } - - virtual TestSpec const& testSpec() const override { return m_testSpec; } - - bool showHelp() const { return m_data.showHelp; } + bool showHelp() const; // IConfig interface - virtual bool allowThrows() const override { return !m_data.noThrow; } - virtual std::ostream& stream() const override { return m_stream->stream(); } - virtual std::string name() const override { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const override { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const override { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const override { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const override { return m_data.runOrder; } - virtual unsigned int rngSeed() const override { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const override { return m_data.useColour; } - virtual bool shouldDebugBreak() const override { return m_data.shouldDebugBreak; } - virtual int abortAfter() const override { return m_data.abortAfter; } - virtual bool showInvisibles() const override { return m_data.showInvisibles; } + virtual bool allowThrows() const override; + virtual std::ostream& stream() const override; + virtual std::string name() const override; + virtual bool includeSuccessfulResults() const override; + virtual bool warnAboutMissingAssertions() const override; + virtual ShowDurations::OrNot showDurations() const override; + virtual RunTests::InWhatOrder runOrder() const override; + virtual unsigned int rngSeed() const override; + virtual UseColour::YesOrNo useColour() const override; + virtual bool shouldDebugBreak() const override; + virtual int abortAfter() const override; + virtual bool showInvisibles() const override; private: - 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 ); - } + IStream const* openStream(); ConfigData m_data; std::unique_ptr m_stream; diff --git a/include/internal/catch_impl.hpp b/include/internal/catch_impl.hpp index 96b7a452..94696662 100644 --- a/include/internal/catch_impl.hpp +++ b/include/internal/catch_impl.hpp @@ -55,33 +55,13 @@ namespace Catch { IMutableRegistryHub::~IMutableRegistryHub() {} IExceptionTranslator::~IExceptionTranslator() {} IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} - IReporterFactory::~IReporterFactory() {} - IReporterRegistry::~IReporterRegistry() {} - IStreamingReporter::~IStreamingReporter() {} - AssertionStats::~AssertionStats() {} - SectionStats::~SectionStats() {} - TestCaseStats::~TestCaseStats() {} - TestGroupStats::~TestGroupStats() {} - TestRunStats::~TestRunStats() {} IRunner::~IRunner() {} IMutableContext::~IMutableContext() {} IConfig::~IConfig() {} - WildcardPattern::~WildcardPattern() {} - TestSpec::Pattern::~Pattern() {} - TestSpec::NamePattern::~NamePattern() {} - TestSpec::TagPattern::~TagPattern() {} - TestSpec::ExcludedPattern::~ExcludedPattern() {} Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} void Config::dummy() {} - - namespace TestCaseTracking { - ITracker::~ITracker() {} - TrackerBase::~TrackerBase() {} - SectionTracker::~SectionTracker() {} - IndexTracker::~IndexTracker() {} - } } #ifdef __clang__ diff --git a/include/internal/catch_interfaces_reporter.cpp b/include/internal/catch_interfaces_reporter.cpp new file mode 100644 index 00000000..8ad16248 --- /dev/null +++ b/include/internal/catch_interfaces_reporter.cpp @@ -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 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( existingReporter.get() ); + } + else { + auto newMulti = std::unique_ptr( new MultipleReporters ); + newMulti->add( std::move( existingReporter ) ); + multi = newMulti.get(); + existingReporter = std::move( newMulti ); + } + multi->add( std::move( additionalReporter ) ); + } + +} // end namespace Catch diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h index 7a42f097..eb419a20 100644 --- a/include/internal/catch_interfaces_reporter.h +++ b/include/internal/catch_interfaces_reporter.h @@ -10,29 +10,29 @@ #include "catch_section_info.h" #include "catch_common.h" -#include "catch_totals.hpp" #include "catch_config.hpp" +#include "catch_context.h" +#include "catch_totals.hpp" #include "catch_test_case_info.h" #include "catch_assertionresult.h" #include "catch_message.h" #include "catch_option.hpp" + #include -#include +#include #include #include -namespace Catch -{ +namespace Catch { + struct ReporterConfig { - explicit ReporterConfig( IConfigPtr const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + explicit ReporterConfig( IConfigPtr const& _fullConfig ); - ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); - std::ostream& stream() const { return *m_stream; } - IConfigPtr fullConfig() const { return m_fullConfig; } + std::ostream& stream() const; + IConfigPtr fullConfig() const; private: std::ostream* m_stream; @@ -40,16 +40,11 @@ namespace Catch }; struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} - - bool shouldRedirectStdOut; + bool shouldRedirectStdOut = false; }; template struct LazyStat : Option { - LazyStat() : used( false ) {} LazyStat& operator=( T const& _value ) { Option::operator=( _value ); used = false; @@ -59,21 +54,17 @@ namespace Catch Option::reset(); used = false; } - bool used; + bool used = false; }; struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} + TestRunInfo( std::string const& _name ); std::string name; }; struct GroupInfo { GroupInfo( std::string const& _name, std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} + std::size_t _groupsCount ); std::string name; std::size_t groupIndex; @@ -83,27 +74,13 @@ namespace Catch struct AssertionStats { AssertionStats( AssertionResult const& _assertionResult, std::vector 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 ); - } - } - virtual ~AssertionStats(); + Totals const& _totals ); AssertionStats( AssertionStats const& ) = default; AssertionStats( AssertionStats && ) = default; AssertionStats& operator = ( AssertionStats const& ) = default; AssertionStats& operator = ( AssertionStats && ) = default; + virtual ~AssertionStats() = default; AssertionResult assertionResult; std::vector infoMessages; @@ -114,17 +91,12 @@ namespace Catch SectionStats( SectionInfo const& _sectionInfo, Counts const& _assertions, double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - virtual ~SectionStats(); + bool _missingAssertions ); SectionStats( SectionStats const& ) = default; SectionStats( SectionStats && ) = default; SectionStats& operator = ( SectionStats const& ) = default; SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats() = default; SectionInfo sectionInfo; Counts assertions; @@ -137,19 +109,13 @@ namespace Catch Totals const& _totals, std::string const& _stdOut, std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} - virtual ~TestCaseStats(); + bool _aborting ); TestCaseStats( TestCaseStats const& ) = default; TestCaseStats( TestCaseStats && ) = default; TestCaseStats& operator = ( TestCaseStats const& ) = default; TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats() = default; TestCaseInfo testInfo; Totals totals; @@ -161,21 +127,14 @@ namespace Catch struct TestGroupStats { TestGroupStats( GroupInfo const& _groupInfo, Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} - virtual ~TestGroupStats(); + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); TestGroupStats( TestGroupStats const& ) = default; TestGroupStats( TestGroupStats && ) = default; TestGroupStats& operator = ( TestGroupStats const& ) = default; TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats() = default; GroupInfo groupInfo; Totals totals; @@ -185,17 +144,13 @@ namespace Catch struct TestRunStats { TestRunStats( TestRunInfo const& _runInfo, Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - virtual ~TestRunStats(); + bool _aborting ); TestRunStats( TestRunStats const& ) = default; TestRunStats( TestRunStats && ) = default; TestRunStats& operator = ( TestRunStats const& ) = default; TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats() = default; TestRunInfo runInfo; Totals totals; @@ -205,7 +160,7 @@ namespace Catch class MultipleReporters; struct IStreamingReporter { - virtual ~IStreamingReporter(); + virtual ~IStreamingReporter() = default; // Implementing class must also provide the following static method: // static std::string getDescription(); @@ -232,12 +187,12 @@ namespace Catch virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - virtual bool isMulti() const { return false; } + virtual bool isMulti() const; }; using IStreamingReporterPtr = std::unique_ptr; struct IReporterFactory { - virtual ~IReporterFactory(); + virtual ~IReporterFactory() = default; virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; virtual std::string getDescription() const = 0; }; @@ -247,13 +202,14 @@ namespace Catch using FactoryMap = std::map; using Listeners = std::vector; - virtual ~IReporterRegistry(); + virtual ~IReporterRegistry() = default; virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; virtual FactoryMap const& getFactories() const = 0; virtual Listeners const& getListeners() const = 0; }; void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); -} + +} // end namespace Catch #endif // TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED diff --git a/include/internal/catch_result_builder.cpp b/include/internal/catch_result_builder.cpp index 727b42e7..09a2f53e 100644 --- a/include/internal/catch_result_builder.cpp +++ b/include/internal/catch_result_builder.cpp @@ -19,6 +19,16 @@ 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, SourceLineInfo const& lineInfo, char const* capturedExpression, diff --git a/include/internal/catch_result_builder.h b/include/internal/catch_result_builder.h index 1d822361..d442ce40 100644 --- a/include/internal/catch_result_builder.h +++ b/include/internal/catch_result_builder.h @@ -21,14 +21,9 @@ namespace Catch { struct CopyableStream { CopyableStream() = default; - CopyableStream( CopyableStream const& other ) { - oss << other.oss.str(); - } - CopyableStream& operator=( CopyableStream const& other ) { - oss.str(std::string()); - oss << other.oss.str(); - return *this; - } + CopyableStream( CopyableStream const& other ); + CopyableStream& operator=( CopyableStream const& other ); + std::ostringstream oss; }; diff --git a/include/internal/catch_run_context.cpp b/include/internal/catch_run_context.cpp index a8d36122..7ed79950 100644 --- a/include/internal/catch_run_context.cpp +++ b/include/internal/catch_run_context.cpp @@ -1,5 +1,7 @@ #include "catch_run_context.hpp" +#include + namespace Catch { StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) diff --git a/include/internal/catch_run_context.hpp b/include/internal/catch_run_context.hpp index 44b648a9..abe8c1fe 100644 --- a/include/internal/catch_run_context.hpp +++ b/include/internal/catch_run_context.hpp @@ -22,7 +22,6 @@ #include "catch_result_builder.h" #include "catch_fatal_condition.h" -#include #include namespace Catch { diff --git a/include/internal/catch_test_case_tracker.cpp b/include/internal/catch_test_case_tracker.cpp new file mode 100644 index 00000000..7efb470c --- /dev/null +++ b/include/internal/catch_test_case_tracker.cpp @@ -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 +#include +#include +#include + +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( 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( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + 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 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 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 tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, 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 diff --git a/include/internal/catch_test_case_tracker.hpp b/include/internal/catch_test_case_tracker.hpp index a7b754c2..964b45f9 100644 --- a/include/internal/catch_test_case_tracker.hpp +++ b/include/internal/catch_test_case_tracker.hpp @@ -11,11 +11,8 @@ #include "catch_compiler_capabilities.h" #include "catch_common.h" -#include #include -#include #include -#include #include CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS @@ -27,10 +24,7 @@ namespace TestCaseTracking { std::string name; SourceLineInfo location; - NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) - : name( _name ), - location( _location ) - {} + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); }; struct ITracker; @@ -38,7 +32,7 @@ namespace TestCaseTracking { using ITrackerPtr = std::shared_ptr; struct ITracker { - virtual ~ITracker(); + virtual ~ITracker() = default; // static queries virtual NameAndLocation const& nameAndLocation() const = 0; @@ -79,36 +73,17 @@ namespace TestCaseTracking { public: - static TrackerContext& instance() { - static TrackerContext s_instance; - return s_instance; - } + static TrackerContext& instance(); ITracker& startRun(); + void endRun(); - void endRun() { - m_rootTracker.reset(); - m_currentTracker = nullptr; - m_runState = NotStarted; - } + void startCycle(); + void completeCycle(); - void startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } - void completeCycle() { - m_runState = CompletedCycle; - } - - bool completedCycle() const { - return m_runState == CompletedCycle; - } - ITracker& currentTracker() { - return *m_currentTracker; - } - void setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; - } + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); }; class TrackerBase : public ITracker { @@ -121,239 +96,87 @@ namespace TestCaseTracking { CompletedSuccessfully, Failed }; + class TrackerHasName { NameAndLocation m_nameAndLocation; public: - TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} - bool operator ()( ITrackerPtr const& tracker ) { - return - tracker->nameAndLocation().name == m_nameAndLocation.name && - tracker->nameAndLocation().location == m_nameAndLocation.location; - } + TrackerHasName( NameAndLocation const& nameAndLocation ); + bool operator ()( ITrackerPtr const& tracker ); }; + typedef std::vector Children; NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState = NotStarted; + public: - TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : m_nameAndLocation( nameAndLocation ), - m_ctx( ctx ), - m_parent( parent ) - {} - virtual ~TrackerBase(); + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + virtual ~TrackerBase() = default; - virtual NameAndLocation const& nameAndLocation() const override { - return m_nameAndLocation; - } - virtual bool isComplete() const override { - return m_runState == CompletedSuccessfully || m_runState == Failed; - } - 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 NameAndLocation const& nameAndLocation() const override; + virtual bool isComplete() const override; + virtual bool isSuccessfullyCompleted() const override; + virtual bool isOpen() const override; + virtual bool hasChildren() const override; - virtual void addChild( ITrackerPtr const& child ) override { - m_children.push_back( child ); - } + virtual void addChild( ITrackerPtr const& child ) override; - virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override { - auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); - 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 ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + virtual ITracker& parent() override; - virtual void openChild() override { - if( m_runState != ExecutingChildren ) { - m_runState = ExecutingChildren; - if( m_parent ) - m_parent->openChild(); - } - } + virtual void openChild() override; - virtual bool isSectionTracker() const override { return false; } - virtual bool isIndexTracker() const override { return false; } + virtual bool isSectionTracker() const override; + virtual bool isIndexTracker() const override; - void open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); - } + void open(); - 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: - void moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); - } - void moveToThis() { - m_ctx.setCurrentTracker( this ); - } + void moveToParent(); + void moveToThis(); }; class SectionTracker : public TrackerBase { std::vector m_filters; public: - SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( nameAndLocation, ctx, parent ) - { - if( parent ) { - while( !parent->isSectionTracker() ) - parent = &parent->parent(); + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + virtual ~SectionTracker() = default; - SectionTracker& parentSection = static_cast( *parent ); - addNextFilters( parentSection.m_filters ); - } - } - virtual ~SectionTracker(); + virtual bool isSectionTracker() const override; - virtual bool isSectionTracker() const override { return true; } + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { - std::shared_ptr section; + void tryOpen(); - ITracker& currentTracker = ctx.currentTracker(); - if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - section = std::static_pointer_cast( childTracker ); - } - else { - section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); - 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 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 const& filters ) { - if( filters.size() > 1 ) - m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); - } + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); }; class IndexTracker : public TrackerBase { int m_size; int m_index = -1; public: - IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( nameAndLocation, ctx, parent ), - m_size( size ) - {} - virtual ~IndexTracker(); + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); + virtual ~IndexTracker() = default; - virtual bool isIndexTracker() const override { return true; } + virtual bool isIndexTracker() const override; - static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { - std::shared_ptr tracker; + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); - ITracker& currentTracker = ctx.currentTracker(); - if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isIndexTracker() ); - tracker = std::static_pointer_cast( childTracker ); - } - else { - tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, size ); - currentTracker.addChild( tracker ); - } + int index() const; - if( !ctx.completedCycle() && !tracker->isComplete() ) { - if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) - tracker->moveNext(); - tracker->open(); - } + void moveNext(); - return *tracker; - } - - 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; - } + virtual void close() override; }; - inline ITracker& TrackerContext::startRun() { - m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); - m_currentTracker = nullptr; - m_runState = Executing; - return *m_rootTracker; - } - } // namespace TestCaseTracking using TestCaseTracking::ITracker; diff --git a/include/internal/catch_test_spec.cpp b/include/internal/catch_test_spec.cpp new file mode 100644 index 00000000..878d8424 --- /dev/null +++ b/include/internal/catch_test_spec.cpp @@ -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 +#include +#include + +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; + } +} diff --git a/include/internal/catch_test_spec.hpp b/include/internal/catch_test_spec.hpp index 6571fdd5..48788fd7 100644 --- a/include/internal/catch_test_spec.hpp +++ b/include/internal/catch_test_spec.hpp @@ -24,40 +24,34 @@ namespace Catch { class TestSpec { struct Pattern { - virtual ~Pattern(); + virtual ~Pattern() = default; virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; using PatternPtr = std::shared_ptr; class NamePattern : public Pattern { public: - NamePattern( std::string const& name ) - : m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} - virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( toLower( testCase.name ) ); - } + NamePattern( std::string const& name ); + virtual ~NamePattern() = default; + virtual bool matches( TestCaseInfo const& testCase ) const; private: WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: - TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} - virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); - } + TagPattern( std::string const& tag ); + virtual ~TagPattern() = default; + virtual bool matches( TestCaseInfo const& testCase ) const; private: std::string m_tag; }; class ExcludedPattern : public Pattern { public: - ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} - virtual ~ExcludedPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + ExcludedPattern( PatternPtr const& underlyingPattern ); + virtual ~ExcludedPattern() = default; + virtual bool matches( TestCaseInfo const& testCase ) const; private: PatternPtr m_underlyingPattern; }; @@ -65,27 +59,12 @@ namespace Catch { struct Filter { std::vector m_patterns; - 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; - } + bool matches( TestCaseInfo const& testCase ) const; }; public: - bool hasFilters() const { - return !m_filters.empty(); - } - 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; - } + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; private: std::vector m_filters; diff --git a/include/internal/catch_test_spec_parser.cpp b/include/internal/catch_test_spec_parser.cpp new file mode 100644 index 00000000..fcfc44c9 --- /dev/null +++ b/include/internal/catch_test_spec_parser.cpp @@ -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(); + 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(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + 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 diff --git a/include/internal/catch_test_spec_parser.hpp b/include/internal/catch_test_spec_parser.hpp index c2c797ce..2cc44f09 100644 --- a/include/internal/catch_test_spec_parser.hpp +++ b/include/internal/catch_test_spec_parser.hpp @@ -30,69 +30,17 @@ namespace Catch { ITagAliasRegistry const* m_tagAliases; 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(); - return *this; - } - TestSpec testSpec() { - addFilter(); - return m_testSpec; - } private: - void 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(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern(); - startNewMode( Tag, ++m_pos ); - } - else if( c == '\\' ) - escape(); - } - else if( m_mode == EscapedName ) - m_mode = Name; - else if( m_mode == QuotedName && c == '"' ) - addPattern(); - else if( m_mode == Tag && c == ']' ) - addPattern(); - } - 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 ); } + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + template void addPattern() { std::string token = subString(); @@ -112,16 +60,10 @@ namespace Catch { m_exclusion = false; m_mode = None; } - void addFilter() { - if( !m_currentFilter.m_patterns.empty() ) { - m_testSpec.m_filters.push_back( m_currentFilter ); - m_currentFilter = TestSpec::Filter(); - } - } + + void addFilter(); }; - inline TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); - } + TestSpec parseTestSpec( std::string const& arg ); } // namespace Catch diff --git a/include/internal/catch_totals.cpp b/include/internal/catch_totals.cpp new file mode 100644 index 00000000..3ddc6bab --- /dev/null +++ b/include/internal/catch_totals.cpp @@ -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; + } + +} diff --git a/include/internal/catch_totals.hpp b/include/internal/catch_totals.hpp index 8c26263c..9507582d 100644 --- a/include/internal/catch_totals.hpp +++ b/include/internal/catch_totals.hpp @@ -13,29 +13,12 @@ namespace Catch { struct 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& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); - std::size_t total() const { - return passed + failed + failedButOk; - } - bool allPassed() const { - return failed == 0 && failedButOk == 0; - } - bool allOk() const { - return failed == 0; - } + std::size_t total() const; + bool allPassed() const; + bool allOk() const; std::size_t passed = 0; std::size_t failed = 0; @@ -44,29 +27,11 @@ namespace Catch { struct Totals { - Totals operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); - 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 delta( Totals const& prevTotals ) const; - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } Counts assertions; Counts testCases; diff --git a/include/internal/catch_wildcard_pattern.cpp b/include/internal/catch_wildcard_pattern.cpp new file mode 100644 index 00000000..e99ff5f2 --- /dev/null +++ b/include/internal/catch_wildcard_pattern.cpp @@ -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( 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; + } +} diff --git a/include/internal/catch_wildcard_pattern.hpp b/include/internal/catch_wildcard_pattern.hpp index 04d70f12..e163650c 100644 --- a/include/internal/catch_wildcard_pattern.hpp +++ b/include/internal/catch_wildcard_pattern.hpp @@ -25,38 +25,12 @@ namespace Catch public: - 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( 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" ); - } - } + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; + private: - std::string adjustCase( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; - } + std::string adjustCase( std::string const& str ) const; CaseSensitive::Choice m_caseSensitivity; WildcardPosition m_wildcard = NoWildcard; std::string m_pattern; diff --git a/include/internal/catch_xmlwriter.cpp b/include/internal/catch_xmlwriter.cpp new file mode 100644 index 00000000..2ecb768c --- /dev/null +++ b/include/internal/catch_xmlwriter.cpp @@ -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 + +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 << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; + + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + 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( 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_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 << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\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 << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} diff --git a/include/internal/catch_xmlwriter.hpp b/include/internal/catch_xmlwriter.hpp index 2cd0b1c4..2d7e0fdc 100644 --- a/include/internal/catch_xmlwriter.hpp +++ b/include/internal/catch_xmlwriter.hpp @@ -12,9 +12,7 @@ #include "catch_compiler_capabilities.h" #include -#include #include -#include namespace Catch { @@ -22,55 +20,11 @@ namespace Catch { public: enum ForWhat { ForTextNodes, ForAttributes }; - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) - : m_str( str ), - m_forWhat( forWhat ) - {} + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); - void encodeTo( std::ostream& os ) const { + void 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 << "<"; break; - case '&': os << "&"; break; - - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) - os << ">"; - else - os << c; - break; - - case '\"': - if( m_forWhat == ForAttributes ) - os << """; - 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( c ); - } - else - os << c; - } - } - } - - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; - } + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); private: std::string m_str; @@ -82,24 +36,13 @@ namespace Catch { class ScopedElement { public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} + ScopedElement( XmlWriter* writer ); - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = nullptr; - } + ScopedElement( ScopedElement const& other ); - ~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } + ~ScopedElement(); - ScopedElement& writeText( std::string const& text, bool indent = true ) { - m_writer->writeText( text, indent ); - return *this; - } + ScopedElement& writeText( std::string const& text, bool indent = true ); template ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { @@ -111,57 +54,21 @@ namespace Catch { mutable XmlWriter* m_writer; }; - XmlWriter( std::ostream& os = Catch::cout() ) : m_os( os ) - { - writeDeclaration(); - } + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; - ~XmlWriter() { - while( !m_tags.empty() ) - 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_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 ) { - m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; - return *this; - } + XmlWriter& writeAttribute( std::string const& name, bool attribute ); template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { @@ -171,56 +78,21 @@ namespace Catch { return writeAttribute( name, m_oss.str() ); } - 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& writeText( std::string const& text, bool indent = true ); - XmlWriter& writeComment( std::string const& text ) { - ensureTagClosed(); - m_os << m_indent << ""; - m_needsNewline = true; - return *this; - } + XmlWriter& writeComment( std::string const& text ); - void writeStylesheetRef( std::string const& url ) { - m_os << "\n"; - } + void writeStylesheetRef( std::string const& url ); - XmlWriter& writeBlankLine() { - ensureTagClosed(); - m_os << '\n'; - return *this; - } + XmlWriter& writeBlankLine(); - void ensureTagClosed() { - if( m_tagIsOpen ) { - m_os << ">" << std::endl; - m_tagIsOpen = false; - } - } + void ensureTagClosed(); private: - XmlWriter( XmlWriter const& ); - void operator=( XmlWriter const& ); - void writeDeclaration() { - m_os << "\n"; - } + void writeDeclaration(); - void newlineIfNecessary() { - if( m_needsNewline ) { - m_os << std::endl; - m_needsNewline = false; - } - } + void newlineIfNecessary(); bool m_tagIsOpen = false; bool m_needsNewline = false; diff --git a/include/reporters/catch_reporter_junit.cpp b/include/reporters/catch_reporter_junit.cpp index 49d913bf..c993c468 100644 --- a/include/reporters/catch_reporter_junit.cpp +++ b/include/reporters/catch_reporter_junit.cpp @@ -15,6 +15,8 @@ #include +#include + namespace Catch { namespace { diff --git a/include/reporters/catch_reporter_multi.cpp b/include/reporters/catch_reporter_multi.cpp index d16b9ba3..d2650bea 100644 --- a/include/reporters/catch_reporter_multi.cpp +++ b/include/reporters/catch_reporter_multi.cpp @@ -6,119 +6,86 @@ * 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 { -class MultipleReporters : public IStreamingReporter { - typedef std::vector Reporters; - Reporters m_reporters; - -public: - void add( IStreamingReporterPtr&& reporter ) { + void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { m_reporters.push_back( std::move( reporter ) ); } -public: // IStreamingReporter - - virtual ReporterPreferences getPreferences() const override { + ReporterPreferences MultipleReporters::getPreferences() const { 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 ) reporter->noMatchingTestCases( spec ); } - virtual void testRunStarting( TestRunInfo const& testRunInfo ) override { + void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { for( auto const& reporter : m_reporters ) reporter->testRunStarting( testRunInfo ); } - virtual void testGroupStarting( GroupInfo const& groupInfo ) override { + void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { for( auto const& reporter : m_reporters ) reporter->testGroupStarting( groupInfo ); } - virtual void testCaseStarting( TestCaseInfo const& testInfo ) override { + void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { for( auto const& reporter : m_reporters ) reporter->testCaseStarting( testInfo ); } - virtual void sectionStarting( SectionInfo const& sectionInfo ) override { + void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { for( auto const& reporter : m_reporters ) reporter->sectionStarting( sectionInfo ); } - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) override { + void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { for( auto const& reporter : m_reporters ) reporter->assertionStarting( assertionInfo ); } - // 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; for( auto const& reporter : m_reporters ) clearBuffer |= reporter->assertionEnded( assertionStats ); return clearBuffer; } - virtual void sectionEnded( SectionStats const& sectionStats ) override { + void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { for( auto const& reporter : m_reporters ) reporter->sectionEnded( sectionStats ); } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) override { + void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { for( auto const& reporter : m_reporters ) reporter->testCaseEnded( testCaseStats ); } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) override { + void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { for( auto const& reporter : m_reporters ) reporter->testGroupEnded( testGroupStats ); } - virtual void testRunEnded( TestRunStats const& testRunStats ) override { + void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { for( auto const& reporter : m_reporters ) reporter->testRunEnded( testRunStats ); } - virtual void skipTest( TestCaseInfo const& testInfo ) override { + void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { for( auto const& reporter : m_reporters ) reporter->skipTest( testInfo ); } - virtual bool isMulti() const override { + bool MultipleReporters::isMulti() const { return true; } -}; - -void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { - - if( !existingReporter ) { - existingReporter = std::move( additionalReporter ); - return; - } - - MultipleReporters* multi = nullptr; - - if( existingReporter->isMulti() ) { - multi = static_cast( existingReporter.get() ); - } - else { - auto newMulti = std::unique_ptr( new MultipleReporters ); - newMulti->add( std::move( existingReporter ) ); - multi = newMulti.get(); - existingReporter = std::move( newMulti ); - } - multi->add( std::move( additionalReporter ) ); -} - - } // end namespace Catch diff --git a/include/reporters/catch_reporter_multi.h b/include/reporters/catch_reporter_multi.h new file mode 100644 index 00000000..047bb110 --- /dev/null +++ b/include/reporters/catch_reporter_multi.h @@ -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 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