Tags beginning with a non alpha-numeric character are now disallowed.

Added !throws special tag which denotes a test case to be skipped when run with -e
(the idea being that the test case is expected to throw an exception which is not caught within a XXX_THROWS assertion).
This commit is contained in:
Phil Nash 2014-04-15 18:44:37 +01:00
parent c5406a25bf
commit 20cad7cb1d
18 changed files with 312 additions and 244 deletions

View File

@ -1,6 +1,6 @@
![catch logo](catch-logo-small.png) ![catch logo](catch-logo-small.png)
*v1.0 build 35 (master branch)* *v1.0 build 36 (master branch)*
Build status (on Travis CI) [![Build Status](https://travis-ci.org/philsquared/Catch.png)](https://travis-ci.org/philsquared/Catch) Build status (on Travis CI) [![Build Status](https://travis-ci.org/philsquared/Catch.png)](https://travis-ci.org/philsquared/Catch)

View File

@ -50,14 +50,16 @@ namespace Catch {
} }
return totals; return totals;
} }
Totals runTestsForGroup( RunContext& context, TestCaseFilters const& filterGroup ) {
Totals runTestsForGroup( RunContext& context, const TestCaseFilters& filterGroup ) {
Totals totals; Totals totals;
std::vector<TestCase>::const_iterator it = getRegistryHub().getTestCaseRegistry().getAllTests().begin();
std::vector<TestCase>::const_iterator itEnd = getRegistryHub().getTestCaseRegistry().getAllTests().end(); std::vector<TestCase> testCases;
getRegistryHub().getTestCaseRegistry().getFilteredTests( filterGroup, *m_config, testCases );
int testsRunForGroup = 0; int testsRunForGroup = 0;
for(; it != itEnd; ++it ) { for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
if( filterGroup.shouldInclude( *it ) ) { it != itEnd;
++it ) {
testsRunForGroup++; testsRunForGroup++;
if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
@ -68,11 +70,9 @@ namespace Catch {
m_testsAlreadyRun.insert( *it ); m_testsAlreadyRun.insert( *it );
} }
} }
}
if( testsRunForGroup == 0 && !filterGroup.getName().empty() ) if( testsRunForGroup == 0 && !filterGroup.getName().empty() )
m_reporter->noMatchingTestCases( filterGroup.getName() ); m_reporter->noMatchingTestCases( filterGroup.getName() );
return totals; return totals;
} }
private: private:

View File

@ -58,7 +58,7 @@ namespace Catch {
static void use( Code _colourCode ); static void use( Code _colourCode );
private: private:
static Detail::IColourImpl* impl; static Detail::IColourImpl* impl();
}; };
} // end namespace Catch } // end namespace Catch

View File

@ -73,7 +73,10 @@ namespace {
return true; return true;
} }
Win32ColourImpl platformColourImpl; static Detail::IColourImpl* platformColourInstance() {
static Win32ColourImpl s_instance;
return &s_instance;
}
} // end anon namespace } // end anon namespace
} // end namespace Catch } // end namespace Catch
@ -120,7 +123,10 @@ namespace {
return isatty(STDOUT_FILENO); return isatty(STDOUT_FILENO);
} }
PosixColourImpl platformColourImpl; static Detail::IColourImpl* platformColourInstance() {
static PosixColourImpl s_instance;
return &s_instance;
}
} // end anon namespace } // end anon namespace
} // end namespace Catch } // end namespace Catch
@ -132,21 +138,28 @@ namespace Catch {
namespace { namespace {
struct NoColourImpl : Detail::IColourImpl { struct NoColourImpl : Detail::IColourImpl {
void use( Colour::Code ) {} void use( Colour::Code ) {}
static IColourImpl* instance() {
static NoColourImpl s_instance;
return &s_instance;
}
}; };
NoColourImpl noColourImpl; static bool shouldUseColour() {
static const bool shouldUseColour = shouldUseColourForPlatform() && return shouldUseColourForPlatform() && !isDebuggerActive();
!isDebuggerActive(); }
} }
Colour::Colour( Code _colourCode ){ use( _colourCode ); } Colour::Colour( Code _colourCode ){ use( _colourCode ); }
Colour::~Colour(){ use( None ); } Colour::~Colour(){ use( None ); }
void Colour::use( Code _colourCode ) { void Colour::use( Code _colourCode ) {
impl->use( _colourCode ); impl()->use( _colourCode );
} }
Detail::IColourImpl* Colour::impl = shouldUseColour Detail::IColourImpl* Colour::impl() {
? static_cast<Detail::IColourImpl*>( &platformColourImpl ) return shouldUseColour()
: static_cast<Detail::IColourImpl*>( &noColourImpl ); ? platformColourInstance()
: NoColourImpl::instance();
}
} // end namespace Catch } // end namespace Catch

