Reworked SectionTracker and moved into own header

This commit is contained in:
Phil Nash 2013-07-23 18:48:36 +01:00
parent 372a6c6fed
commit 6a484fdb02
3 changed files with 200 additions and 161 deletions

View File

@ -0,0 +1,145 @@
/*
* Created by Phil Nash on 23/7/2013
* Copyright 2013 Two Blue Cubes Ltd. All rights reserved.
*
* 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)
*/
#ifndef TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
#include <map>
#include <string>
#include <assert.h>
namespace Catch {
namespace SectionTracking {
class TrackedSection {
typedef std::map<std::string, TrackedSection> TrackedSections;
public:
enum RunState {
NotStarted,
Executing,
ExecutingChildren,
Completed
};
TrackedSection( std::string const& name, TrackedSection* parent )
: m_name( name ), m_runState( NotStarted ), m_parent( parent )
{}
RunState runState() const { return m_runState; }
void addChild( std::string const& childName ) {
m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
}
TrackedSection* getChild( std::string const& childName ) {
return &m_children.find( childName )->second;
}
void enter() {
if( m_runState == NotStarted )
m_runState = Executing;
}
void 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;
}
m_runState = Completed;
}
TrackedSection* getParent() {
return m_parent;
}
private:
std::string m_name;
RunState m_runState;
TrackedSections m_children;
TrackedSection* m_parent;
};
class SectionTracker {
public:
SectionTracker( TrackedSection& testCase )
: m_currentSection( &testCase ),
m_completedASectionThisRun( false )
{}
bool enterSection( std::string const& name ) {
if( m_completedASectionThisRun )
return false;
if( m_currentSection->runState() == TrackedSection::Executing ) {
m_currentSection->addChild( name );
return false;
}
else {
TrackedSection* child = m_currentSection->getChild( name );
if( child->runState() != TrackedSection::Completed ) {
m_currentSection = child;
m_currentSection->enter();
return true;
}
return false;
}
}
void leaveSection() {
m_currentSection->leave();
m_currentSection = m_currentSection->getParent();
assert( m_currentSection != NULL );
m_completedASectionThisRun = true;
}
private:
TrackedSection* m_currentSection;
bool m_completedASectionThisRun;
};
class TestCaseTracker {
public:
TestCaseTracker( std::string const& testCaseName )
: m_testCase( testCaseName, NULL ),
sections( m_testCase )
{}
void enter() {
sections = SectionTracker( m_testCase );
m_testCase.enter();
}
void leave() {
m_testCase.leave();
}
bool enterSection( std::string const& name ) {
return sections.enterSection( name );
}
void leaveSection() {
sections.leaveSection();
}
bool isCompleted() const {
return m_testCase.runState() == TrackedSection::Completed;
}
private:
TrackedSection m_testCase;
SectionTracker sections;
};
} // namespace SectionTracking
using SectionTracking::TestCaseTracker;
} // namespace Catch
#endif // TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED

View File

