mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 20:27:11 +01:00 
			
		
		
		
	More method bodies moved out of line
This commit is contained in:
		| @@ -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 ) | ||||
|   | ||||
| @@ -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<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 | ||||
|     { | ||||
|         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; | ||||
|   | ||||
							
								
								
									
										71
									
								
								include/internal/catch_config.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								include/internal/catch_config.cpp
									
									
									
									
									
										Normal 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 | ||||
| @@ -16,7 +16,6 @@ | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <stdexcept> | ||||
|  | ||||
| #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<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; } | ||||
|         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; } | ||||
|         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<IStream const> m_stream; | ||||
|   | ||||
| @@ -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__ | ||||
|   | ||||
							
								
								
									
										120
									
								
								include/internal/catch_interfaces_reporter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								include/internal/catch_interfaces_reporter.cpp
									
									
									
									
									
										Normal 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 | ||||
| @@ -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 <string> | ||||
| #include <ostream> | ||||
| #include <iosfwd> | ||||
| #include <map> | ||||
| #include <memory> | ||||
|  | ||||
| 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<typename T> | ||||
|     struct LazyStat : Option<T> { | ||||
|         LazyStat() : used( false ) {} | ||||
|         LazyStat& operator=( T const& _value ) { | ||||
|             Option<T>::operator=( _value ); | ||||
|             used = false; | ||||
| @@ -59,21 +54,17 @@ namespace Catch | ||||
|             Option<T>::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<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 ); | ||||
|             } | ||||
|         } | ||||
|         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<MessageInfo> 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<IStreamingReporter>; | ||||
|  | ||||
|     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<std::string, IReporterFactoryPtr>; | ||||
|         using Listeners = std::vector<IReporterFactoryPtr>; | ||||
|  | ||||
|         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 | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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; | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| #include "catch_run_context.hpp" | ||||
|  | ||||
| #include <cassert> | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) | ||||
|   | ||||
| @@ -22,7 +22,6 @@ | ||||
| #include "catch_result_builder.h" | ||||
| #include "catch_fatal_condition.h" | ||||
|  | ||||
| #include <set> | ||||
| #include <string> | ||||
|  | ||||
| namespace Catch { | ||||
|   | ||||
							
								
								
									
										277
									
								
								include/internal/catch_test_case_tracker.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								include/internal/catch_test_case_tracker.cpp
									
									
									
									
									
										Normal 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, ¤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<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, ¤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 | ||||
| @@ -11,11 +11,8 @@ | ||||
| #include "catch_compiler_capabilities.h" | ||||
| #include "catch_common.h" | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <string> | ||||
| #include <assert.h> | ||||
| #include <vector> | ||||
| #include <stdexcept> | ||||
| #include <memory> | ||||
|  | ||||
| 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<ITracker>; | ||||
|  | ||||
|     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<ITrackerPtr> 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<std::string> 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<SectionTracker&>( *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<SectionTracker> section; | ||||
|         void tryOpen(); | ||||
|  | ||||
|             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, ¤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<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() ); | ||||
|         } | ||||
|         void addInitialFilters( std::vector<std::string> const& filters ); | ||||
|         void addNextFilters( std::vector<std::string> 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<IndexTracker> 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<IndexTracker>( childTracker ); | ||||
|             } | ||||
|             else { | ||||
|                 tracker = std::make_shared<IndexTracker>( 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<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); | ||||
|         m_currentTracker = nullptr; | ||||
|         m_runState = Executing; | ||||
|         return *m_rootTracker; | ||||
|     } | ||||
|  | ||||
| } // namespace TestCaseTracking | ||||
|  | ||||
| using TestCaseTracking::ITracker; | ||||
|   | ||||
							
								
								
									
										50
									
								
								include/internal/catch_test_spec.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								include/internal/catch_test_spec.cpp
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
| @@ -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<Pattern>; | ||||
|  | ||||
|         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<PatternPtr> 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<Filter> m_filters; | ||||
|   | ||||
							
								
								
									
										87
									
								
								include/internal/catch_test_spec_parser.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								include/internal/catch_test_spec_parser.cpp
									
									
									
									
									
										Normal 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 | ||||
| @@ -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<TestSpec::NamePattern>(); | ||||
|             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<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 ); } | ||||
|         void visitChar( char c ); | ||||
|         void startNewMode( Mode mode, std::size_t start ); | ||||
|         void escape(); | ||||
|         std::string subString() const; | ||||
|  | ||||
|         template<typename T> | ||||
|         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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										61
									
								
								include/internal/catch_totals.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								include/internal/catch_totals.cpp
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|   | ||||
							
								
								
									
										46
									
								
								include/internal/catch_wildcard_pattern.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								include/internal/catch_wildcard_pattern.cpp
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
| @@ -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<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" ); | ||||
|             } | ||||
|         } | ||||
|         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; | ||||
|   | ||||
							
								
								
									
										181
									
								
								include/internal/catch_xmlwriter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								include/internal/catch_xmlwriter.cpp
									
									
									
									
									
										Normal 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 << "<"; 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<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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -12,9 +12,7 @@ | ||||
| #include "catch_compiler_capabilities.h" | ||||
|  | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <iomanip> | ||||
|  | ||||
| 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<int>( 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<typename T> | ||||
|             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_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 ) { | ||||
|             m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; | ||||
|             return *this; | ||||
|         } | ||||
|         XmlWriter& writeAttribute( std::string const& name, bool attribute ); | ||||
|  | ||||
|         template<typename T> | ||||
|         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 << "<!--" << text << "-->"; | ||||
|             m_needsNewline = true; | ||||
|             return *this; | ||||
|         } | ||||
|         XmlWriter& writeComment( std::string const& text ); | ||||
|  | ||||
|         void writeStylesheetRef( std::string const& url ) { | ||||
|             m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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; | ||||
|   | ||||
| @@ -15,6 +15,8 @@ | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| #include <ctime> | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     namespace { | ||||
|   | ||||
| @@ -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<IStreamingReporterPtr> 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<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 | ||||
|   | ||||
							
								
								
									
										57
									
								
								include/reporters/catch_reporter_multi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								include/reporters/catch_reporter_multi.h
									
									
									
									
									
										Normal 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 | ||||
		Reference in New Issue
	
	Block a user
	 Martin Hořeňovský
					Martin Hořeňovský