View File

@ -10,6 +10,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector>
#include "catch_ptr.hpp" #include "catch_ptr.hpp"
@ -32,6 +33,8 @@ namespace Catch {
Never Never
}; }; }; };
class TestCaseFilters;
struct IConfig : IShared { struct IConfig : IShared {
virtual ~IConfig(); virtual ~IConfig();
@ -44,6 +47,7 @@ namespace Catch {
virtual bool warnAboutMissingAssertions() const = 0; virtual bool warnAboutMissingAssertions() const = 0;
virtual int abortAfter() const = 0; virtual int abortAfter() const = 0;
virtual ShowDurations::OrNot showDurations() const = 0; virtual ShowDurations::OrNot showDurations() const = 0;
virtual std::vector<TestCaseFilters> const& filters() const = 0;
}; };
} }

View File

@ -23,11 +23,14 @@ namespace Catch {
}; };
class TestCase; class TestCase;
struct IConfig;
struct ITestCaseRegistry { struct ITestCaseRegistry {
virtual ~ITestCaseRegistry(); virtual ~ITestCaseRegistry();
virtual std::vector<TestCase> const& getAllTests() const = 0; virtual std::vector<TestCase> const& getAllTests() const = 0;
virtual std::vector<TestCase> getMatchingTestCases( std::string const& rawTestSpec ) const = 0; virtual void getFilteredTests( TestCaseFilters const& filters, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const = 0;
virtual void getFilteredTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) const = 0;
}; };
} }

View File

