Merge branch 'NewSectionTracking' into develop

* NewSectionTracking:
  Added SUCCEEDs to empty leaf sections to avoid failing due to no assertions
  Removed deprecated section tracking implementation and tests
  Approved changes due to "No assertions" warnings now firing correctly on inner sections
  perform startRun() at the start of each test case
  Fitted new section tracking
  Converted all new part tracking tests/ sections to non variadic form
  Moved all new tracking impl into catch_test_case_tracker.pp
  Removed the "part" component of the tracker names
  More minor tweaks
  Added tests for failing a section within a generator - small fixes to implementation to make it work
  more minor clean-ups
  Cleaned tests up a bit
  Added IndexTracker and got it working with a single generator - made some simplifications to state machine
  More name changes
  Tweaks
  First cut of new section/ part tracking
  Refactored stream related stuff - simpler, polymorphic hierarchy-based, approach - less bitty conditionals spread across the code - all resolved up-front so now config class is immutable (it had evolved the way it was and in need of a clean-up sweep for a long time)
  Some small clean-ups and refactorings - removed previous instance saves in RunContext (they were a hang-over from embedded contexts) - started cleaning up config usage
This commit is contained in:
Phil Nash 2015-11-03 07:55:29 +00:00
commit 4636be9744
21 changed files with 3004 additions and 837 deletions

View File

@ -43,7 +43,7 @@ namespace Catch {
reporter = addReporter( reporter, createReporter( *it, config ) ); reporter = addReporter( reporter, createReporter( *it, config ) );
return reporter; return reporter;
} }
Ptr<IStreamingReporter> addListeners( Ptr<IConfig> const& config, Ptr<IStreamingReporter> reporters ) { Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
it != itEnd; it != itEnd;
@ -52,27 +52,15 @@ namespace Catch {
return reporters; return reporters;
} }
void openStreamInto( Ptr<Config> const& config, std::ofstream& ofs ) {
// Open output file, if specified
if( !config->getFilename().empty() ) {
ofs.open( config->getFilename().c_str() );
if( ofs.fail() ) {
std::ostringstream oss;
oss << "Unable to open file: '" << config->getFilename() << "'";
throw std::domain_error( oss.str() );
}
config->setStreamBuf( ofs.rdbuf() );
}
}
Totals runTests( Ptr<Config> const& config ) { Totals runTests( Ptr<Config> const& config ) {
std::ofstream ofs; Ptr<IConfig const> iconfig = config.get();
openStreamInto( config, ofs );
Ptr<IStreamingReporter> reporter = makeReporter( config );
reporter = addListeners( config.get(), reporter );
RunContext context( config.get(), reporter ); Ptr<IStreamingReporter> reporter = makeReporter( config );
reporter = addListeners( iconfig, reporter );
RunContext context( iconfig, reporter );
Totals totals; Totals totals;
@ -82,17 +70,17 @@ namespace Catch {
if( !testSpec.hasFilters() ) if( !testSpec.hasFilters() )
testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *config ); std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
it != itEnd; it != itEnd;
++it ) { ++it ) {
if( !context.aborting() && matchTest( *it, testSpec, *config ) ) if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
totals += context.runTest( *it ); totals += context.runTest( *it );
else else
reporter->skipTest( *it ); reporter->skipTest( *it );
} }
context.testGroupEnded( config->name(), totals, 1, 1 ); context.testGroupEnded( iconfig->name(), totals, 1, 1 );
return totals; return totals;
} }

View File

