mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-23 03:43:28 +01:00
More method bodies moved out of line
This commit is contained in:
parent
d7ff62430a
commit
4a1e898eae
@ -197,6 +197,7 @@ set(IMPL_SOURCES
|
||||
${HEADER_DIR}/internal/catch_assertionresult.cpp
|
||||
${HEADER_DIR}/internal/catch_commandline.cpp
|
||||
${HEADER_DIR}/internal/catch_common.cpp
|
||||
${HEADER_DIR}/internal/catch_config.cpp
|
||||
${HEADER_DIR}/internal/catch_console_colour.cpp
|
||||
${HEADER_DIR}/internal/catch_context.cpp
|
||||
${HEADER_DIR}/internal/catch_debugger.cpp
|
||||
@ -209,6 +210,7 @@ set(IMPL_SOURCES
|
||||
${HEADER_DIR}/internal/catch_message.cpp
|
||||
${HEADER_DIR}/internal/catch_notimplemented_exception.cpp
|
||||
${HEADER_DIR}/internal/catch_registry_hub.cpp
|
||||
${HEADER_DIR}/internal/catch_interfaces_reporter.cpp
|
||||
${HEADER_DIR}/internal/catch_result_builder.cpp
|
||||
${HEADER_DIR}/internal/catch_result_type.cpp
|
||||
${HEADER_DIR}/internal/catch_run_context.cpp
|
||||
@ -221,9 +223,15 @@ set(IMPL_SOURCES
|
||||
${HEADER_DIR}/internal/catch_stringref.cpp
|
||||
${HEADER_DIR}/internal/catch_tag_alias_registry.cpp
|
||||
${HEADER_DIR}/internal/catch_test_case_info.cpp
|
||||
${HEADER_DIR}/internal/catch_test_case_tracker.cpp
|
||||
${HEADER_DIR}/internal/catch_test_spec.cpp
|
||||
${HEADER_DIR}/internal/catch_test_spec_parser.cpp
|
||||
${HEADER_DIR}/internal/catch_timer.cpp
|
||||
${HEADER_DIR}/internal/catch_tostring.cpp
|
||||
${HEADER_DIR}/internal/catch_totals.cpp
|
||||
${HEADER_DIR}/internal/catch_version.cpp
|
||||
${HEADER_DIR}/internal/catch_wildcard_pattern.cpp
|
||||
${HEADER_DIR}/internal/catch_xmlwriter.cpp
|
||||
)
|
||||
set(INTERNAL_FILES ${IMPL_SOURCES} ${INTERNAL_HEADERS})
|
||||
CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal)
|
||||
@ -232,6 +240,7 @@ CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal)
|
||||
set(REPORTER_HEADERS
|
||||
${HEADER_DIR}/reporters/catch_reporter_automake.hpp
|
||||
${HEADER_DIR}/reporters/catch_reporter_bases.hpp
|
||||
${HEADER_DIR}/reporters/catch_reporter_multi.h
|
||||
${HEADER_DIR}/reporters/catch_reporter_tap.hpp
|
||||
${HEADER_DIR}/reporters/catch_reporter_teamcity.hpp
|
||||
)
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user