diff --git a/include/internal/catch_commandline.hpp b/include/internal/catch_commandline.hpp index 89eced34..adee52bb 100644 --- a/include/internal/catch_commandline.hpp +++ b/include/internal/catch_commandline.hpp @@ -23,6 +23,7 @@ namespace Catch { config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addWarning( ConfigData& config, std::string const& _warning ) { @@ -176,6 +177,10 @@ namespace Catch { .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); + cli["-c"]["--section"] + .describe( "specify section to run" ) + .bind( &addSectionToRun, "section name" ); + // Less common commands which don't have a short form cli["--list-test-names-only"] .describe( "list all/matching test cases names only" ) diff --git a/include/internal/catch_config.hpp b/include/internal/catch_config.hpp index 72b0f6c7..7d7887aa 100644 --- a/include/internal/catch_config.hpp +++ b/include/internal/catch_config.hpp @@ -74,6 +74,7 @@ namespace Catch { std::vector reporterNames; std::vector testsOrTags; + std::vector sectionsToRun; }; @@ -115,7 +116,8 @@ namespace Catch { bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - std::vector getReporterNames() const { return m_data.reporterNames; } + std::vector const& getReporterNames() const { return m_data.reporterNames; } + std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } int abortAfter() const { return m_data.abortAfter; } diff --git a/include/internal/catch_interfaces_config.h b/include/internal/catch_interfaces_config.h index 17914b4e..768550b9 100644 --- a/include/internal/catch_interfaces_config.h +++ b/include/internal/catch_interfaces_config.h @@ -62,6 +62,8 @@ namespace Catch { virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + }; } diff --git a/include/internal/catch_run_context.hpp b/include/internal/catch_run_context.hpp index d37bdba1..e458afca 100644 --- a/include/internal/catch_run_context.hpp +++ b/include/internal/catch_run_context.hpp @@ -97,10 +97,11 @@ namespace Catch { do { - m_trackerContext.startRun(); + ITracker& rootTracker = m_trackerContext.startRun(); + dynamic_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); do { m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); @@ -155,10 +156,7 @@ namespace Catch { Counts& assertions ) { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); diff --git a/include/internal/catch_test_case_tracker.hpp b/include/internal/catch_test_case_tracker.hpp index 3973e7b9..d2b14a2e 100644 --- a/include/internal/catch_test_case_tracker.hpp +++ b/include/internal/catch_test_case_tracker.hpp @@ -19,11 +19,21 @@ namespace Catch { namespace TestCaseTracking { + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + }; + struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries - virtual std::string name() const = 0; + virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed @@ -39,7 +49,7 @@ namespace TestCaseTracking { virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( std::string const& name ) = 0; + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking @@ -47,7 +57,7 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const = 0; }; - class TrackerContext { + class TrackerContext { enum RunState { NotStarted, @@ -110,30 +120,32 @@ namespace TestCaseTracking { Failed }; class TrackerHasName { - std::string m_name; + NameAndLocation m_nameAndLocation; public: - TrackerHasName( std::string const& name ) : m_name( name ) {} + TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool operator ()( Ptr const& tracker ) { - return tracker->name() == m_name; + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; } }; typedef std::vector > Children; - std::string m_name; + NameAndLocation m_nameAndLocation; 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 ), + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); - virtual std::string name() const CATCH_OVERRIDE { - return m_name; + virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { + return m_nameAndLocation; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; @@ -153,8 +165,8 @@ namespace TestCaseTracking { m_children.push_back( child ); } - virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; @@ -232,41 +244,65 @@ namespace TestCaseTracking { }; class SectionTracker : public TrackerBase { + std::vector m_filters; public: - SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( name, ctx, parent ) - {} + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } virtual ~SectionTracker(); virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } - static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); section = static_cast( childTracker ); } else { - section = new SectionTracker( name, ctx, ¤tTracker ); + section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( section ); } - if( !ctx.completedCycle() && !section->isComplete() ) { - - section->open(); - } + if( !ctx.completedCycle() ) + section->tryOpen(); return *section; } + + void tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + std::copy( filters.begin(), filters.end(), std::back_inserter( m_filters ) ); + } + } + void addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + std::copy( filters.begin()+1, filters.end(), std::back_inserter( m_filters ) ); + } }; 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 ), + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), m_size( size ), m_index( -1 ) {} @@ -274,17 +310,17 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } - static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isIndexTracker() ); tracker = static_cast( childTracker ); } else { - tracker = new IndexTracker( name, ctx, ¤tTracker, size ); + tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } @@ -312,7 +348,7 @@ namespace TestCaseTracking { }; inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); + m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; diff --git a/projects/SelfTest/PartTrackerTests.cpp b/projects/SelfTest/PartTrackerTests.cpp index 29256351..45f4cf1c 100644 --- a/projects/SelfTest/PartTrackerTests.cpp +++ b/projects/SelfTest/PartTrackerTests.cpp @@ -36,16 +36,21 @@ using namespace Catch; // REQUIRE( C_A_T_C_H_Context().i() == 42 ); //} +Catch::TestCaseTracking::NameAndLocation makeNAL( std::string const& name ) { + return Catch::TestCaseTracking::NameAndLocation( name, Catch::SourceLineInfo() ); +} + TEST_CASE( "Tracker", "" ) { TrackerContext ctx; ctx.startRun(); ctx.startCycle(); - ITracker& testCase = SectionTracker::acquire( ctx, "Testcase" ); + + ITracker& testCase = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase.isOpen() ); - ITracker& s1 = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1 = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1.isOpen() ); SECTION( "successfully close one section", "" ) { @@ -70,10 +75,10 @@ TEST_CASE( "Tracker", "" ) { SECTION( "re-enter after failed section", "" ) { ctx.startCycle(); - ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" ); + ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); - ITracker& s1b = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1b.isOpen() == false ); testCase2.close(); @@ -83,13 +88,13 @@ TEST_CASE( "Tracker", "" ) { } SECTION( "re-enter after failed section and find next section", "" ) { ctx.startCycle(); - ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" ); + ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); - ITracker& s1b = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1b.isOpen() == false ); - ITracker& s2 = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2.isOpen() ); s2.close(); @@ -104,7 +109,7 @@ TEST_CASE( "Tracker", "" ) { SECTION( "successfully close one section, then find another", "" ) { s1.close(); - ITracker& s2 = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2.isOpen() == false ); testCase.close(); @@ -112,13 +117,13 @@ TEST_CASE( "Tracker", "" ) { SECTION( "Re-enter - skips S1 and enters S2", "" ) { ctx.startCycle(); - ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" ); + ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); - ITracker& s1b = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1b.isOpen() == false ); - ITracker& s2b = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2b = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2b.isOpen() ); REQUIRE( ctx.completedCycle() == false ); @@ -145,13 +150,13 @@ TEST_CASE( "Tracker", "" ) { // Need a final cycle ctx.startCycle(); - ITracker& testCase3 = SectionTracker::acquire( ctx, "Testcase" ); + ITracker& testCase3 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase3.isOpen() ); - ITracker& s1c = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1c = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1c.isOpen() == false ); - ITracker& s2c = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2c = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2c.isOpen() == false ); testCase3.close(); @@ -161,7 +166,7 @@ TEST_CASE( "Tracker", "" ) { } SECTION( "open a nested section", "" ) { - ITracker& s2 = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2.isOpen() ); s2.close(); @@ -177,7 +182,7 @@ TEST_CASE( "Tracker", "" ) { } SECTION( "start a generator", "" ) { - IndexTracker& g1 = IndexTracker::acquire( ctx, "G1", 2 ); + IndexTracker& g1 = IndexTracker::acquire( ctx, makeNAL( "G1" ), 2 ); REQUIRE( g1.isOpen() ); REQUIRE( g1.index() == 0 ); @@ -193,14 +198,14 @@ TEST_CASE( "Tracker", "" ) { SECTION( "Re-enter for second generation", "" ) { ctx.startCycle(); - ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" ); + ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); - ITracker& s1b = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1b.isOpen() ); - IndexTracker& g1b = IndexTracker::acquire( ctx, "G1", 2 ); + IndexTracker& g1b = IndexTracker::acquire( ctx, makeNAL( "G1" ), 2 ); REQUIRE( g1b.isOpen() ); REQUIRE( g1b.index() == 1 ); @@ -214,7 +219,7 @@ TEST_CASE( "Tracker", "" ) { } } SECTION( "Start a new inner section", "" ) { - ITracker& s2 = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2.isOpen() ); s2.close(); @@ -228,19 +233,19 @@ TEST_CASE( "Tracker", "" ) { SECTION( "Re-enter for second generation", "" ) { ctx.startCycle(); - ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" ); + ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); - ITracker& s1b = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1b.isOpen() ); // generator - next value - IndexTracker& g1b = IndexTracker::acquire( ctx, "G1", 2 ); + IndexTracker& g1b = IndexTracker::acquire( ctx, makeNAL( "G1" ), 2 ); REQUIRE( g1b.isOpen() ); REQUIRE( g1b.index() == 1 ); // inner section again - ITracker& s2b = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2b = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2b.isOpen() ); s2b.close(); @@ -256,7 +261,7 @@ TEST_CASE( "Tracker", "" ) { } SECTION( "Fail an inner section", "" ) { - ITracker& s2 = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2 = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2.isOpen() ); s2.fail(); @@ -271,19 +276,19 @@ TEST_CASE( "Tracker", "" ) { SECTION( "Re-enter for second generation", "" ) { ctx.startCycle(); - ITracker& testCase2 = SectionTracker::acquire( ctx, "Testcase" ); + ITracker& testCase2 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase2.isOpen() ); - ITracker& s1b = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1b = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1b.isOpen() ); // generator - still same value - IndexTracker& g1b = IndexTracker::acquire( ctx, "G1", 2 ); + IndexTracker& g1b = IndexTracker::acquire( ctx, makeNAL( "G1" ), 2 ); REQUIRE( g1b.isOpen() ); REQUIRE( g1b.index() == 0 ); // inner section again - this time won't open - ITracker& s2b = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2b = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2b.isOpen() == false ); s1b.close(); @@ -295,19 +300,19 @@ TEST_CASE( "Tracker", "" ) { // Another cycle - now should complete ctx.startCycle(); - ITracker& testCase3 = SectionTracker::acquire( ctx, "Testcase" ); + ITracker& testCase3 = SectionTracker::acquire( ctx, makeNAL( "Testcase" ) ); REQUIRE( testCase3.isOpen() ); - ITracker& s1c = SectionTracker::acquire( ctx, "S1" ); + ITracker& s1c = SectionTracker::acquire( ctx, makeNAL( "S1" ) ); REQUIRE( s1c.isOpen() ); // generator - now next value - IndexTracker& g1c = IndexTracker::acquire( ctx, "G1", 2 ); + IndexTracker& g1c = IndexTracker::acquire( ctx, makeNAL( "G1" ), 2 ); REQUIRE( g1c.isOpen() ); REQUIRE( g1c.index() == 1 ); // inner section - now should open again - ITracker& s2c = SectionTracker::acquire( ctx, "S2" ); + ITracker& s2c = SectionTracker::acquire( ctx, makeNAL( "S2" ) ); REQUIRE( s2c.isOpen() ); s2c.close();