@ -85,12 +85,11 @@ namespace Catch {
public: public:
Config() Config()
: m_os( Catch::cout().rdbuf() )
{} {}
Config( ConfigData const& data ) Config( ConfigData const& data )
: m_data( data ), : m_data( data ),
m_os( Catch::cout().rdbuf() ) m_stream( openStream() )
{ {
if( !data.testsOrTags.empty() ) { if( !data.testsOrTags.empty() ) {
TestSpecParser parser( ITagAliasRegistry::get() ); TestSpecParser parser( ITagAliasRegistry::get() );
@ -101,12 +100,6 @@ namespace Catch {
} }
virtual ~Config() { virtual ~Config() {
m_os.rdbuf( Catch::cout().rdbuf() );
m_stream.release();
}
void setFilename( std::string const& filename ) {
m_data.outputFilename = filename;
} }
std::string const& getFilename() const { std::string const& getFilename() const {
@ -122,17 +115,6 @@ namespace Catch {
bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
void setStreamBuf( std::streambuf* buf ) {
m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
}
void useStream( std::string const& streamName ) {
Stream stream = createStream( streamName );
setStreamBuf( stream.streamBuf );
m_stream.release();
m_stream = stream;
}
std::vector<std::string> getReporterNames() const { return m_data.reporterNames; } std::vector<std::string> getReporterNames() const { return m_data.reporterNames; }
int abortAfter() const { return m_data.abortAfter; } int abortAfter() const { return m_data.abortAfter; }
@ -144,7 +126,7 @@ namespace Catch {
// IConfig interface // IConfig interface
virtual bool allowThrows() const { return !m_data.noThrow; } virtual bool allowThrows() const { return !m_data.noThrow; }
virtual std::ostream& stream() const { return m_os; } virtual std::ostream& stream() const { return m_stream->stream(); }
virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
@ -154,14 +136,25 @@ namespace Catch {
virtual bool forceColour() const { return m_data.forceColour; } virtual bool forceColour() const { return m_data.forceColour; }
private: 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
throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
}
else
return new FileStream( m_data.outputFilename );
}
ConfigData m_data; ConfigData m_data;
Stream m_stream; std::auto_ptr<IStream const> m_stream;
mutable std::ostream m_os;
TestSpec m_testSpec; TestSpec m_testSpec;
}; };
} // end namespace Catch } // end namespace Catch
#endif // TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED

View File

@ -95,14 +95,6 @@ namespace Catch {
return getCurrentMutableContext(); return getCurrentMutableContext();
} }
Stream createStream( std::string const& streamName ) {
if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
throw std::domain_error( "Unknown stream: " + streamName );
}
void cleanUpContext() { void cleanUpContext() {
delete currentContext; delete currentContext;
currentContext = CATCH_NULL; currentContext = CATCH_NULL;

View File

@ -35,6 +35,7 @@
#include "catch_tostring.hpp" #include "catch_tostring.hpp"
#include "catch_result_builder.hpp" #include "catch_result_builder.hpp"
#include "catch_tag_alias_registry.hpp" #include "catch_tag_alias_registry.hpp"
#include "catch_test_case_tracker.hpp"
#include "../reporters/catch_reporter_multi.hpp" #include "../reporters/catch_reporter_multi.hpp"
#include "../reporters/catch_reporter_xml.hpp" #include "../reporters/catch_reporter_xml.hpp"
@ -43,8 +44,11 @@
#include "../reporters/catch_reporter_compact.hpp" #include "../reporters/catch_reporter_compact.hpp"
namespace Catch { namespace Catch {
// These are all here to avoid warnings about not having any out of line
// virtual methods
NonCopyable::~NonCopyable() {} NonCopyable::~NonCopyable() {}
IShared::~IShared() {} IShared::~IShared() {}
IStream::~IStream() CATCH_NOEXCEPT {}
StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
IContext::~IContext() {} IContext::~IContext() {}
IResultCapture::~IResultCapture() {} IResultCapture::~IResultCapture() {}
@ -90,6 +94,13 @@ namespace Catch {
Matchers::Impl::StdString::EndsWith::~EndsWith() {} Matchers::Impl::StdString::EndsWith::~EndsWith() {}
void Config::dummy() {} void Config::dummy() {}
namespace TestCaseTracking {
ITracker::~ITracker() {}
TrackerBase::~TrackerBase() {}
SectionTracker::~SectionTracker() {}
IndexTracker::~IndexTracker() {}
}
} }
#ifdef __clang__ #ifdef __clang__

View File

@ -26,18 +26,18 @@
namespace Catch namespace Catch
{ {
struct ReporterConfig { struct ReporterConfig {
explicit ReporterConfig( Ptr<IConfig> const& _fullConfig ) explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
: m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream ) ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
: m_stream( &_stream ), m_fullConfig( _fullConfig ) {} : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
std::ostream& stream() const { return *m_stream; } std::ostream& stream() const { return *m_stream; }
Ptr<IConfig> fullConfig() const { return m_fullConfig; } Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
private: private:
std::ostream* m_stream; std::ostream* m_stream;
Ptr<IConfig> m_fullConfig; Ptr<IConfig const> m_fullConfig;
}; };
struct ReporterPreferences { struct ReporterPreferences {
@ -261,7 +261,7 @@ namespace Catch
typedef std::vector<Ptr<IReporterFactory> > Listeners; typedef std::vector<Ptr<IReporterFactory> > Listeners;
virtual ~IReporterRegistry(); virtual ~IReporterRegistry();
virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0; virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
virtual FactoryMap const& getFactories() const = 0; virtual FactoryMap const& getFactories() const = 0;
virtual Listeners const& getListeners() const = 0; virtual Listeners const& getListeners() const = 0;
}; };

View File

@ -20,7 +20,7 @@ namespace Catch {
virtual ~ReporterRegistry() CATCH_OVERRIDE {} virtual ~ReporterRegistry() CATCH_OVERRIDE {}
virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const CATCH_OVERRIDE { virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
FactoryMap::const_iterator it = m_factories.find( name ); FactoryMap::const_iterator it = m_factories.find( name );
if( it == m_factories.end() ) if( it == m_factories.end() )
return CATCH_NULL; return CATCH_NULL;

View File

@ -64,10 +64,7 @@ namespace Catch {
m_context( getCurrentMutableContext() ), m_context( getCurrentMutableContext() ),
m_activeTestCase( CATCH_NULL ), m_activeTestCase( CATCH_NULL ),
m_config( _config ), m_config( _config ),
m_reporter( reporter ), m_reporter( reporter )
m_prevRunner( m_context.getRunner() ),
m_prevResultCapture( m_context.getResultCapture() ),
m_prevConfig( m_context.getConfig() )
{ {
m_context.setRunner( this ); m_context.setRunner( this );
m_context.setConfig( m_config ); m_context.setConfig( m_config );
@ -77,10 +74,6 @@ namespace Catch {
virtual ~RunContext() { virtual ~RunContext() {
m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
m_context.setRunner( m_prevRunner );
m_context.setConfig( Ptr<IConfig const>() );
m_context.setResultCapture( m_prevResultCapture );
m_context.setConfig( m_prevConfig );
} }
void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
@ -101,14 +94,18 @@ namespace Catch {
m_reporter->testCaseStarting( testInfo ); m_reporter->testCaseStarting( testInfo );
m_activeTestCase = &testCase; m_activeTestCase = &testCase;
m_testCaseTracker = TestCaseTracker( testInfo.name );
do { do {
m_trackerContext.startRun();
do { do {
m_trackerContext.startCycle();
m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name );
runCurrentTest( redirectedCout, redirectedCerr ); runCurrentTest( redirectedCout, redirectedCerr );
} }
while( !m_testCaseTracker->isCompleted() && !aborting() ); while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
} }
// !TBD: deprecated - this will be replaced by indexed trackers
while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
Totals deltaTotals = m_totals.delta( prevTotals ); Totals deltaTotals = m_totals.delta( prevTotals );
@ -120,7 +117,7 @@ namespace Catch {
aborting() ) ); aborting() ) );
m_activeTestCase = CATCH_NULL; m_activeTestCase = CATCH_NULL;
m_testCaseTracker.reset(); m_testCaseTracker = CATCH_NULL;
return deltaTotals; return deltaTotals;
} }
@ -156,8 +153,10 @@ namespace Catch {
std::ostringstream oss; std::ostringstream oss;
oss << sectionInfo.name << "@" << sectionInfo.lineInfo; oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
if( !m_testCaseTracker->enterSection( oss.str() ) ) ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() );
if( !sectionTracker.isOpen() )
return false; return false;
m_activeSections.push_back( &sectionTracker );
m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
@ -168,9 +167,11 @@ namespace Catch {
return true; return true;
} }
bool testForMissingAssertions( Counts& assertions ) { bool testForMissingAssertions( Counts& assertions ) {
if( assertions.total() != 0 || if( assertions.total() != 0 )
!m_config->warnAboutMissingAssertions() || return false;
m_testCaseTracker->currentSectionHasChildren() ) if( m_config->warnAboutMissingAssertions() )
return false;
if( m_trackerContext.currentTracker().hasChildren() )
return false; return false;
m_totals.assertions.failed++; m_totals.assertions.failed++;
assertions.failed++; assertions.failed++;
@ -181,13 +182,22 @@ namespace Catch {
Counts assertions = m_totals.assertions - endInfo.prevAssertions; Counts assertions = m_totals.assertions - endInfo.prevAssertions;
bool missingAssertions = testForMissingAssertions( assertions ); bool missingAssertions = testForMissingAssertions( assertions );
m_testCaseTracker->leaveSection(); if( !m_activeSections.empty() ) {
m_activeSections.back()->close();
m_activeSections.pop_back();
}
m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
m_messages.clear(); m_messages.clear();
} }
virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
if( m_unfinishedSections.empty() )
m_activeSections.back()->fail();
else
m_activeSections.back()->close();
m_activeSections.pop_back();
m_unfinishedSections.push_back( endInfo ); m_unfinishedSections.push_back( endInfo );
} }
@ -256,7 +266,6 @@ namespace Catch {
double duration = 0; double duration = 0;
try { try {
m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
TestCaseTracker::Guard guard( *m_testCaseTracker );
seedRng( *m_config ); seedRng( *m_config );
@ -278,6 +287,7 @@ namespace Catch {
catch(...) { catch(...) {
makeUnexpectedResultBuilder().useActiveException(); makeUnexpectedResultBuilder().useActiveException();
} }
m_testCaseTracker->close();
handleUnfinishedSections(); handleUnfinishedSections();
m_messages.clear(); m_messages.clear();
@ -323,18 +333,18 @@ namespace Catch {
TestRunInfo m_runInfo; TestRunInfo m_runInfo;
IMutableContext& m_context; IMutableContext& m_context;
TestCase const* m_activeTestCase; TestCase const* m_activeTestCase;
Option<TestCaseTracker> m_testCaseTracker; ITracker* m_testCaseTracker;
ITracker* m_currentSectionTracker;
AssertionResult m_lastResult; AssertionResult m_lastResult;
Ptr<IConfig const> m_config; Ptr<IConfig const> m_config;
Totals m_totals; Totals m_totals;
Ptr<IStreamingReporter> m_reporter; Ptr<IStreamingReporter> m_reporter;
std::vector<MessageInfo> m_messages; std::vector<MessageInfo> m_messages;
IRunner* m_prevRunner;
IResultCapture* m_prevResultCapture;
Ptr<IConfig const> m_prevConfig;
AssertionInfo m_lastAssertionInfo; AssertionInfo m_lastAssertionInfo;
std::vector<SectionEndInfo> m_unfinishedSections; std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext;
}; };
IResultCapture& getResultCapture() { IResultCapture& getResultCapture() {

View File

@ -9,28 +9,52 @@
#ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#include <streambuf> #include "catch_compiler_capabilities.h"
#include "catch_streambuf.h"
#ifdef __clang__ #include <streambuf>
#pragma clang diagnostic ignored "-Wpadded" #include <ostream>
#endif #include <fstream>
namespace Catch { namespace Catch {
class Stream {
public:
Stream();
Stream( std::streambuf* _streamBuf, bool _isOwned );
void release();
std::streambuf* streamBuf;
private:
bool isOwned;
};
std::ostream& cout(); std::ostream& cout();
std::ostream& cerr(); std::ostream& cerr();
struct IStream {
virtual ~IStream() CATCH_NOEXCEPT;
virtual std::ostream& stream() const = 0;
};
class FileStream : public IStream {
mutable std::ofstream m_ofs;
public:
FileStream( std::string const& filename );
public: // IStream
virtual std::ostream& stream() const CATCH_OVERRIDE;
};
class CoutStream : public IStream {
mutable std::ostream m_os;
public:
CoutStream();
public: // IStream
virtual std::ostream& stream() const CATCH_OVERRIDE;
};
class DebugOutStream : public IStream {
std::auto_ptr<StreamBufBase> m_streamBuf;
mutable std::ostream m_os;
public:
DebugOutStream();
public: // IStream
virtual std::ostream& stream() const CATCH_OVERRIDE;
};
} }
#endif // TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #endif // TWOBLUECUBES_CATCH_STREAM_H_INCLUDED

View File

@ -10,7 +10,6 @@
#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
#include "catch_stream.h" #include "catch_stream.h"
#include "catch_streambuf.h"
#include "catch_debugger.h" #include "catch_debugger.h"
#include <stdexcept> #include <stdexcept>
@ -57,6 +56,20 @@ namespace Catch {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
FileStream::FileStream( std::string const& filename ) {
m_ofs.open( filename.c_str() );
if( m_ofs.fail() ) {
std::ostringstream oss;
oss << "Unable to open file: '" << filename << "'";
throw std::domain_error( oss.str() );
}
}
std::ostream& FileStream::stream() const {
return m_ofs;
}
struct OutputDebugWriter { struct OutputDebugWriter {
void operator()( std::string const&str ) { void operator()( std::string const&str ) {
@ -64,22 +77,26 @@ namespace Catch {
} }
}; };
Stream::Stream() DebugOutStream::DebugOutStream()
: streamBuf( CATCH_NULL ), isOwned( false ) : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
m_os( m_streamBuf.get() )
{} {}
Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) std::ostream& DebugOutStream::stream() const {
: streamBuf( _streamBuf ), isOwned( _isOwned ) return m_os;
{}
void Stream::release() {
if( isOwned ) {
delete streamBuf;
streamBuf = CATCH_NULL;
isOwned = false;
}
} }
// Store the streambuf from cout up-front because
// cout may get redirected when running tests
CoutStream::CoutStream()
: m_os( Catch::cout().rdbuf() )
{}
std::ostream& CoutStream::stream() const {
return m_os;
}
#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
std::ostream& cout() { std::ostream& cout() {
return std::cout; return std::cout;

View File

@ -19,6 +19,7 @@
# pragma clang diagnostic ignored "-Wc++98-compat" # pragma clang diagnostic ignored "-Wc++98-compat"
# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
# pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wswitch-enum"
# pragma clang diagnostic ignored "-Wcovered-switch-default"
# endif # endif
#elif defined __GNUC__ #elif defined __GNUC__
# pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wvariadic-macros"

View File

@ -9,141 +9,307 @@
#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
#include "catch_compiler_capabilities.h" #include "catch_compiler_capabilities.h"
#include "catch_ptr.hpp"
#include <map> #include <map>
#include <string> #include <string>
#include <assert.h> #include <assert.h>
#include <vector>
namespace Catch { namespace Catch {
namespace SectionTracking { namespace TestCaseTracking {
class TrackedSection { struct ITracker : SharedImpl<> {
virtual ~ITracker();
typedef std::map<std::string, TrackedSection> TrackedSections; // static queries
virtual std::string name() const = 0;
// dynamic queries
virtual bool isComplete() const = 0; // Successfully completed or failed
virtual bool isSuccessfullyCompleted() const = 0;
virtual bool isOpen() const = 0; // Started but not complete
virtual bool hasChildren() const = 0;
virtual ITracker& parent() = 0;
// actions
virtual void close() = 0; // Successfully complete
virtual void fail() = 0;
virtual void markAsNeedingAnotherRun() = 0;
virtual void addChild( Ptr<ITracker> const& child ) = 0;
virtual ITracker* findChild( std::string const& name ) = 0;
virtual void openChild() = 0;
};
class TrackerContext {
public:
enum RunState { enum RunState {
NotStarted, NotStarted,
Executing, Executing,
ExecutingChildren, CompletedCycle
Completed
}; };
TrackedSection( std::string const& name, TrackedSection* parent ) Ptr<ITracker> m_rootTracker;
: m_name( name ), m_runState( NotStarted ), m_parent( parent ) ITracker* m_currentTracker;
RunState m_runState;
public:
static TrackerContext& instance() {
static TrackerContext s_instance;
return s_instance;
}
TrackerContext()
: m_currentTracker( CATCH_NULL ),
m_runState( NotStarted )
{} {}
RunState runState() const { return m_runState; }
TrackedSection* findChild( std::string const& childName ); ITracker& startRun();
TrackedSection* acquireChild( std::string const& childName );
void enter() { void endRun() {
if( m_runState == NotStarted ) m_rootTracker.reset();
m_runState = Executing; m_currentTracker = CATCH_NULL;
m_runState = NotStarted;
} }
void leave();
TrackedSection* getParent() { void startCycle() {
return m_parent; m_currentTracker = m_rootTracker.get();
m_runState = Executing;
} }
bool hasChildren() const { void completeCycle() {
m_runState = CompletedCycle;
}
bool completedCycle() const {
return m_runState == CompletedCycle;
}
ITracker& currentTracker() {
return *m_currentTracker;
}
void setCurrentTracker( ITracker* tracker ) {
m_currentTracker = tracker;
}
};
class TrackerBase : public ITracker {
protected:
enum CycleState {
NotStarted,
Executing,
ExecutingChildren,
NeedsAnotherRun,
CompletedSuccessfully,
Failed
};
class TrackerHasName {
std::string m_name;
public:
TrackerHasName( std::string const& name ) : m_name( name ) {}
bool operator ()( Ptr<ITracker> const& tracker ) {
return tracker->name() == m_name;
}
};
typedef std::vector<Ptr<ITracker> > Children;
std::string m_name;
TrackerContext& m_ctx;
ITracker* m_parent;
Children m_children;
CycleState m_runState;
public:
TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent )
: m_name( name ),
m_ctx( ctx ),
m_parent( parent ),
m_runState( NotStarted )
{}
virtual ~TrackerBase();
virtual std::string name() const CATCH_OVERRIDE {
return m_name;
}
virtual bool isComplete() const CATCH_OVERRIDE {
return m_runState == CompletedSuccessfully || m_runState == Failed;
}
virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
return m_runState == CompletedSuccessfully;
}
virtual bool isOpen() const CATCH_OVERRIDE {
return m_runState != NotStarted && !isComplete();
}
virtual bool hasChildren() const CATCH_OVERRIDE {
return !m_children.empty(); return !m_children.empty();
} }
private:
std::string m_name;
RunState m_runState;
TrackedSections m_children;
TrackedSection* m_parent;
};
inline TrackedSection* TrackedSection::findChild( std::string const& childName ) { virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
TrackedSections::iterator it = m_children.find( childName ); m_children.push_back( child );
return it != m_children.end() }
? &it->second
: CATCH_NULL; virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE {
} Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) );
inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) { return( it != m_children.end() )
if( TrackedSection* child = findChild( childName ) ) ? it->get()
return child; : CATCH_NULL;
m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); }
return findChild( childName ); virtual ITracker& parent() CATCH_OVERRIDE {
} assert( m_parent ); // Should always be non-null except for root
inline void TrackedSection::leave() { return *m_parent;
for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); }
it != itEnd;
++it ) virtual void openChild() CATCH_OVERRIDE {
if( it->second.runState() != Completed ) { if( m_runState != ExecutingChildren ) {
m_runState = ExecutingChildren; m_runState = ExecutingChildren;
return; if( m_parent )
m_parent->openChild();
} }
m_runState = Completed;
}
class TestCaseTracker {
public:
TestCaseTracker( std::string const& testCaseName )
: m_testCase( testCaseName, CATCH_NULL ),
m_currentSection( &m_testCase ),
m_completedASectionThisRun( false )
{}
bool enterSection( std::string const& name ) {
TrackedSection* child = m_currentSection->acquireChild( name );
if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
return false;
m_currentSection = child;
m_currentSection->enter();
return true;
} }
void leaveSection() { void open() {
m_currentSection->leave(); m_runState = Executing;
m_currentSection = m_currentSection->getParent(); moveToThis();
assert( m_currentSection != CATCH_NULL ); if( m_parent )
m_completedASectionThisRun = true; m_parent->openChild();
} }
bool currentSectionHasChildren() const { virtual void close() CATCH_OVERRIDE {
return m_currentSection->hasChildren();
}
bool isCompleted() const {
return m_testCase.runState() == TrackedSection::Completed;
}
class Guard { // Close any still open children (e.g. generators)
public: while( &m_ctx.currentTracker() != this )
Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { m_ctx.currentTracker().close();
m_tracker.enterTestCase();
switch( m_runState ) {
case NotStarted:
case CompletedSuccessfully:
case Failed:
throw std::logic_error( "Illogical state" );
case NeedsAnotherRun:
break;;
case Executing:
m_runState = CompletedSuccessfully;
break;
case ExecutingChildren:
if( m_children.empty() || m_children.back()->isComplete() )
m_runState = CompletedSuccessfully;
break;
default:
throw std::logic_error( "Unexpected state" );
} }
~Guard() { moveToParent();
m_tracker.leaveTestCase(); m_ctx.completeCycle();
} }
private: virtual void fail() CATCH_OVERRIDE {
Guard( Guard const& ); m_runState = Failed;
void operator = ( Guard const& ); if( m_parent )
TestCaseTracker& m_tracker; m_parent->markAsNeedingAnotherRun();
}; moveToParent();
m_ctx.completeCycle();
}
virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
m_runState = NeedsAnotherRun;
}
private: private:
void enterTestCase() { void moveToParent() {
m_currentSection = &m_testCase; assert( m_parent );
m_completedASectionThisRun = false; m_ctx.setCurrentTracker( m_parent );
m_testCase.enter();
} }
void leaveTestCase() { void moveToThis() {
m_testCase.leave(); m_ctx.setCurrentTracker( this );
} }
TrackedSection m_testCase;
TrackedSection* m_currentSection;
bool m_completedASectionThisRun;
}; };
} // namespace SectionTracking class SectionTracker : public TrackerBase {
public:
SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent )
: TrackerBase( name, ctx, parent )
{}
virtual ~SectionTracker();
using SectionTracking::TestCaseTracker; static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) {
SectionTracker* section = CATCH_NULL;
ITracker& currentTracker = ctx.currentTracker();
if( ITracker* childTracker = currentTracker.findChild( name ) ) {
section = dynamic_cast<SectionTracker*>( childTracker );
assert( section );
}
else {
section = new SectionTracker( name, ctx, &currentTracker );
currentTracker.addChild( section );
}
if( !ctx.completedCycle() && !section->isComplete() ) {
section->open();
}
return *section;
}
};
class IndexTracker : public TrackerBase {
int m_size;
int m_index;
public:
IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size )
: TrackerBase( name, ctx, parent ),
m_size( size ),
m_index( -1 )
{}
virtual ~IndexTracker();
static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) {
IndexTracker* tracker = CATCH_NULL;
ITracker& currentTracker = ctx.currentTracker();
if( ITracker* childTracker = currentTracker.findChild( name ) ) {
tracker = dynamic_cast<IndexTracker*>( childTracker );
assert( tracker );
}
else {
tracker = new IndexTracker( name, ctx, &currentTracker, size );
currentTracker.addChild( tracker );
}
if( !ctx.completedCycle() && !tracker->isComplete() ) {
if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
tracker->moveNext();
tracker->open();
}
return *tracker;
}
int index() const { return m_index; }
void moveNext() {
m_index++;
m_children.clear();
}
virtual void close() CATCH_OVERRIDE {
TrackerBase::close();
if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
m_runState = Executing;
}
};
inline ITracker& TrackerContext::startRun() {
m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL );
m_currentTracker = CATCH_NULL;
m_runState = Executing;
return *m_rootTracker;
}
} // namespace TestCaseTracking
using TestCaseTracking::ITracker;
using TestCaseTracking::TrackerContext;
using TestCaseTracking::SectionTracker;
using TestCaseTracking::IndexTracker;
} // namespace Catch } // namespace Catch