@ -17,14 +17,6 @@
#include <algorithm> #include <algorithm>
namespace Catch { namespace Catch {
inline bool matchesFilters( std::vector<TestCaseFilters> const& filters, TestCase const& testCase ) {
std::vector<TestCaseFilters>::const_iterator it = filters.begin();
std::vector<TestCaseFilters>::const_iterator itEnd = filters.end();
for(; it != itEnd; ++it )
if( !it->shouldInclude( testCase ) )
return false;
return true;
}
inline std::size_t listTests( Config const& config ) { inline std::size_t listTests( Config const& config ) {
if( config.filters().empty() ) if( config.filters().empty() )
@ -37,11 +29,11 @@ namespace Catch {
nameAttr.setInitialIndent( 2 ).setIndent( 4 ); nameAttr.setInitialIndent( 2 ).setIndent( 4 );
tagsAttr.setIndent( 6 ); tagsAttr.setIndent( 6 );
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); std::vector<TestCase> matchedTestCases;
for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end(); getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases );
for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
it != itEnd; it != itEnd;
++it ) ++it ) {
if( matchesFilters( config.filters(), *it ) ) {
matchedTests++; matchedTests++;
TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
Colour::Code colour = testCaseInfo.isHidden Colour::Code colour = testCaseInfo.isHidden
@ -63,11 +55,11 @@ namespace Catch {
inline std::size_t listTestsNamesOnly( Config const& config ) { inline std::size_t listTestsNamesOnly( Config const& config ) {
std::size_t matchedTests = 0; std::size_t matchedTests = 0;
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); std::vector<TestCase> matchedTestCases;
for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end(); getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases );
for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
it != itEnd; it != itEnd;
++it ) ++it ) {
if( matchesFilters( config.filters(), *it ) ) {
matchedTests++; matchedTests++;
TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
std::cout << testCaseInfo.name << std::endl; std::cout << testCaseInfo.name << std::endl;
@ -83,12 +75,11 @@ namespace Catch {
std::map<std::string, int> tagCounts; std::map<std::string, int> tagCounts;
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); std::vector<TestCase> matchedTestCases;
for( std::vector<TestCase>::const_iterator it = allTests.begin(), getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases );
itEnd = allTests.end(); for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
it != itEnd; it != itEnd;
++it ) { ++it ) {
if( matchesFilters( config.filters(), *it ) ) {
for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
tagItEnd = it->getTestCaseInfo().tags.end(); tagItEnd = it->getTestCaseInfo().tags.end();
tagIt != tagItEnd; tagIt != tagItEnd;
@ -101,7 +92,6 @@ namespace Catch {
countIt->second++; countIt->second++;
} }
} }
}
for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(), for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(),
countItEnd = tagCounts.end(); countItEnd = tagCounts.end();

View File

@ -88,23 +88,6 @@ namespace Catch {
m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
} }
Totals runMatching( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
std::vector<TestCase> matchingTests = getRegistryHub().getTestCaseRegistry().getMatchingTestCases( testSpec );
Totals totals;
testGroupStarting( testSpec, groupIndex, groupsCount );
std::vector<TestCase>::const_iterator it = matchingTests.begin();
std::vector<TestCase>::const_iterator itEnd = matchingTests.end();
for(; it != itEnd; ++it )
totals += runTest( *it );
testGroupEnded( testSpec, totals, groupIndex, groupsCount );
return totals;
}
Totals runTest( TestCase const& testCase ) { Totals runTest( TestCase const& testCase ) {
Totals prevTotals = m_totals; Totals prevTotals = m_totals;

View File

@ -40,6 +40,7 @@ namespace Catch {
std::string tagsAsString; std::string tagsAsString;
SourceLineInfo lineInfo; SourceLineInfo lineInfo;
bool isHidden; bool isHidden;
bool throws;
}; };
class TestCase : protected TestCaseInfo { class TestCase : protected TestCaseInfo {
@ -55,6 +56,7 @@ namespace Catch {
TestCaseInfo const& getTestCaseInfo() const; TestCaseInfo const& getTestCaseInfo() const;
bool isHidden() const; bool isHidden() const;
bool throws() const;
bool hasTag( std::string const& tag ) const; bool hasTag( std::string const& tag ) const;
bool matchesTags( std::string const& tagPattern ) const; bool matchesTags( std::string const& tagPattern ) const;
std::set<std::string> const& getTags() const; std::set<std::string> const& getTags() const;

View File

@ -15,6 +15,16 @@
namespace Catch { namespace Catch {
inline bool isSpecialTag( std::string const& tag ) {
return tag == "." ||
tag == "hide" ||
tag == "!hide" ||
tag == "!throws";
}
inline bool isReservedTag( std::string const& tag ) {
return !isSpecialTag( tag ) && tag.size() > 0 && !isalnum( tag[0] );
}
TestCase makeTestCase( ITestCase* _testCase, TestCase makeTestCase( ITestCase* _testCase,
std::string const& _className, std::string const& _className,
std::string const& _name, std::string const& _name,
@ -25,6 +35,23 @@ namespace Catch {
bool isHidden( startsWith( _name, "./" ) ); // Legacy support bool isHidden( startsWith( _name, "./" ) ); // Legacy support
std::set<std::string> tags; std::set<std::string> tags;
TagExtracter( tags ).parse( desc ); TagExtracter( tags ).parse( desc );
for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end();
it != itEnd;
++it )
if( isReservedTag( *it ) ) {
{
Colour colourGuard( Colour::Red );
std::cerr
<< "Tag name [" << *it << "] not allowed.\n"
<< "Tag names starting with non alpha-numeric characters are reserved\n";
}
{
Colour colourGuard( Colour::FileName );
std::cerr << _lineInfo << std::endl;
}
exit(1);
}
if( tags.find( "hide" ) != tags.end() || tags.find( "." ) != tags.end() ) if( tags.find( "hide" ) != tags.end() || tags.find( "." ) != tags.end() )
isHidden = true; isHidden = true;
@ -47,11 +74,15 @@ namespace Catch {
description( _description ), description( _description ),
tags( _tags ), tags( _tags ),
lineInfo( _lineInfo ), lineInfo( _lineInfo ),
isHidden( _isHidden ) isHidden( _isHidden ),
throws( false )
{ {
std::ostringstream oss; std::ostringstream oss;
for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
oss << "[" << *it << "]"; oss << "[" << *it << "]";
if( *it == "!throws" )
throws = true;
}
tagsAsString = oss.str(); tagsAsString = oss.str();
} }
@ -62,7 +93,8 @@ namespace Catch {
tags( other.tags ), tags( other.tags ),
tagsAsString( other.tagsAsString ), tagsAsString( other.tagsAsString ),
lineInfo( other.lineInfo ), lineInfo( other.lineInfo ),
isHidden( other.isHidden ) isHidden( other.isHidden ),
throws( other.throws )
{} {}
TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
@ -85,6 +117,9 @@ namespace Catch {
bool TestCase::isHidden() const { bool TestCase::isHidden() const {
return TestCaseInfo::isHidden; return TestCaseInfo::isHidden;
} }
bool TestCase::throws() const {
return TestCaseInfo::throws;
}
bool TestCase::hasTag( std::string const& tag ) const { bool TestCase::hasTag( std::string const& tag ) const {
return tags.find( toLower( tag ) ) != tags.end(); return tags.find( toLower( tag ) ) != tags.end();

View File

@ -41,9 +41,12 @@ namespace Catch {
} }
else { else {
TestCase const& prev = *m_functions.find( testCase ); TestCase const& prev = *m_functions.find( testCase );
{
Colour colourGuard( Colour::Red );
std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
<< "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n" << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
<< "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl; << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
}
exit(1); exit(1);
} }
} }
@ -56,32 +59,24 @@ namespace Catch {
return m_nonHiddenFunctions; return m_nonHiddenFunctions;
} }
// !TBD deprecated virtual void getFilteredTests( TestCaseFilters const& filters, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const {
virtual std::vector<TestCase> getMatchingTestCases( std::string const& rawTestSpec ) const { for( std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(),
std::vector<TestCase> matchingTests; itEnd = m_functionsInOrder.end();
getMatchingTestCases( rawTestSpec, matchingTests ); it != itEnd;
return matchingTests; ++it ) {
if( filters.shouldInclude( *it ) && ( config.allowThrows() || !it->throws() ) )
matchingTestCases.push_back( *it );
} }
}
virtual void getFilteredTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) const {
if( config.filters().empty() )
return getFilteredTests( TestCaseFilters( "empty" ), config, matchingTestCases );
// !TBD deprecated for( std::vector<TestCaseFilters>::const_iterator it = config.filters().begin(),
virtual void getMatchingTestCases( std::string const& rawTestSpec, std::vector<TestCase>& matchingTestsOut ) const { itEnd = config.filters().end();
TestCaseFilter filter( rawTestSpec ); it != itEnd;
++it )
std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(); getFilteredTests( *it, config, matchingTestCases );
std::vector<TestCase>::const_iterator itEnd = m_functionsInOrder.end();
for(; it != itEnd; ++it ) {
if( filter.shouldInclude( *it ) ) {
matchingTestsOut.push_back( *it );
}
}
}
virtual void getMatchingTestCases( TestCaseFilters const& filters, std::vector<TestCase>& matchingTestsOut ) const {
std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin();
std::vector<TestCase>::const_iterator itEnd = m_functionsInOrder.end();
// !TBD: replace with algorithm
for(; it != itEnd; ++it )
if( filters.shouldInclude( *it ) )
matchingTestsOut.push_back( *it );
} }
private: private:

View File

@ -13,7 +13,7 @@
namespace Catch { namespace Catch {
// These numbers are maintained by a script // These numbers are maintained by a script
Version libraryVersion( 1, 0, 35, "master" ); Version libraryVersion( 1, 0, 36, "master" );
} }
#endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED

View File

@ -774,5 +774,5 @@ with expansion:
"first" == "second" "first" == "second"
=============================================================================== ===============================================================================
124 test cases - 38 failed (660 assertions - 93 failed) 125 test cases - 38 failed (660 assertions - 93 failed)

View File

@ -3650,6 +3650,15 @@ MiscTests.cpp:<line number>: FAILED:
explicitly with message: explicitly with message:
to infinity and beyond to infinity and beyond
-------------------------------------------------------------------------------
not allowed
-------------------------------------------------------------------------------
MiscTests.cpp:<line number>
...............................................................................
No assertions in test case 'not allowed'
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Process can be configured on command line Process can be configured on command line
default - no arguments default - no arguments
@ -6982,5 +6991,5 @@ with expansion:
true true
=============================================================================== ===============================================================================
124 test cases - 53 failed (679 assertions - 112 failed) 125 test cases - 54 failed (680 assertions - 113 failed)

View File

@ -1,5 +1,5 @@
<testsuites> <testsuites>
<testsuite name="~_" errors="12" failures="100" tests="679" hostname="tbd" time="{duration}" timestamp="tbd"> <testsuite name="~_" errors="12" failures="101" tests="680" hostname="tbd" time="{duration}" timestamp="tbd">
<testcase classname="global" name="Some simple comparisons between doubles" time="{duration}"/> <testcase classname="global" name="Some simple comparisons between doubles" time="{duration}"/>
<testcase classname="global" name="Approximate comparisons with different epsilons" time="{duration}"/> <testcase classname="global" name="Approximate comparisons with different epsilons" time="{duration}"/>
<testcase classname="global" name="Approximate comparisons with floats" time="{duration}"/> <testcase classname="global" name="Approximate comparisons with floats" time="{duration}"/>

View File

@ -3723,6 +3723,9 @@
</Failure> </Failure>
<OverallResult success="false"/> <OverallResult success="false"/>
</TestCase> </TestCase>
<TestCase name="not allowed">
<OverallResult success="true"/>
</TestCase>
<TestCase name="Process can be configured on command line"> <TestCase name="Process can be configured on command line">
<Section name="default - no arguments"> <Section name="default - no arguments">
<Expression success="true" filename="/Users/philnash/Dev/OSS/Catch/projects/SelfTest/TestMain.cpp" > <Expression success="true" filename="/Users/philnash/Dev/OSS/Catch/projects/SelfTest/TestMain.cpp" >
@ -7242,7 +7245,7 @@ there&quot;
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<OverallResults successes="567" failures="112"/> <OverallResults successes="567" failures="113"/>
</Group> </Group>
<OverallResults successes="567" failures="112"/> <OverallResults successes="567" failures="113"/>
</Catch> </Catch>

View File

@ -333,3 +333,9 @@ TEST_CASE("A couple of nested sections followed by a failure", "[failing][.]")
FAIL("to infinity and beyond"); FAIL("to infinity and beyond");
} }
TEST_CASE("not allowed", "[!throws]")
{
// This test case should not be included if you run with -e on the command line
SUCCEED();
}

View File

@ -1,6 +1,6 @@
/* /*
* CATCH v1.0 build 35 (master branch) * CATCH v1.0 build 36 (master branch)
* Generated: 2014-04-12 19:20:39.856403 * Generated: 2014-04-15 18:42:33.686099
* ---------------------------------------------------------- * ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly * This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@ -420,11 +420,14 @@ namespace Catch {
}; };
class TestCase; class TestCase;
struct IConfig;
struct ITestCaseRegistry { struct ITestCaseRegistry {
virtual ~ITestCaseRegistry(); virtual ~ITestCaseRegistry();
virtual std::vector<TestCase> const& getAllTests() const = 0; virtual std::vector<TestCase> const& getAllTests() const = 0;
virtual std::vector<TestCase> getMatchingTestCases( std::string const& rawTestSpec ) const = 0; virtual void getFilteredTests( TestCaseFilters const& filters, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const = 0;
virtual void getFilteredTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) const = 0;
}; };
} }
@ -1497,6 +1500,7 @@ namespace Catch {
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector>
namespace Catch { namespace Catch {
@ -1517,6 +1521,8 @@ namespace Catch {
Never Never
}; }; }; };
class TestCaseFilters;
struct IConfig : IShared { struct IConfig : IShared {
virtual ~IConfig(); virtual ~IConfig();
@ -1529,6 +1535,7 @@ namespace Catch {
virtual bool warnAboutMissingAssertions() const = 0; virtual bool warnAboutMissingAssertions() const = 0;
virtual int abortAfter() const = 0; virtual int abortAfter() const = 0;
virtual ShowDurations::OrNot showDurations() const = 0; virtual ShowDurations::OrNot showDurations() const = 0;
virtual std::vector<TestCaseFilters> const& filters() const = 0;
}; };
} }
@ -2411,6 +2418,7 @@ namespace Catch {
std::string tagsAsString; std::string tagsAsString;
SourceLineInfo lineInfo; SourceLineInfo lineInfo;
bool isHidden; bool isHidden;
bool throws;
}; };
class TestCase : protected TestCaseInfo { class TestCase : protected TestCaseInfo {
@ -2426,6 +2434,7 @@ namespace Catch {
TestCaseInfo const& getTestCaseInfo() const; TestCaseInfo const& getTestCaseInfo() const;
bool isHidden() const; bool isHidden() const;
bool throws() const;
bool hasTag( std::string const& tag ) const; bool hasTag( std::string const& tag ) const;
bool matchesTags( std::string const& tagPattern ) const; bool matchesTags( std::string const& tagPattern ) const;
std::set<std::string> const& getTags() const; std::set<std::string> const& getTags() const;
@ -4221,7 +4230,7 @@ namespace Catch {
static void use( Code _colourCode ); static void use( Code _colourCode );
private: private:
static Detail::IColourImpl* impl; static Detail::IColourImpl* impl();
}; };
} // end namespace Catch } // end namespace Catch
@ -4505,14 +4514,6 @@ namespace Catch
#include <algorithm> #include <algorithm>
namespace Catch { namespace Catch {
inline bool matchesFilters( std::vector<TestCaseFilters> const& filters, TestCase const& testCase ) {
std::vector<TestCaseFilters>::const_iterator it = filters.begin();
std::vector<TestCaseFilters>::const_iterator itEnd = filters.end();
for(; it != itEnd; ++it )
if( !it->shouldInclude( testCase ) )
return false;
return true;
}
inline std::size_t listTests( Config const& config ) { inline std::size_t listTests( Config const& config ) {
if( config.filters().empty() ) if( config.filters().empty() )
@ -4525,11 +4526,11 @@ namespace Catch {
nameAttr.setInitialIndent( 2 ).setIndent( 4 ); nameAttr.setInitialIndent( 2 ).setIndent( 4 );
tagsAttr.setIndent( 6 ); tagsAttr.setIndent( 6 );
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); std::vector<TestCase> matchedTestCases;
for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end(); getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases );
for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
it != itEnd; it != itEnd;
++it ) ++it ) {
if( matchesFilters( config.filters(), *it ) ) {
matchedTests++; matchedTests++;
TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
Colour::Code colour = testCaseInfo.isHidden Colour::Code colour = testCaseInfo.isHidden
@ -4551,11 +4552,11 @@ namespace Catch {
inline std::size_t listTestsNamesOnly( Config const& config ) { inline std::size_t listTestsNamesOnly( Config const& config ) {
std::size_t matchedTests = 0; std::size_t matchedTests = 0;
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); std::vector<TestCase> matchedTestCases;
for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end(); getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases );
for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
it != itEnd; it != itEnd;
++it ) ++it ) {
if( matchesFilters( config.filters(), *it ) ) {
matchedTests++; matchedTests++;
TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
std::cout << testCaseInfo.name << std::endl; std::cout << testCaseInfo.name << std::endl;
@ -4571,12 +4572,11 @@ namespace Catch {
std::map<std::string, int> tagCounts; std::map<std::string, int> tagCounts;
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); std::vector<TestCase> matchedTestCases;
for( std::vector<TestCase>::const_iterator it = allTests.begin(), getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases );
itEnd = allTests.end(); for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
it != itEnd; it != itEnd;
++it ) { ++it ) {
if( matchesFilters( config.filters(), *it ) ) {
for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
tagItEnd = it->getTestCaseInfo().tags.end(); tagItEnd = it->getTestCaseInfo().tags.end();
tagIt != tagItEnd; tagIt != tagItEnd;
@ -4589,7 +4589,6 @@ namespace Catch {
countIt->second++; countIt->second++;
} }
} }
}
for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(), for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(),
countItEnd = tagCounts.end(); countItEnd = tagCounts.end();
@ -4856,23 +4855,6 @@ namespace Catch {
m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
} }
Totals runMatching( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
std::vector<TestCase> matchingTests = getRegistryHub().getTestCaseRegistry().getMatchingTestCases( testSpec );
Totals totals;
testGroupStarting( testSpec, groupIndex, groupsCount );
std::vector<TestCase>::const_iterator it = matchingTests.begin();
std::vector<TestCase>::const_iterator itEnd = matchingTests.end();
for(; it != itEnd; ++it )
totals += runTest( *it );
testGroupEnded( testSpec, totals, groupIndex, groupsCount );
return totals;
}
Totals runTest( TestCase const& testCase ) { Totals runTest( TestCase const& testCase ) {
Totals prevTotals = m_totals; Totals prevTotals = m_totals;
@ -5165,14 +5147,16 @@ namespace Catch {
} }
return totals; return totals;
} }
Totals runTestsForGroup( RunContext& context, TestCaseFilters const& filterGroup ) {
Totals runTestsForGroup( RunContext& context, const TestCaseFilters& filterGroup ) {
Totals totals; Totals totals;
std::vector<TestCase>::const_iterator it = getRegistryHub().getTestCaseRegistry().getAllTests().begin();
std::vector<TestCase>::const_iterator itEnd = getRegistryHub().getTestCaseRegistry().getAllTests().end(); std::vector<TestCase> testCases;
getRegistryHub().getTestCaseRegistry().getFilteredTests( filterGroup, *m_config, testCases );
int testsRunForGroup = 0; int testsRunForGroup = 0;
for(; it != itEnd; ++it ) { for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
if( filterGroup.shouldInclude( *it ) ) { it != itEnd;
++it ) {
testsRunForGroup++; testsRunForGroup++;
if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
@ -5183,11 +5167,9 @@ namespace Catch {
m_testsAlreadyRun.insert( *it ); m_testsAlreadyRun.insert( *it );
} }
} }
}
if( testsRunForGroup == 0 && !filterGroup.getName().empty() ) if( testsRunForGroup == 0 && !filterGroup.getName().empty() )
m_reporter->noMatchingTestCases( filterGroup.getName() ); m_reporter->noMatchingTestCases( filterGroup.getName() );
return totals; return totals;
} }
private: private:
@ -5370,9 +5352,12 @@ namespace Catch {
} }
else { else {
TestCase const& prev = *m_functions.find( testCase ); TestCase const& prev = *m_functions.find( testCase );
{
Colour colourGuard( Colour::Red );
std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
<< "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n" << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
<< "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl; << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
}
exit(1); exit(1);
} }
} }
@ -5385,32 +5370,24 @@ namespace Catch {
return m_nonHiddenFunctions; return m_nonHiddenFunctions;
} }
// !TBD deprecated virtual void getFilteredTests( TestCaseFilters const& filters, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const {
virtual std::vector<TestCase> getMatchingTestCases( std::string const& rawTestSpec ) const { for( std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(),
std::vector<TestCase> matchingTests; itEnd = m_functionsInOrder.end();
getMatchingTestCases( rawTestSpec, matchingTests ); it != itEnd;
return matchingTests; ++it ) {
if( filters.shouldInclude( *it ) && ( config.allowThrows() || !it->throws() ) )
matchingTestCases.push_back( *it );
} }
}
virtual void getFilteredTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) const {
if( config.filters().empty() )
return getFilteredTests( TestCaseFilters( "empty" ), config, matchingTestCases );
// !TBD deprecated for( std::vector<TestCaseFilters>::const_iterator it = config.filters().begin(),
virtual void getMatchingTestCases( std::string const& rawTestSpec, std::vector<TestCase>& matchingTestsOut ) const { itEnd = config.filters().end();
TestCaseFilter filter( rawTestSpec ); it != itEnd;
++it )
std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(); getFilteredTests( *it, config, matchingTestCases );
std::vector<TestCase>::const_iterator itEnd = m_functionsInOrder.end();
for(; it != itEnd; ++it ) {
if( filter.shouldInclude( *it ) ) {
matchingTestsOut.push_back( *it );
}
}
}
virtual void getMatchingTestCases( TestCaseFilters const& filters, std::vector<TestCase>& matchingTestsOut ) const {
std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin();
std::vector<TestCase>::const_iterator itEnd = m_functionsInOrder.end();
// !TBD: replace with algorithm
for(; it != itEnd; ++it )
if( filters.shouldInclude( *it ) )
matchingTestsOut.push_back( *it );
} }
private: private:
@ -5909,7 +5886,10 @@ namespace {
return true; return true;
} }
Win32ColourImpl platformColourImpl; static Detail::IColourImpl* platformColourInstance() {
static Win32ColourImpl s_instance;
return &s_instance;
}
} // end anon namespace } // end anon namespace
} // end namespace Catch } // end namespace Catch
@ -5956,7 +5936,10 @@ namespace {
return isatty(STDOUT_FILENO); return isatty(STDOUT_FILENO);
} }
PosixColourImpl platformColourImpl; static Detail::IColourImpl* platformColourInstance() {
static PosixColourImpl s_instance;
return &s_instance;
}
} // end anon namespace } // end anon namespace
} // end namespace Catch } // end namespace Catch
@ -5968,21 +5951,28 @@ namespace Catch {
namespace { namespace {
struct NoColourImpl : Detail::IColourImpl { struct NoColourImpl : Detail::IColourImpl {
void use( Colour::Code ) {} void use( Colour::Code ) {}
static IColourImpl* instance() {
static NoColourImpl s_instance;
return &s_instance;
}
}; };
NoColourImpl noColourImpl; static bool shouldUseColour() {
static const bool shouldUseColour = shouldUseColourForPlatform() && return shouldUseColourForPlatform() && !isDebuggerActive();
!isDebuggerActive(); }
} }
Colour::Colour( Code _colourCode ){ use( _colourCode ); } Colour::Colour( Code _colourCode ){ use( _colourCode ); }
Colour::~Colour(){ use( None ); } Colour::~Colour(){ use( None ); }
void Colour::use( Code _colourCode ) { void Colour::use( Code _colourCode ) {
impl->use( _colourCode ); impl()->use( _colourCode );
} }
Detail::IColourImpl* Colour::impl = shouldUseColour Detail::IColourImpl* Colour::impl() {
? static_cast<Detail::IColourImpl*>( &platformColourImpl ) return shouldUseColour()
: static_cast<Detail::IColourImpl*>( &noColourImpl ); ? platformColourInstance()
: NoColourImpl::instance();
}
} // end namespace Catch } // end namespace Catch
@ -6233,6 +6223,16 @@ namespace Catch {
namespace Catch { namespace Catch {
inline bool isSpecialTag( std::string const& tag ) {
return tag == "." ||
tag == "hide" ||
tag == "!hide" ||
tag == "!throws";
}
inline bool isReservedTag( std::string const& tag ) {
return !isSpecialTag( tag ) && tag.size() > 0 && !isalnum( tag[0] );
}
TestCase makeTestCase( ITestCase* _testCase, TestCase makeTestCase( ITestCase* _testCase,
std::string const& _className, std::string const& _className,
std::string const& _name, std::string const& _name,
@ -6243,6 +6243,23 @@ namespace Catch {
bool isHidden( startsWith( _name, "./" ) ); // Legacy support bool isHidden( startsWith( _name, "./" ) ); // Legacy support
std::set<std::string> tags; std::set<std::string> tags;
TagExtracter( tags ).parse( desc ); TagExtracter( tags ).parse( desc );
for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end();
it != itEnd;
++it )
if( isReservedTag( *it ) ) {
{
Colour colourGuard( Colour::Red );
std::cerr
<< "Tag name [" << *it << "] not allowed.\n"
<< "Tag names starting with non alpha-numeric characters are reserved\n";
}
{
Colour colourGuard( Colour::FileName );
std::cerr << _lineInfo << std::endl;
}
exit(1);
}
if( tags.find( "hide" ) != tags.end() || tags.find( "." ) != tags.end() ) if( tags.find( "hide" ) != tags.end() || tags.find( "." ) != tags.end() )
isHidden = true; isHidden = true;
@ -6265,11 +6282,15 @@ namespace Catch {
description( _description ), description( _description ),
tags( _tags ), tags( _tags ),
lineInfo( _lineInfo ), lineInfo( _lineInfo ),
isHidden( _isHidden ) isHidden( _isHidden ),
throws( false )
{ {
std::ostringstream oss; std::ostringstream oss;
for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
oss << "[" << *it << "]"; oss << "[" << *it << "]";
if( *it == "!throws" )
throws = true;
}
tagsAsString = oss.str(); tagsAsString = oss.str();
} }
@ -6280,7 +6301,8 @@ namespace Catch {
tags( other.tags ), tags( other.tags ),
tagsAsString( other.tagsAsString ), tagsAsString( other.tagsAsString ),
lineInfo( other.lineInfo ), lineInfo( other.lineInfo ),
isHidden( other.isHidden ) isHidden( other.isHidden ),
throws( other.throws )
{} {}
TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
@ -6303,6 +6325,9 @@ namespace Catch {
bool TestCase::isHidden() const { bool TestCase::isHidden() const {
return TestCaseInfo::isHidden; return TestCaseInfo::isHidden;
} }
bool TestCase::throws() const {
return TestCaseInfo::throws;
}
bool TestCase::hasTag( std::string const& tag ) const { bool TestCase::hasTag( std::string const& tag ) const {
return tags.find( toLower( tag ) ) != tags.end(); return tags.find( toLower( tag ) ) != tags.end();
@ -6598,7 +6623,7 @@ namespace Catch {
namespace Catch { namespace Catch {
// These numbers are maintained by a script // These numbers are maintained by a script
Version libraryVersion( 1, 0, 35, "master" ); Version libraryVersion( 1, 0, 36, "master" );
} }
// #included from: catch_message.hpp // #included from: catch_message.hpp