@ -10,256 +10,148 @@
#pragma clang diagnostic ignored "-Wpadded" #pragma clang diagnostic ignored "-Wpadded"
#endif #endif
#include "internal/catch_test_case_tracker.hpp"
#include "catch.hpp" #include "catch.hpp"
using namespace Catch; TEST_CASE( "section tracking" ) {
class TrackedSection;
typedef std::map<std::string, TrackedSection> TrackedSections;
class TrackedSection {
public:
enum RunState {
NotStarted,
Executing,
ExecutingChildren,
Completed
};
TrackedSection( std::string const& name, TrackedSection* parent )
: m_name( name ), m_runState( NotStarted ), m_parent( parent )
{}
RunState runState() const { return m_runState; }
void addChild( std::string const& childName ) {
m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
}
TrackedSection* getChild( std::string const& childName ) {
return &m_children.find( childName )->second;
}
void enter() {
if( m_runState == NotStarted )
m_runState = Executing;
}
void 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;
}
m_runState = Completed;
}
TrackedSection* getParent() {
return m_parent;
}
private:
std::string m_name;
RunState m_runState;
TrackedSections m_children;
TrackedSection* m_parent;
};
class SectionTracker {
public:
SectionTracker( std::string const& testCaseName )
: m_testCase( testCaseName, NULL ),
m_currentSection( NULL ),
m_completedASectionThisRun( false )
{}
void enter() {
m_completedASectionThisRun = false;
m_currentSection = &m_testCase;
m_testCase.enter();
}
void leave() {
m_testCase.leave();
}
bool enterSection( std::string const& name ) {
if( m_completedASectionThisRun )
return false;
if( m_currentSection->runState() == TrackedSection::Executing ) {
m_currentSection->addChild( name );
return false;
}
else {
TrackedSection* child = m_currentSection->getChild( name );
if( child->runState() != TrackedSection::Completed ) {
m_currentSection = child;
m_currentSection->enter();
return true;
}
return false;
}
}
void leaveSection() {
m_currentSection->leave();
m_currentSection = m_currentSection->getParent();
assert( m_currentSection != NULL );
m_completedASectionThisRun = true;
}
bool isCompleted() const {
return m_testCase.runState() == TrackedSection::Completed;
}
bool hasCompletedASectionThisRun() const {
return m_completedASectionThisRun;
}
private:
TrackedSection m_testCase;
TrackedSection* m_currentSection;
bool m_completedASectionThisRun;
};
TEST_CASE( "sections" ) {
using namespace Catch; using namespace Catch;
SectionTracker sectionTracker( "test case" ); TestCaseTracker testCaseTracker( "test case" );
const std::string section1Name = "section 1"; const std::string section1Name = "section 1";
const std::string section2Name = "section 2"; const std::string section2Name = "section 2";
CHECK_FALSE( sectionTracker.isCompleted() ); CHECK_FALSE( testCaseTracker.isCompleted() );
SECTION( "test case with no sections" ) { SECTION( "test case with no sections" ) {
sectionTracker.enter(); testCaseTracker.enter();
CHECK_FALSE( sectionTracker.isCompleted() ); CHECK_FALSE( testCaseTracker.isCompleted() );
sectionTracker.leave(); testCaseTracker.leave();
CHECK( sectionTracker.isCompleted() ); CHECK( testCaseTracker.isCompleted() );
} }
SECTION( "test case with one section" ) { SECTION( "test case with one section" ) {
// Enter test case // Enter test case
sectionTracker.enter(); testCaseTracker.enter();
// Enter section? - no, not yet // Enter section? - no, not yet
CHECK_FALSE( sectionTracker.enterSection( section1Name ) ); CHECK_FALSE( testCaseTracker.enterSection( section1Name ) );
CHECK_FALSE( sectionTracker.isCompleted() ); CHECK_FALSE( testCaseTracker.isCompleted() );
// Leave test case - incomplete (still need to visit section) // Leave test case - incomplete (still need to visit section)
sectionTracker.leave(); testCaseTracker.leave();
CHECK_FALSE( sectionTracker.isCompleted() ); CHECK_FALSE( testCaseTracker.isCompleted() );
// ... // ...
// Enter test case again // Enter test case again
sectionTracker.enter(); testCaseTracker.enter();
// Enter section? - yes // Enter section? - yes
CHECK( sectionTracker.enterSection( section1Name ) ); CHECK( testCaseTracker.enterSection( section1Name ) );
// Leave section and test case - now complete // Leave section and test case - now complete
sectionTracker.leaveSection(); testCaseTracker.leaveSection();
sectionTracker.leave(); testCaseTracker.leave();
CHECK( sectionTracker.isCompleted() ); CHECK( testCaseTracker.isCompleted() );
} }
SECTION( "test case with two consecutive sections" ) { SECTION( "test case with two consecutive sections" ) {
// Enter test case // Enter test case
sectionTracker.enter(); testCaseTracker.enter();
// Enter section 1? - no, not yet // Enter section 1? - no, not yet
CHECK_FALSE( sectionTracker.enterSection( section1Name ) ); CHECK_FALSE( testCaseTracker.enterSection( section1Name ) );
// Enter section 2? - no, not yet // Enter section 2? - no, not yet
CHECK_FALSE( sectionTracker.enterSection( section2Name ) ); CHECK_FALSE( testCaseTracker.enterSection( section2Name ) );
// Leave test case - incomplete (still need to visit sections) // Leave test case - incomplete (still need to visit sections)
sectionTracker.leave(); testCaseTracker.leave();
CHECK_FALSE( sectionTracker.isCompleted() ); CHECK_FALSE( testCaseTracker.isCompleted() );
// ... // ...
// Enter test case again // Enter test case again
sectionTracker.enter(); testCaseTracker.enter();
// Enter section 1? - yes // Enter section 1? - yes
CHECK( sectionTracker.enterSection( section1Name ) ); CHECK( testCaseTracker.enterSection( section1Name ) );
sectionTracker.leaveSection(); testCaseTracker.leaveSection();
// Enter section 2? - no, not yet // Enter section 2? - no, not yet
CHECK_FALSE( sectionTracker.enterSection( section2Name ) ); CHECK_FALSE( testCaseTracker.enterSection( section2Name ) );
// Leave test case - incomplete (still need to visit section 2) // Leave test case - incomplete (still need to visit section 2)
sectionTracker.leave(); testCaseTracker.leave();
CHECK_FALSE( sectionTracker.isCompleted() ); CHECK_FALSE( testCaseTracker.isCompleted() );
// ... // ...
// Enter test case again // Enter test case again
sectionTracker.enter(); testCaseTracker.enter();
// Enter section 1? - no, already done now // Enter section 1? - no, already done now
CHECK_FALSE( sectionTracker.enterSection( section1Name ) ); CHECK_FALSE( testCaseTracker.enterSection( section1Name ) );
// Enter section 2? - yes - finally // Enter section 2? - yes - finally
CHECK( sectionTracker.enterSection( section2Name ) ); CHECK( testCaseTracker.enterSection( section2Name ) );
sectionTracker.leaveSection(); testCaseTracker.leaveSection();
// Leave test case - now complete // Leave test case - now complete
sectionTracker.leave(); testCaseTracker.leave();
CHECK( sectionTracker.isCompleted() ); CHECK( testCaseTracker.isCompleted() );
} }
SECTION( "test case with one section within another" ) { SECTION( "test case with one section within another" ) {
// Enter test case // Enter test case
sectionTracker.enter(); testCaseTracker.enter();
// Enter section 1? - no, not yet // Enter section 1? - no, not yet
CHECK_FALSE( sectionTracker.enterSection( section1Name ) ); CHECK_FALSE( testCaseTracker.enterSection( section1Name ) );
// Leave test case - incomplete (still need to visit sections) // Leave test case - incomplete (still need to visit sections)
sectionTracker.leave(); testCaseTracker.leave();
CHECK_FALSE( sectionTracker.isCompleted() ); CHECK_FALSE( testCaseTracker.isCompleted() );
// ... // ...
// Enter test case again // Enter test case again
sectionTracker.enter(); testCaseTracker.enter();
// Enter section 1? - yes // Enter section 1? - yes
CHECK( sectionTracker.enterSection( section1Name ) ); CHECK( testCaseTracker.enterSection( section1Name ) );
// Enter section 2? - no, not yet // Enter section 2? - no, not yet
CHECK_FALSE( sectionTracker.enterSection( section2Name ) ); CHECK_FALSE( testCaseTracker.enterSection( section2Name ) );
sectionTracker.leaveSection(); // section 1 - incomplete (section 2) testCaseTracker.leaveSection(); // section 1 - incomplete (section 2)
// Leave test case - incomplete // Leave test case - incomplete
sectionTracker.leave(); testCaseTracker.leave();
CHECK_FALSE( sectionTracker.isCompleted() ); CHECK_FALSE( testCaseTracker.isCompleted() );
// ... // ...
// Enter test case again // Enter test case again
sectionTracker.enter(); testCaseTracker.enter();
// Enter section 1? - yes - so we can execute section 2 // Enter section 1? - yes - so we can execute section 2
CHECK( sectionTracker.enterSection( section1Name ) ); CHECK( testCaseTracker.enterSection( section1Name ) );
// Enter section 2? - yes - finally // Enter section 2? - yes - finally
CHECK( sectionTracker.enterSection( section2Name ) ); CHECK( testCaseTracker.enterSection( section2Name ) );
sectionTracker.leaveSection(); // section 2 testCaseTracker.leaveSection(); // section 2
sectionTracker.leaveSection(); // section 1 testCaseTracker.leaveSection(); // section 1
// Leave test case - now complete // Leave test case - now complete
sectionTracker.leave(); testCaseTracker.leave();
CHECK( sectionTracker.isCompleted() ); CHECK( testCaseTracker.isCompleted() );
} }
} }