View File

@ -65,7 +65,7 @@ namespace Catch {
// It can optionally be overridden in the derived class. // It can optionally be overridden in the derived class.
} }
Ptr<IConfig> m_config; Ptr<IConfig const> m_config;
std::ostream& stream; std::ostream& stream;
LazyStat<TestRunInfo> currentTestRunInfo; LazyStat<TestRunInfo> currentTestRunInfo;
@ -204,7 +204,7 @@ namespace Catch {
virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
Ptr<IConfig> m_config; Ptr<IConfig const> m_config;
std::ostream& stream; std::ostream& stream;
std::vector<AssertionStats> m_assertions; std::vector<AssertionStats> m_assertions;
std::vector<std::vector<Ptr<SectionNode> > > m_sections; std::vector<std::vector<Ptr<SectionNode> > > m_sections;

View File

@ -494,7 +494,27 @@ explicitly with message:
Message from section two Message from section two
Message from section one Message from section one
-------------------------------------------------------------------------------
Standard output from all sections is reported
one
-------------------------------------------------------------------------------
MessageTests.cpp:<line number>
...............................................................................
No assertions in section 'one'
Message from section two Message from section two
-------------------------------------------------------------------------------
Standard output from all sections is reported
two
-------------------------------------------------------------------------------
MessageTests.cpp:<line number>
...............................................................................
No assertions in section 'two'
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
SCOPED_INFO is reset for each loop SCOPED_INFO is reset for each loop
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -797,6 +817,6 @@ with expansion:
"first" == "second" "first" == "second"
=============================================================================== ===============================================================================
test cases: 159 | 119 passed | 39 failed | 1 failed as expected test cases: 159 | 118 passed | 40 failed | 1 failed as expected
assertions: 788 | 695 passed | 80 failed | 13 failed as expected assertions: 907 | 812 passed | 82 failed | 13 failed as expected

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
<testsuites> <testsuites>
<testsuite name="CatchSelfTest" errors="12" failures="88" tests="808" hostname="tbd" time="{duration}" timestamp="tbd"> <testsuite name="CatchSelfTest" errors="12" failures="68" tests="905" hostname="tbd" time="{duration}" timestamp="tbd">
<testcase classname="global" name="toString(enum)" time="{duration}"/> <testcase classname="global" name="toString(enum)" time="{duration}"/>
<testcase classname="global" name="toString(enum w/operator&lt;&lt;)" time="{duration}"/> <testcase classname="global" name="toString(enum w/operator&lt;&lt;)" time="{duration}"/>
<testcase classname="global" name="toString(enum class)" time="{duration}"/> <testcase classname="global" name="toString(enum class)" time="{duration}"/>
@ -341,6 +341,11 @@ MessageTests.cpp:<line number>
MiscTests.cpp:<line number> MiscTests.cpp:<line number>
</failure> </failure>
</testcase> </testcase>
<testcase classname="more nested SECTION tests" name="s1/s3" time="{duration}"/>
<testcase classname="more nested SECTION tests" name="s1/s4" time="{duration}"/>
<testcase classname="even more nested SECTION tests" name="c/d (leaf)" time="{duration}"/>
<testcase classname="even more nested SECTION tests" name="c/e (leaf)" time="{duration}"/>
<testcase classname="even more nested SECTION tests" name="f (leaf)" time="{duration}"/>
<testcase classname="looped SECTION tests" name="s1" time="{duration}"> <testcase classname="looped SECTION tests" name="s1" time="{duration}">
<failure message="0 > 1" type="CHECK"> <failure message="0 > 1" type="CHECK">
MiscTests.cpp:<line number> MiscTests.cpp:<line number>
@ -399,6 +404,8 @@ MiscTests.cpp:<line number>
MiscTests.cpp:<line number> MiscTests.cpp:<line number>
</failure> </failure>
</testcase> </testcase>
<testcase classname="xmlentitycheck" name="embedded xml" time="{duration}"/>
<testcase classname="xmlentitycheck" name="encoded chars" time="{duration}"/>
<testcase classname="global" name="send a single char to INFO" time="{duration}"> <testcase classname="global" name="send a single char to INFO" time="{duration}">
<failure message="false" type="REQUIRE"> <failure message="false" type="REQUIRE">
3 3
@ -566,6 +573,23 @@ TrickyTests.cpp:<line number>
<testcase classname="global" name="toString( vectors&lt;has_toString )" time="{duration}"/> <testcase classname="global" name="toString( vectors&lt;has_toString )" time="{duration}"/>
<testcase classname="global" name="toString( vectors&lt;has_maker )" time="{duration}"/> <testcase classname="global" name="toString( vectors&lt;has_maker )" time="{duration}"/>
<testcase classname="global" name="toString( vectors&lt;has_maker_and_toString )" time="{duration}"/> <testcase classname="global" name="toString( vectors&lt;has_maker_and_toString )" time="{duration}"/>
<testcase classname="Tracker" name="root" time="{duration}"/>
<testcase classname="Tracker" name="successfully close one section" time="{duration}"/>
<testcase classname="Tracker" name="fail one section" time="{duration}"/>
<testcase classname="Tracker" name="fail one section/re-enter after failed section" time="{duration}"/>
<testcase classname="Tracker" name="fail one section/re-enter after failed section and find next section" time="{duration}"/>
<testcase classname="Tracker" name="successfully close one section, then find another" time="{duration}"/>
<testcase classname="Tracker" name="successfully close one section, then find another/Re-enter - skips S1 and enters S2" time="{duration}"/>
<testcase classname="Tracker" name="successfully close one section, then find another/Re-enter - skips S1 and enters S2/Successfully close S2" time="{duration}"/>
<testcase classname="Tracker" name="successfully close one section, then find another/Re-enter - skips S1 and enters S2/fail S2" time="{duration}"/>
<testcase classname="Tracker" name="open a nested section" time="{duration}"/>
<testcase classname="Tracker" name="start a generator" time="{duration}"/>
<testcase classname="Tracker" name="start a generator/close outer section" time="{duration}"/>
<testcase classname="Tracker" name="start a generator/close outer section/Re-enter for second generation" time="{duration}"/>
<testcase classname="Tracker" name="start a generator/Start a new inner section" time="{duration}"/>
<testcase classname="Tracker" name="start a generator/Start a new inner section/Re-enter for second generation" time="{duration}"/>
<testcase classname="Tracker" name="start a generator/Fail an inner section" time="{duration}"/>
<testcase classname="Tracker" name="start a generator/Fail an inner section/Re-enter for second generation" time="{duration}"/>
<testcase classname="global" name="std::pair&lt;int,std::string> -> toString" time="{duration}"/> <testcase classname="global" name="std::pair&lt;int,std::string> -> toString" time="{duration}"/>
<testcase classname="global" name="std::pair&lt;int,const std::string> -> toString" time="{duration}"/> <testcase classname="global" name="std::pair&lt;int,const std::string> -> toString" time="{duration}"/>
<testcase classname="global" name="std::vector&lt;std::pair&lt;std::string,int> > -> toString" time="{duration}"/> <testcase classname="global" name="std::vector&lt;std::pair&lt;std::string,int> > -> toString" time="{duration}"/>
@ -625,11 +649,6 @@ TrickyTests.cpp:<line number>
<testcase classname="Scenario: This is a really long scenario name to see how the list command deals with wrapping" name="Given: A section name that is so long that it cannot fit in a single console width/When: The test headers are printed as part of the normal running of the scenario/Then: The, deliberately very long and overly verbose (you see what I did there?) section names must wrap, along with an indent" time="{duration}"/> <testcase classname="Scenario: This is a really long scenario name to see how the list command deals with wrapping" name="Given: A section name that is so long that it cannot fit in a single console width/When: The test headers are printed as part of the normal running of the scenario/Then: The, deliberately very long and overly verbose (you see what I did there?) section names must wrap, along with an indent" time="{duration}"/>
<testcase classname="Fixture" name="Scenario: BDD tests requiring Fixtures to provide commonly-accessed data or methods/Given: No operations precede me" time="{duration}"/> <testcase classname="Fixture" name="Scenario: BDD tests requiring Fixtures to provide commonly-accessed data or methods/Given: No operations precede me" time="{duration}"/>
<testcase classname="Fixture" name="Scenario: BDD tests requiring Fixtures to provide commonly-accessed data or methods/Given: No operations precede me/When: We get the count/Then: Subsequently values are higher" time="{duration}"/> <testcase classname="Fixture" name="Scenario: BDD tests requiring Fixtures to provide commonly-accessed data or methods/Given: No operations precede me/When: We get the count/Then: Subsequently values are higher" time="{duration}"/>
<testcase classname="section tracking" name="root" time="{duration}"/>
<testcase classname="section tracking" name="test case with no sections" time="{duration}"/>
<testcase classname="section tracking" name="test case with one section" time="{duration}"/>
<testcase classname="section tracking" name="test case with two consecutive sections" time="{duration}"/>
<testcase classname="section tracking" name="test case with one section within another" time="{duration}"/>
<system-out> <system-out>
Message from section one Message from section one
Message from section two Message from section two

File diff suppressed because it is too large Load Diff

View File

@ -80,15 +80,18 @@ TEST_CASE( "even more nested SECTION tests", "[sections]" )
{ {
SECTION( "d (leaf)", "" ) SECTION( "d (leaf)", "" )
{ {
SUCCEED(""); // avoid failing due to no tests
} }
SECTION( "e (leaf)", "" ) SECTION( "e (leaf)", "" )
{ {
SUCCEED(""); // avoid failing due to no tests
} }
} }
SECTION( "f (leaf)", "" ) SECTION( "f (leaf)", "" )
{ {
SUCCEED(""); // avoid failing due to no tests
} }
} }
@ -177,11 +180,11 @@ TEST_CASE( "xmlentitycheck", "" )
{ {
SECTION( "embedded xml", "<test>it should be possible to embed xml characters, such as <, \" or &, or even whole <xml>documents</xml> within an attribute</test>" ) SECTION( "embedded xml", "<test>it should be possible to embed xml characters, such as <, \" or &, or even whole <xml>documents</xml> within an attribute</test>" )
{ {
// No test SUCCEED(""); // We need this here to stop it failing due to no tests
} }
SECTION( "encoded chars", "these should all be encoded: &&&\"\"\"<<<&\"<<&\"" ) SECTION( "encoded chars", "these should all be encoded: &&&\"\"\"<<<&\"<<&\"" )
{ {
// No test SUCCEED(""); // We need this here to stop it failing due to no tests
} }
} }

