Added IndexTracker and got it working with a single generator

- made some simplifications to state machine
This commit is contained in:
Phil Nash 2015-10-29 08:02:40 +00:00
parent 9a6a0865f2
commit 293e54dcbe

View File

@ -23,8 +23,10 @@ namespace Catch
struct IPartTracker : SharedImpl<> { struct IPartTracker : SharedImpl<> {
virtual ~IPartTracker() {} virtual ~IPartTracker() {}
// queries // static queries
virtual std::string name() const = 0; virtual std::string name() const = 0;
// dynamic queries
virtual bool hasStarted() const = 0; // true even if ended virtual bool hasStarted() const = 0; // true even if ended
virtual bool hasEnded() const = 0; virtual bool hasEnded() const = 0;
virtual bool isSuccessfullyCompleted() const = 0; virtual bool isSuccessfullyCompleted() const = 0;
@ -35,12 +37,11 @@ namespace Catch
// actions // actions
virtual void close() = 0; virtual void close() = 0;
virtual void fail() = 0; virtual void fail() = 0;
virtual void markAsNeedingAnotherRun() =0;
virtual void addChild( Ptr<IPartTracker> const& child ) = 0; virtual void addChild( Ptr<IPartTracker> const& child ) = 0;
virtual IPartTracker* findChild( std::string const& name ) = 0; virtual IPartTracker* findChild( std::string const& name ) = 0;
virtual void openChild() = 0; virtual void openChild() = 0;
virtual void childFailed() = 0;
}; };
@ -103,13 +104,14 @@ namespace Catch
}; };
class PartTrackerBase : public IPartTracker { class PartTrackerBase : public IPartTracker {
protected:
enum RunState { enum RunState {
NotStarted, NotStarted,
Executing, Executing,
ExecutingChildren, ExecutingChildren,
NeedsAnotherRun,
CompletedSuccessfully, CompletedSuccessfully,
Failed, Failed
ChildFailed
}; };
class TrackerHasName { class TrackerHasName {
std::string m_name; std::string m_name;
@ -146,7 +148,7 @@ namespace Catch
return m_runState != NotStarted; return m_runState != NotStarted;
} }
virtual bool isOpen() const CATCH_OVERRIDE { virtual bool isOpen() const CATCH_OVERRIDE {
return m_runState == Executing || m_runState == ExecutingChildren; return hasStarted() && !hasEnded();
} }
@ -158,8 +160,8 @@ namespace Catch
virtual IPartTracker* findChild( std::string const& name ) CATCH_OVERRIDE { virtual IPartTracker* findChild( std::string const& name ) CATCH_OVERRIDE {
Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) );
return( it != m_children.end() ) return( it != m_children.end() )
? it->get() ? it->get()
: CATCH_NULL; : CATCH_NULL;
} }
virtual IPartTracker& parent() CATCH_OVERRIDE { virtual IPartTracker& parent() CATCH_OVERRIDE {
assert( m_parent ); // Should always be non-null except for root assert( m_parent ); // Should always be non-null except for root
@ -173,12 +175,6 @@ namespace Catch
m_parent->openChild(); m_parent->openChild();
} }
} }
virtual void childFailed() CATCH_OVERRIDE {
assert( m_runState == ExecutingChildren );
m_runState = ChildFailed;
if( m_parent )
m_parent->childFailed();
}
void open() { void open() {
m_runState = Executing; m_runState = Executing;
moveToThis(); moveToThis();
@ -187,16 +183,25 @@ namespace Catch
} }
virtual void close() CATCH_OVERRIDE { virtual void close() CATCH_OVERRIDE {
// Close any still open children (e.g. generators)
while( &m_ctx.currentPart() != this )
m_ctx.currentPart().close();
switch( m_runState ) { switch( m_runState ) {
case CompletedSuccessfully:
case Failed:
return;
case Executing: case Executing:
m_runState = CompletedSuccessfully; m_runState = CompletedSuccessfully;
break; break;
case ExecutingChildren: case ExecutingChildren:
if( !hasUnstartedChildren() ) if( m_children.empty() || m_children.back()->hasEnded() )
m_runState = CompletedSuccessfully; m_runState = CompletedSuccessfully;
break; break;
case ChildFailed: case NeedsAnotherRun:
m_runState = ExecutingChildren; m_runState = Executing;
break; break;
default: default:
throw std::logic_error( "Unexpected state" ); throw std::logic_error( "Unexpected state" );
@ -207,21 +212,21 @@ namespace Catch
virtual void fail() CATCH_OVERRIDE { virtual void fail() CATCH_OVERRIDE {
m_runState = Failed; m_runState = Failed;
if( m_parent ) if( m_parent )
m_parent->childFailed(); m_parent->markAsNeedingAnotherRun();
moveToParent(); moveToParent();
m_ctx.completeCycle(); m_ctx.completeCycle();
} }
virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
m_runState = NeedsAnotherRun;
}
private: private:
void moveToParent() { void moveToParent() {
assert( m_parent );
m_ctx.setCurrentPart( m_parent ); m_ctx.setCurrentPart( m_parent );
} }
void moveToThis() { void moveToThis() {
m_ctx.setCurrentPart( this ); m_ctx.setCurrentPart( this );
} }
bool hasUnstartedChildren() const {
return !m_children.empty() && !m_children.back()->hasStarted();
}
}; };
@ -245,12 +250,60 @@ namespace Catch
currentPart.addChild( section ); currentPart.addChild( section );
} }
if( !ctx.completedCycle() && !section->hasEnded() ) { if( !ctx.completedCycle() && !section->hasEnded() ) {
section->open(); section->open();
} }
return *section; return *section;
} }
}; };
class IndexTracker : public PartTrackerBase {
int m_size;
int m_index;
public:
IndexTracker( std::string const& name, TrackerContext& ctx, IPartTracker* parent, int size )
: PartTrackerBase( name, ctx, parent ),
m_size( size ),
m_index( -1 )
{}
static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) {
IndexTracker* tracker = CATCH_NULL;
IPartTracker& currentPart = ctx.currentPart();
if( IPartTracker* part = currentPart.findChild( name ) ) {
tracker = dynamic_cast<IndexTracker*>( part );
assert( tracker );
}
else {
tracker = new IndexTracker( name, ctx, &currentPart, size );
currentPart.addChild( tracker );
}
if( !ctx.completedCycle() && !tracker->hasEnded() ) {
if( tracker->m_runState != ExecutingChildren )
tracker->moveNext();
tracker->open();
}
return *tracker;
}
int index() const { return m_index; }
void moveNext() {
m_index++;
m_children.clear();
}
virtual void close() CATCH_OVERRIDE {
PartTrackerBase::close();
if( m_runState == CompletedSuccessfully )
if( m_index < m_size-1 )
m_runState = Executing;
}
};
IPartTracker& TrackerContext::startRun() { IPartTracker& TrackerContext::startRun() {
m_rootPart = new SectionTracker( "{root}", *this, CATCH_NULL ); m_rootPart = new SectionTracker( "{root}", *this, CATCH_NULL );
m_currentPart = CATCH_NULL; m_currentPart = CATCH_NULL;
@ -302,9 +355,8 @@ TEST_CASE( "PartTracker" ) {
REQUIRE( testCase.hasEnded() == false ); REQUIRE( testCase.hasEnded() == false );
testCase.close(); testCase.close();
REQUIRE( testCase.isSuccessfullyCompleted() == true );
REQUIRE( ctx.completedCycle() == true ); REQUIRE( ctx.completedCycle() == true );
REQUIRE( testCase.isSuccessfullyCompleted() == true );
} }
SECTION( "fail one section" ) { SECTION( "fail one section" ) {
@ -324,7 +376,7 @@ TEST_CASE( "PartTracker" ) {
REQUIRE( testCase2.isSuccessfullyCompleted() == false ); REQUIRE( testCase2.isSuccessfullyCompleted() == false );
IPartTracker& s1b = SectionTracker::acquire( ctx, "S1" ); IPartTracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isSuccessfullyCompleted() == false ); REQUIRE( s1b.isOpen() == false );
testCase2.close(); testCase2.close();
REQUIRE( ctx.completedCycle() == true ); REQUIRE( ctx.completedCycle() == true );
@ -414,7 +466,88 @@ TEST_CASE( "PartTracker" ) {
testCase.close(); testCase.close();
REQUIRE( testCase.isSuccessfullyCompleted() == true ); REQUIRE( testCase.isSuccessfullyCompleted() == true );
} }
SECTION( "start a generator" ) {
IndexTracker& g1 = IndexTracker::acquire( ctx, "G1", 2 );
REQUIRE( g1.isOpen() == true );
REQUIRE( g1.index() == 0 );
REQUIRE( g1.isSuccessfullyCompleted() == false );
REQUIRE( s1.isSuccessfullyCompleted() == false );
SECTION( "close outer section" )
{
s1.close();
REQUIRE( s1.isSuccessfullyCompleted() == false );
testCase.close();
REQUIRE( testCase.isSuccessfullyCompleted() == false );
SECTION( "Re-enter for second generation" ) {
ctx.startCycle();
IPartTracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase2.isOpen() == true );
IPartTracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isOpen() == true );
IndexTracker& g1b = IndexTracker::acquire( ctx, "G1", 2 );
REQUIRE( g1b.isOpen() == true );
REQUIRE( g1b.index() == 1 );
REQUIRE( s1.isSuccessfullyCompleted() == false );
s1b.close();
REQUIRE( s1b.isSuccessfullyCompleted() == true );
REQUIRE( g1b.isSuccessfullyCompleted() == true );
testCase2.close();
REQUIRE( testCase2.isSuccessfullyCompleted() == true );
}
}
SECTION( "Start a new inner section" ) {
IPartTracker& s2 = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2.isOpen() == true );
s2.close();
REQUIRE( s2.isSuccessfullyCompleted() == true );
s1.close();
REQUIRE( s1.isSuccessfullyCompleted() == false );
testCase.close();
REQUIRE( testCase.isSuccessfullyCompleted() == false );
SECTION( "Re-enter for second generation" ) {
ctx.startCycle();
IPartTracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" );
REQUIRE( testCase2.isSuccessfullyCompleted() == false );
IPartTracker& s1b = SectionTracker::acquire( ctx, "S1" );
REQUIRE( s1b.isSuccessfullyCompleted() == false );
// generator - next value
IndexTracker& g1b = IndexTracker::acquire( ctx, "G1", 2 );
REQUIRE( g1b.isOpen() == true );
REQUIRE( g1b.index() == 1 );
// inner section again
IPartTracker& s2b = SectionTracker::acquire( ctx, "S2" );
REQUIRE( s2b.isOpen() == true );
s2b.close();
REQUIRE( s2b.isSuccessfullyCompleted() == true );
s1b.close();
REQUIRE( s1b.isSuccessfullyCompleted() == true );
REQUIRE( g1b.isSuccessfullyCompleted() == true );
testCase2.close();
REQUIRE( testCase2.isSuccessfullyCompleted() == true );
}
}
// !TBD"
// nested generator
// two sections within a generator
}
} }