View File

@ -69,6 +69,7 @@
26847E5C16BBACB60043B9C1 /* catch_message.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_message.hpp; sourceTree = "<group>"; }; 26847E5C16BBACB60043B9C1 /* catch_message.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_message.hpp; sourceTree = "<group>"; };
26847E5D16BBADB40043B9C1 /* catch_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_message.cpp; path = ../../../SelfTest/SurrogateCpps/catch_message.cpp; sourceTree = "<group>"; }; 26847E5D16BBADB40043B9C1 /* catch_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_message.cpp; path = ../../../SelfTest/SurrogateCpps/catch_message.cpp; sourceTree = "<group>"; };
26948284179A9AB900ED166E /* SectionTrackerTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SectionTrackerTests.cpp; path = ../../../SelfTest/SectionTrackerTests.cpp; 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>"; };
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>"; };
26C5F3EC17514B970056FB3C /* clara.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = clara.h; path = ../../../../include/internal/clara.h; sourceTree = "<group>"; }; 26C5F3EC17514B970056FB3C /* clara.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = clara.h; path = ../../../../include/internal/clara.h; sourceTree = "<group>"; };
26DACF2F17206D3400A21326 /* catch_text.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_text.h; sourceTree = "<group>"; }; 26DACF2F17206D3400A21326 /* catch_text.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_text.h; sourceTree = "<group>"; };
@ -378,6 +379,7 @@
4AB77CB81553BB3800857BF0 /* catch_running_test.hpp */, 4AB77CB81553BB3800857BF0 /* catch_running_test.hpp */,
4A084F1D15DAD15F0027E631 /* catch_test_spec.h */, 4A084F1D15DAD15F0027E631 /* catch_test_spec.h */,
4A8E4DCC160A344100194CBD /* catch_tags.hpp */, 4A8E4DCC160A344100194CBD /* catch_tags.hpp */,
26948287179EF7F900ED166E /* catch_test_case_tracker.hpp */,
); );
name = "Test execution"; name = "Test execution";
sourceTree = "<group>"; sourceTree = "<group>";