View File

@ -0,0 +1,328 @@
/*
* Created by Phil on 1/10/2015.
* Copyright 2015 Two Blue Cubes Ltd
*
* 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_suppress_warnings.h"
#include "internal/catch_test_case_tracker.hpp"
namespace Catch
{
class LocalContext {
public:
TrackerContext& operator()() const {
return TrackerContext::instance();
}
};
} // namespace Catch
inline Catch::TrackerContext& C_A_T_C_H_Context() {
return Catch::TrackerContext::instance();
}
// -------------------
#include "catch.hpp"
using namespace Catch;
//inline void testCase( Catch::LocalContext const& C_A_T_C_H_Context ) {
//
// REQUIRE( C_A_T_C_H_Context().i() == 42 );
//}
TEST_CASE( "Tracker", "" ) {
TrackerContext ctx;
ctx.startRun();
ctx.startCycle();
ITracker& testCase = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase.isOpen() );
ITracker& s1 = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1.isOpen() );
SECTION( "successfully close one section", "" ) {
s1.close();
REQUIRE( s1.isSuccessfullyCompleted() );
REQUIRE( testCase.isComplete() == false );
testCase.close();
REQUIRE( ctx.completedCycle() );
REQUIRE( testCase.isSuccessfullyCompleted() );
}
SECTION( "fail one section", "" ) {
s1.fail();
REQUIRE( s1.isComplete() );
REQUIRE( s1.isSuccessfullyCompleted() == false );
REQUIRE( testCase.isComplete() == false );
testCase.close();
REQUIRE( ctx.completedCycle() );
REQUIRE( testCase.isSuccessfullyCompleted() == false );
SECTION( "re-enter after failed section", "" ) {
ctx.startCycle();
ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase2.isOpen() );
ITracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isOpen() == false );
testCase2.close();
REQUIRE( ctx.completedCycle() );
REQUIRE( testCase.isComplete() );
REQUIRE( testCase.isSuccessfullyCompleted() );
}
SECTION( "re-enter after failed section and find next section", "" ) {
ctx.startCycle();
ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase2.isOpen() );
ITracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isOpen() == false );
ITracker& s2 = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2.isOpen() );
s2.close();
REQUIRE( ctx.completedCycle() );
testCase2.close();
REQUIRE( testCase.isComplete() );
REQUIRE( testCase.isSuccessfullyCompleted() );
}
}
SECTION( "successfully close one section, then find another", "" ) {
s1.close();
ITracker& s2 = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2.isOpen() == false );
testCase.close();
REQUIRE( testCase.isComplete() == false );
SECTION( "Re-enter - skips S1 and enters S2", "" ) {
ctx.startCycle();
ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase2.isOpen() );
ITracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isOpen() == false );
ITracker& s2b = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2b.isOpen() );
REQUIRE( ctx.completedCycle() == false );
SECTION ("Successfully close S2") {
s2b.close();
REQUIRE( ctx.completedCycle() );
REQUIRE( s2b.isSuccessfullyCompleted() );
REQUIRE( testCase2.isComplete() == false );
testCase2.close();
REQUIRE( testCase2.isSuccessfullyCompleted() );
}
SECTION ("fail S2") {
s2b.fail();
REQUIRE( ctx.completedCycle() );
REQUIRE( s2b.isComplete() );
REQUIRE( s2b.isSuccessfullyCompleted() == false );
testCase2.close();
REQUIRE( testCase2.isSuccessfullyCompleted() == false );
// Need a final cycle
ctx.startCycle();
ITracker& testCase3 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase3.isOpen() );
ITracker& s1c = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1c.isOpen() == false );
ITracker& s2c = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2c.isOpen() == false );
testCase3.close();
REQUIRE( testCase3.isSuccessfullyCompleted() );
}
}
}
SECTION( "open a nested section", "" ) {
ITracker& s2 = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2.isOpen() );
s2.close();
REQUIRE( s2.isComplete() );
REQUIRE( s1.isComplete() == false );
s1.close();
REQUIRE( s1.isComplete() );
REQUIRE( testCase.isComplete() == false );
testCase.close();
REQUIRE( testCase.isComplete() );
}
SECTION( "start a generator", "" ) {
IndexTracker& g1 = IndexTracker::acquire( ctx, "G1", 2 );
REQUIRE( g1.isOpen() );
REQUIRE( g1.index() == 0 );
REQUIRE( g1.isComplete() == false );
REQUIRE( s1.isComplete() == false );
SECTION( "close outer section" )
{
s1.close();
REQUIRE( s1.isComplete() == false );
testCase.close();
REQUIRE( testCase.isSuccessfullyCompleted() == false );
SECTION( "Re-enter for second generation", "" ) {
ctx.startCycle();
ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase2.isOpen() );
ITracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isOpen() );
IndexTracker& g1b = IndexTracker::acquire( ctx, "G1", 2 );
REQUIRE( g1b.isOpen() );
REQUIRE( g1b.index() == 1 );
REQUIRE( s1.isComplete() == false );
s1b.close();
REQUIRE( s1b.isComplete() );
REQUIRE( g1b.isComplete() );
testCase2.close();
REQUIRE( testCase2.isComplete() );
}
}
SECTION( "Start a new inner section", "" ) {
ITracker& s2 = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2.isOpen() );
s2.close();
REQUIRE( s2.isComplete() );
s1.close();
REQUIRE( s1.isComplete() == false );
testCase.close();
REQUIRE( testCase.isComplete() == false );
SECTION( "Re-enter for second generation", "" ) {
ctx.startCycle();
ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase2.isOpen() );
ITracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isOpen() );
// generator - next value
IndexTracker& g1b = IndexTracker::acquire( ctx, "G1", 2 );
REQUIRE( g1b.isOpen() );
REQUIRE( g1b.index() == 1 );
// inner section again
ITracker& s2b = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2b.isOpen() );
s2b.close();
REQUIRE( s2b.isComplete() );
s1b.close();
REQUIRE( g1b.isComplete() );
REQUIRE( s1b.isComplete() );
testCase2.close();
REQUIRE( testCase2.isComplete() );
}
}
SECTION( "Fail an inner section", "" ) {
ITracker& s2 = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2.isOpen() );
s2.fail();
REQUIRE( s2.isComplete() );
REQUIRE( s2.isSuccessfullyCompleted() == false );
s1.close();
REQUIRE( s1.isComplete() == false );
testCase.close();
REQUIRE( testCase.isComplete() == false );
SECTION( "Re-enter for second generation", "" ) {
ctx.startCycle();
ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase2.isOpen() );
ITracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isOpen() );
// generator - still same value
IndexTracker& g1b = IndexTracker::acquire( ctx, "G1", 2 );
REQUIRE( g1b.isOpen() );
REQUIRE( g1b.index() == 0 );
// inner section again - this time won't open
ITracker& s2b = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2b.isOpen() == false );
s1b.close();
REQUIRE( g1b.isComplete() == false );
REQUIRE( s1b.isComplete() == false );
testCase2.close();
REQUIRE( testCase2.isComplete() == false );
// Another cycle - now should complete
ctx.startCycle();
ITracker& testCase3 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase3.isOpen() );
ITracker& s1c = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1c.isOpen() );
// generator - now next value
IndexTracker& g1c = IndexTracker::acquire( ctx, "G1", 2 );
REQUIRE( g1c.isOpen() );
REQUIRE( g1c.index() == 1 );
// inner section - now should open again
ITracker& s2c = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2c.isOpen() );
s2c.close();
REQUIRE( s2c.isComplete() );
s1c.close();
REQUIRE( g1c.isComplete() );
REQUIRE( s1c.isComplete() );
testCase3.close();
REQUIRE( testCase3.isComplete() );
}
}
// !TBD"
// nested generator
// two sections within a generator
}
}

View File

@ -1,118 +0,0 @@
/*
* Created by Phil on 20/07/2013.
* Copyright 2013 Two Blue Cubes Ltd
*
* 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)
*/
#ifdef __clang__
# pragma clang diagnostic ignored "-Wpadded"
# pragma clang diagnostic ignored "-Wc++98-compat"
#endif
#include "internal/catch_test_case_tracker.hpp"
#include "catch.hpp"
TEST_CASE( "section tracking", "" ) {
using namespace Catch;
TestCaseTracker testCaseTracker( "test case" );
const std::string section1Name = "section 1";
const std::string section2Name = "section 2";
CHECK_FALSE( testCaseTracker.isCompleted() );
SECTION( "test case with no sections", "" ) {
{
TestCaseTracker::Guard guard( testCaseTracker );
CHECK_FALSE( testCaseTracker.isCompleted() );
}
CHECK( testCaseTracker.isCompleted() );
}
SECTION( "test case with one section", "" ) {
{
TestCaseTracker::Guard guard( testCaseTracker );
// Enter section? - yes
CHECK( testCaseTracker.enterSection( section1Name ) );
CHECK_FALSE( testCaseTracker.isCompleted() );
testCaseTracker.leaveSection();
// Leave test case - now complete
}
CHECK( testCaseTracker.isCompleted() );
// ...
// Enter test case again
{
TestCaseTracker::Guard guard( testCaseTracker );
// Enter section? - no - now complete
CHECK_FALSE( testCaseTracker.enterSection( section1Name ) );
}
}
SECTION( "test case with two consecutive sections", "" ) {
// Enter test case
{
TestCaseTracker::Guard guard( testCaseTracker );
// Enter section 1? - yes
CHECK( testCaseTracker.enterSection( section1Name ) );
testCaseTracker.leaveSection();
// Enter section 2? - no - we just exected section 1
CHECK_FALSE( testCaseTracker.enterSection( section2Name ) );
// Leave test case - incomplete (still need to visit section 2)
}
CHECK_FALSE( testCaseTracker.isCompleted() );
// ...
// Enter test case again
{
TestCaseTracker::Guard guard( testCaseTracker );
// Enter section 1? - no, already done now
CHECK_FALSE( testCaseTracker.enterSection( section1Name ) );
// Enter section 2? - yes
CHECK( testCaseTracker.enterSection( section2Name ) );
testCaseTracker.leaveSection();
// Leave test case - now complete
}
CHECK( testCaseTracker.isCompleted() );
}
SECTION( "test case with one section within another", "" ) {
// Enter test case again
{
TestCaseTracker::Guard guard( testCaseTracker );
// Enter section 1? - yes
CHECK( testCaseTracker.enterSection( section1Name ) );
// Enter section 2? - yes
CHECK( testCaseTracker.enterSection( section2Name ) );
CHECK_FALSE( testCaseTracker.isCompleted() );
testCaseTracker.leaveSection(); // section 2
testCaseTracker.leaveSection(); // section 1
// Leave test case - now complete
}
CHECK( testCaseTracker.isCompleted() );
}
}

