diff options
| author | Phil Nash | 2015-11-03 07:55:29 +0000 |
|---|---|---|
| committer | Phil Nash | 2015-11-03 07:55:29 +0000 |
| commit | 4636be974492e2971e5c14940516e72e63ece95d (patch) | |
| tree | 9fdeb537e4f3ae925782761d361da731a62970e9 /include | |
| parent | b3b23520454c96efb02a0dc274cc0c05ce130792 (diff) | |
| parent | 015e07100eebdf0557fbd08154e6adc1cf128ae2 (diff) | |
| download | catch3-4636be974492e2971e5c14940516e72e63ece95d.tar.gz | |
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
Diffstat (limited to 'include')
| -rw-r--r-- | include/catch_session.hpp | 28 | ||||
| -rw-r--r-- | include/internal/catch_config.hpp | 39 | ||||
| -rw-r--r-- | include/internal/catch_context_impl.hpp | 8 | ||||
| -rw-r--r-- | include/internal/catch_impl.hpp | 11 | ||||
| -rw-r--r-- | include/internal/catch_interfaces_reporter.h | 10 | ||||
| -rw-r--r-- | include/internal/catch_reporter_registry.hpp | 2 | ||||
| -rw-r--r-- | include/internal/catch_run_context.hpp | 54 | ||||
| -rw-r--r-- | include/internal/catch_stream.h | 50 | ||||
| -rw-r--r-- | include/internal/catch_stream.hpp | 47 | ||||
| -rw-r--r-- | include/internal/catch_suppress_warnings.h | 1 | ||||
| -rw-r--r-- | include/internal/catch_test_case_tracker.hpp | 364 | ||||
| -rw-r--r-- | include/reporters/catch_reporter_bases.hpp | 4 |
12 files changed, 410 insertions, 208 deletions
diff --git a/include/catch_session.hpp b/include/catch_session.hpp index 50623ad4..c63f517b 100644 --- a/include/catch_session.hpp +++ b/include/catch_session.hpp @@ -43,7 +43,7 @@ namespace Catch { reporter = addReporter( reporter, createReporter( *it, config ) ); 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(); for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); it != itEnd; @@ -52,27 +52,15 @@ namespace Catch { 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 ) { - std::ofstream ofs; - openStreamInto( config, ofs ); + Ptr<IConfig const> iconfig = config.get(); + Ptr<IStreamingReporter> reporter = makeReporter( config ); - reporter = addListeners( config.get(), reporter ); + reporter = addListeners( iconfig, reporter ); - RunContext context( config.get(), reporter ); + RunContext context( iconfig, reporter ); Totals totals; @@ -82,17 +70,17 @@ namespace Catch { if( !testSpec.hasFilters() ) 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(); it != itEnd; ++it ) { - if( !context.aborting() && matchTest( *it, testSpec, *config ) ) + if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) totals += context.runTest( *it ); else reporter->skipTest( *it ); } - context.testGroupEnded( config->name(), totals, 1, 1 ); + context.testGroupEnded( iconfig->name(), totals, 1, 1 ); return totals; } diff --git a/include/internal/catch_config.hpp b/include/internal/catch_config.hpp index 6b2b04a3..c0d0948e 100644 --- a/include/internal/catch_config.hpp +++ b/include/internal/catch_config.hpp @@ -85,12 +85,11 @@ namespace Catch { public: Config() - : m_os( Catch::cout().rdbuf() ) {} Config( ConfigData const& data ) : m_data( data ), - m_os( Catch::cout().rdbuf() ) + m_stream( openStream() ) { if( !data.testsOrTags.empty() ) { TestSpecParser parser( ITagAliasRegistry::get() ); @@ -101,12 +100,6 @@ namespace Catch { } 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 { @@ -122,17 +115,6 @@ namespace Catch { 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; } int abortAfter() const { return m_data.abortAfter; } @@ -144,7 +126,7 @@ namespace Catch { // IConfig interface 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 bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } @@ -154,14 +136,25 @@ namespace Catch { virtual bool forceColour() const { return m_data.forceColour; } 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; - Stream m_stream; - mutable std::ostream m_os; + std::auto_ptr<IStream const> m_stream; TestSpec m_testSpec; }; - } // end namespace Catch #endif // TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED diff --git a/include/internal/catch_context_impl.hpp b/include/internal/catch_context_impl.hpp index a3ac2ae5..030f29e2 100644 --- a/include/internal/catch_context_impl.hpp +++ b/include/internal/catch_context_impl.hpp @@ -95,14 +95,6 @@ namespace Catch { 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() { delete currentContext; currentContext = CATCH_NULL; diff --git a/include/internal/catch_impl.hpp b/include/internal/catch_impl.hpp index 3ff5ac00..baabd7ab 100644 --- a/include/internal/catch_impl.hpp +++ b/include/internal/catch_impl.hpp @@ -35,6 +35,7 @@ #include "catch_tostring.hpp" #include "catch_result_builder.hpp" #include "catch_tag_alias_registry.hpp" +#include "catch_test_case_tracker.hpp" #include "../reporters/catch_reporter_multi.hpp" #include "../reporters/catch_reporter_xml.hpp" @@ -43,8 +44,11 @@ #include "../reporters/catch_reporter_compact.hpp" namespace Catch { + // These are all here to avoid warnings about not having any out of line + // virtual methods NonCopyable::~NonCopyable() {} IShared::~IShared() {} + IStream::~IStream() CATCH_NOEXCEPT {} StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} IContext::~IContext() {} IResultCapture::~IResultCapture() {} @@ -90,6 +94,13 @@ namespace Catch { Matchers::Impl::StdString::EndsWith::~EndsWith() {} void Config::dummy() {} + + namespace TestCaseTracking { + ITracker::~ITracker() {} + TrackerBase::~TrackerBase() {} + SectionTracker::~SectionTracker() {} + IndexTracker::~IndexTracker() {} + } } #ifdef __clang__ diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h index 46720fbb..e40119df 100644 --- a/include/internal/catch_interfaces_reporter.h +++ b/include/internal/catch_interfaces_reporter.h @@ -26,18 +26,18 @@ namespace Catch { struct ReporterConfig { - explicit ReporterConfig( Ptr<IConfig> const& _fullConfig ) + explicit ReporterConfig( Ptr<IConfig const> const& _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 ) {} std::ostream& stream() const { return *m_stream; } - Ptr<IConfig> fullConfig() const { return m_fullConfig; } + Ptr<IConfig const> fullConfig() const { return m_fullConfig; } private: std::ostream* m_stream; - Ptr<IConfig> m_fullConfig; + Ptr<IConfig const> m_fullConfig; }; struct ReporterPreferences { @@ -261,7 +261,7 @@ namespace Catch typedef std::vector<Ptr<IReporterFactory> > Listeners; 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 Listeners const& getListeners() const = 0; }; diff --git a/include/internal/catch_reporter_registry.hpp b/include/internal/catch_reporter_registry.hpp index b67252f3..71f23ff7 100644 --- a/include/internal/catch_reporter_registry.hpp +++ b/include/internal/catch_reporter_registry.hpp @@ -20,7 +20,7 @@ namespace Catch { 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 ); if( it == m_factories.end() ) return CATCH_NULL; diff --git a/include/internal/catch_run_context.hpp b/include/internal/catch_run_context.hpp index 3ec12af6..a3098b5f 100644 --- a/include/internal/catch_run_context.hpp +++ b/include/internal/catch_run_context.hpp @@ -64,10 +64,7 @@ namespace Catch { m_context( getCurrentMutableContext() ), m_activeTestCase( CATCH_NULL ), m_config( _config ), - m_reporter( reporter ), - m_prevRunner( m_context.getRunner() ), - m_prevResultCapture( m_context.getResultCapture() ), - m_prevConfig( m_context.getConfig() ) + m_reporter( reporter ) { m_context.setRunner( this ); m_context.setConfig( m_config ); @@ -77,10 +74,6 @@ namespace Catch { virtual ~RunContext() { 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 ) { @@ -101,14 +94,18 @@ namespace Catch { m_reporter->testCaseStarting( testInfo ); m_activeTestCase = &testCase; - m_testCaseTracker = TestCaseTracker( testInfo.name ); + do { + m_trackerContext.startRun(); do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); 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() ); Totals deltaTotals = m_totals.delta( prevTotals ); @@ -120,7 +117,7 @@ namespace Catch { aborting() ) ); m_activeTestCase = CATCH_NULL; - m_testCaseTracker.reset(); + m_testCaseTracker = CATCH_NULL; return deltaTotals; } @@ -156,9 +153,11 @@ namespace Catch { std::ostringstream oss; oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - if( !m_testCaseTracker->enterSection( oss.str() ) ) + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + if( !sectionTracker.isOpen() ) return false; - + m_activeSections.push_back( §ionTracker ); + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_reporter->sectionStarting( sectionInfo ); @@ -168,9 +167,11 @@ namespace Catch { return true; } bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 || - !m_config->warnAboutMissingAssertions() || - m_testCaseTracker->currentSectionHasChildren() ) + if( assertions.total() != 0 ) + return false; + if( m_config->warnAboutMissingAssertions() ) + return false; + if( m_trackerContext.currentTracker().hasChildren() ) return false; m_totals.assertions.failed++; assertions.failed++; @@ -181,13 +182,22 @@ namespace Catch { Counts assertions = m_totals.assertions - endInfo.prevAssertions; 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_messages.clear(); } 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 ); } @@ -256,7 +266,6 @@ namespace Catch { double duration = 0; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); - TestCaseTracker::Guard guard( *m_testCaseTracker ); seedRng( *m_config ); @@ -278,6 +287,7 @@ namespace Catch { catch(...) { makeUnexpectedResultBuilder().useActiveException(); } + m_testCaseTracker->close(); handleUnfinishedSections(); m_messages.clear(); @@ -323,18 +333,18 @@ namespace Catch { TestRunInfo m_runInfo; IMutableContext& m_context; TestCase const* m_activeTestCase; - Option<TestCaseTracker> m_testCaseTracker; + ITracker* m_testCaseTracker; + ITracker* m_currentSectionTracker; AssertionResult m_lastResult; Ptr<IConfig const> m_config; Totals m_totals; Ptr<IStreamingReporter> m_reporter; std::vector<MessageInfo> m_messages; - IRunner* m_prevRunner; - IResultCapture* m_prevResultCapture; - Ptr<IConfig const> m_prevConfig; AssertionInfo m_lastAssertionInfo; std::vector<SectionEndInfo> m_unfinishedSections; + std::vector<ITracker*> m_activeSections; + TrackerContext m_trackerContext; }; IResultCapture& getResultCapture() { diff --git a/include/internal/catch_stream.h b/include/internal/catch_stream.h index 402a6617..a69c841e 100644 --- a/include/internal/catch_stream.h +++ b/include/internal/catch_stream.h @@ -9,28 +9,52 @@ #ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED -#include <streambuf> +#include "catch_compiler_capabilities.h" +#include "catch_streambuf.h" -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wpadded" -#endif +#include <streambuf> +#include <ostream> +#include <fstream> namespace Catch { - class Stream { + std::ostream& cout(); + 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: - Stream(); - Stream( std::streambuf* _streamBuf, bool _isOwned ); - void release(); + FileStream( std::string const& filename ); + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + - std::streambuf* streamBuf; + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + CoutStream(); - private: - bool isOwned; + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; }; - std::ostream& cout(); - std::ostream& cerr(); + + 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 diff --git a/include/internal/catch_stream.hpp b/include/internal/catch_stream.hpp index 42933487..e6bf7b0c 100644 --- a/include/internal/catch_stream.hpp +++ b/include/internal/catch_stream.hpp @@ -10,7 +10,6 @@ #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED #include "catch_stream.h" -#include "catch_streambuf.h" #include "catch_debugger.h" #include <stdexcept> @@ -57,29 +56,47 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// - struct OutputDebugWriter { + 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 { + void operator()( std::string const&str ) { writeToDebugConsole( str ); } }; - - Stream::Stream() - : streamBuf( CATCH_NULL ), isOwned( false ) + + DebugOutStream::DebugOutStream() + : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), + m_os( m_streamBuf.get() ) {} - - Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) - : streamBuf( _streamBuf ), isOwned( _isOwned ) + + std::ostream& DebugOutStream::stream() const { + return m_os; + } + + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream::CoutStream() + : m_os( Catch::cout().rdbuf() ) {} - void Stream::release() { - if( isOwned ) { - delete streamBuf; - streamBuf = CATCH_NULL; - isOwned = false; - } + std::ostream& CoutStream::stream() const { + return m_os; } - + + #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions std::ostream& cout() { return std::cout; diff --git a/include/internal/catch_suppress_warnings.h b/include/internal/catch_suppress_warnings.h index 094ed98b..8f57b285 100644 --- a/include/internal/catch_suppress_warnings.h +++ b/include/internal/catch_suppress_warnings.h @@ -19,6 +19,7 @@ # pragma clang diagnostic ignored "-Wc++98-compat" # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" diff --git a/include/internal/catch_test_case_tracker.hpp b/include/internal/catch_test_case_tracker.hpp index eb2616e0..713ec96c 100644 --- a/include/internal/catch_test_case_tracker.hpp +++ b/include/internal/catch_test_case_tracker.hpp @@ -9,141 +9,307 @@ #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #include "catch_compiler_capabilities.h" +#include "catch_ptr.hpp" #include <map> #include <string> #include <assert.h> +#include <vector> 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 { NotStarted, Executing, - ExecutingChildren, - Completed + CompletedCycle }; - TrackedSection( std::string const& name, TrackedSection* parent ) - : m_name( name ), m_runState( NotStarted ), m_parent( parent ) + Ptr<ITracker> m_rootTracker; + 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 ); - TrackedSection* acquireChild( std::string const& childName ); - - void enter() { - if( m_runState == NotStarted ) - m_runState = Executing; + ITracker& startRun(); + + void endRun() { + m_rootTracker.reset(); + m_currentTracker = CATCH_NULL; + m_runState = NotStarted; } - void leave(); - - TrackedSection* getParent() { - return m_parent; + + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; } - bool hasChildren() const { - return !m_children.empty(); + void completeCycle() { + m_runState = CompletedCycle; } - private: - std::string m_name; - RunState m_runState; - TrackedSections m_children; - TrackedSection* m_parent; + bool completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& currentTracker() { + return *m_currentTracker; + } + void setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } }; - inline TrackedSection* TrackedSection::findChild( std::string const& childName ) { - TrackedSections::iterator it = m_children.find( childName ); - return it != m_children.end() - ? &it->second - : CATCH_NULL; - } - inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) { - if( TrackedSection* child = findChild( childName ) ) - return child; - m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); - return findChild( childName ); - } - inline void TrackedSection::leave() { - for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); - it != itEnd; - ++it ) - if( it->second.runState() != Completed ) { - m_runState = ExecutingChildren; - return; + 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; } - m_runState = Completed; - } - - class TestCaseTracker { + }; + typedef std::vector<Ptr<ITracker> > Children; + std::string m_name; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState; public: - TestCaseTracker( std::string const& testCaseName ) - : m_testCase( testCaseName, CATCH_NULL ), - m_currentSection( &m_testCase ), - m_completedASectionThisRun( false ) + TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) + : m_name( name ), + m_ctx( ctx ), + m_parent( parent ), + m_runState( NotStarted ) {} - - 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; + virtual ~TrackerBase(); + + virtual std::string name() const CATCH_OVERRIDE { + return m_name; } - void leaveSection() { - m_currentSection->leave(); - m_currentSection = m_currentSection->getParent(); - assert( m_currentSection != CATCH_NULL ); - m_completedASectionThisRun = true; + virtual bool isComplete() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully || m_runState == Failed; } - - bool currentSectionHasChildren() const { - return m_currentSection->hasChildren(); + virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully; } - bool isCompleted() const { - return m_testCase.runState() == TrackedSection::Completed; + virtual bool isOpen() const CATCH_OVERRIDE { + return m_runState != NotStarted && !isComplete(); } - - class Guard { - public: - Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { - m_tracker.enterTestCase(); + virtual bo |