View File

@ -1,2 +1,3 @@
// This file is only here to verify (to the extent possible) the self sufficiency of the header // This file is only here to verify (to the extent possible) the self sufficiency of the header
#include "catch_suppress_warnings.h"
#include "catch_stream.h" #include "catch_stream.h"

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
26059AF21BD4B94C003D575C /* PartTrackerTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26059AF11BD4B94C003D575C /* PartTrackerTests.cpp */; };
263F7A4719B6FCBF009474C2 /* EnumToString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263F7A4619B6FCBF009474C2 /* EnumToString.cpp */; }; 263F7A4719B6FCBF009474C2 /* EnumToString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263F7A4619B6FCBF009474C2 /* EnumToString.cpp */; };
263F7A4B19B6FE1E009474C2 /* ToStringPair.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263F7A4819B6FE1E009474C2 /* ToStringPair.cpp */; }; 263F7A4B19B6FE1E009474C2 /* ToStringPair.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263F7A4819B6FE1E009474C2 /* ToStringPair.cpp */; };
263F7A4C19B6FE1E009474C2 /* ToStringVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263F7A4919B6FE1E009474C2 /* ToStringVector.cpp */; }; 263F7A4C19B6FE1E009474C2 /* ToStringVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263F7A4919B6FE1E009474C2 /* ToStringVector.cpp */; };
@ -17,7 +18,6 @@
26711C8F195D465C0033EDA2 /* TagAliasTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26711C8D195D465C0033EDA2 /* TagAliasTests.cpp */; }; 26711C8F195D465C0033EDA2 /* TagAliasTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26711C8D195D465C0033EDA2 /* TagAliasTests.cpp */; };
26847E5F16BBADB40043B9C1 /* catch_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26847E5D16BBADB40043B9C1 /* catch_message.cpp */; }; 26847E5F16BBADB40043B9C1 /* catch_message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26847E5D16BBADB40043B9C1 /* catch_message.cpp */; };
2691574C1A532A280054F1ED /* ToStringTuple.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2691574B1A532A280054F1ED /* ToStringTuple.cpp */; }; 2691574C1A532A280054F1ED /* ToStringTuple.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2691574B1A532A280054F1ED /* ToStringTuple.cpp */; };
26948286179A9AB900ED166E /* SectionTrackerTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26948284179A9AB900ED166E /* SectionTrackerTests.cpp */; };
2694A1FD16A0000E004816E3 /* catch_text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2694A1FB16A0000E004816E3 /* catch_text.cpp */; }; 2694A1FD16A0000E004816E3 /* catch_text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2694A1FB16A0000E004816E3 /* catch_text.cpp */; };
26E1B7D319213BC900812682 /* CmdLineTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E1B7D119213BC900812682 /* CmdLineTests.cpp */; }; 26E1B7D319213BC900812682 /* CmdLineTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E1B7D119213BC900812682 /* CmdLineTests.cpp */; };
4A45DA2416161EF9004F8D6B /* catch_console_colour.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A45DA2316161EF9004F8D6B /* catch_console_colour.cpp */; }; 4A45DA2416161EF9004F8D6B /* catch_console_colour.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A45DA2316161EF9004F8D6B /* catch_console_colour.cpp */; };
@ -62,6 +62,7 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
26059AF11BD4B94C003D575C /* PartTrackerTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PartTrackerTests.cpp; path = ../../../SelfTest/PartTrackerTests.cpp; sourceTree = "<group>"; };
261488FA184C81130041FBEB /* catch_test_spec.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_test_spec.hpp; sourceTree = "<group>"; }; 261488FA184C81130041FBEB /* catch_test_spec.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_test_spec.hpp; sourceTree = "<group>"; };
261488FC184D1DC10041FBEB /* catch_stream.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_stream.h; sourceTree = "<group>"; }; 261488FC184D1DC10041FBEB /* catch_stream.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_stream.h; sourceTree = "<group>"; };
261488FD184D21290041FBEB /* catch_section_info.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_section_info.h; sourceTree = "<group>"; }; 261488FD184D21290041FBEB /* catch_section_info.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_section_info.h; sourceTree = "<group>"; };
@ -100,7 +101,6 @@
2691574B1A532A280054F1ED /* ToStringTuple.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ToStringTuple.cpp; path = ../../../SelfTest/ToStringTuple.cpp; sourceTree = "<group>"; }; 2691574B1A532A280054F1ED /* ToStringTuple.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ToStringTuple.cpp; path = ../../../SelfTest/ToStringTuple.cpp; sourceTree = "<group>"; };
26926E8318D7777D004E10F2 /* clara.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = clara.h; path = ../../../../include/external/clara.h; sourceTree = "<group>"; }; 26926E8318D7777D004E10F2 /* clara.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = clara.h; path = ../../../../include/external/clara.h; sourceTree = "<group>"; };
26926E8418D77809004E10F2 /* tbc_text_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tbc_text_format.h; path = ../../../../include/external/tbc_text_format.h; sourceTree = "<group>"; }; 26926E8418D77809004E10F2 /* tbc_text_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tbc_text_format.h; path = ../../../../include/external/tbc_text_format.h; sourceTree = "<group>"; };
26948284179A9AB900ED166E /* SectionTrackerTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SectionTrackerTests.cpp; path = ../../../SelfTest/SectionTrackerTests.cpp; sourceTree = "<group>"; };
26948287179EF7F900ED166E /* catch_test_case_tracker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_test_case_tracker.hpp; sourceTree = "<group>"; }; 26948287179EF7F900ED166E /* catch_test_case_tracker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_test_case_tracker.hpp; sourceTree = "<group>"; };
2694A1FB16A0000E004816E3 /* catch_text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = catch_text.cpp; sourceTree = "<group>"; }; 2694A1FB16A0000E004816E3 /* catch_text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = catch_text.cpp; sourceTree = "<group>"; };
269831E519078C1600BB0CE0 /* catch_tostring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = catch_tostring.h; sourceTree = "<group>"; }; 269831E519078C1600BB0CE0 /* catch_tostring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = catch_tostring.h; sourceTree = "<group>"; };
@ -212,7 +212,7 @@
266E9AD317290E710061DAB2 /* Introspective Tests */ = { 266E9AD317290E710061DAB2 /* Introspective Tests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
26948284179A9AB900ED166E /* SectionTrackerTests.cpp */, 26059AF11BD4B94C003D575C /* PartTrackerTests.cpp */,
26E1B7D119213BC900812682 /* CmdLineTests.cpp */, 26E1B7D119213BC900812682 /* CmdLineTests.cpp */,
26711C8D195D465C0033EDA2 /* TagAliasTests.cpp */, 26711C8D195D465C0033EDA2 /* TagAliasTests.cpp */,
); );
@ -548,6 +548,7 @@
4A6D0C3E149B3D9E00DB3EAA /* TestMain.cpp in Sources */, 4A6D0C3E149B3D9E00DB3EAA /* TestMain.cpp in Sources */,
4A6D0C3F149B3D9E00DB3EAA /* TrickyTests.cpp in Sources */, 4A6D0C3F149B3D9E00DB3EAA /* TrickyTests.cpp in Sources */,
263F7A4D19B6FE1E009474C2 /* ToStringWhich.cpp in Sources */, 263F7A4D19B6FE1E009474C2 /* ToStringWhich.cpp in Sources */,
26059AF21BD4B94C003D575C /* PartTrackerTests.cpp in Sources */,
263F7A4B19B6FE1E009474C2 /* ToStringPair.cpp in Sources */, 263F7A4B19B6FE1E009474C2 /* ToStringPair.cpp in Sources */,
4AEE032016142F910071E950 /* catch_common.cpp in Sources */, 4AEE032016142F910071E950 /* catch_common.cpp in Sources */,
263F7A4C19B6FE1E009474C2 /* ToStringVector.cpp in Sources */, 263F7A4C19B6FE1E009474C2 /* ToStringVector.cpp in Sources */,
@ -574,7 +575,6 @@
26847E5F16BBADB40043B9C1 /* catch_message.cpp in Sources */, 26847E5F16BBADB40043B9C1 /* catch_message.cpp in Sources */,
266B06B816F3A60A004ED264 /* VariadicMacrosTests.cpp in Sources */, 266B06B816F3A60A004ED264 /* VariadicMacrosTests.cpp in Sources */,
266ECD74170F3C620030D735 /* BDDTests.cpp in Sources */, 266ECD74170F3C620030D735 /* BDDTests.cpp in Sources */,
26948286179A9AB900ED166E /* SectionTrackerTests.cpp in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };