From 61964881c77b4fed7fdf9b5d4af45f1fc8846c8a Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Thu, 20 Sep 2012 08:17:52 +0100 Subject: [PATCH 01/10] Added copy ctor to ReporterConfig to get rid of warnings --- include/internal/catch_interfaces_reporter.h | 11 +++++++++++ include/internal/catch_tags.h | 14 ++++++++++++++ include/internal/catch_tags.hpp | 9 +++++++++ projects/SelfTest/SurrogateCpps/catch_tags.cpp | 9 +++++++++ projects/SelfTest/SurrogateCpps/catch_tags.h | 14 ++++++++++++++ 5 files changed, 57 insertions(+) create mode 100644 include/internal/catch_tags.h create mode 100644 include/internal/catch_tags.hpp create mode 100644 projects/SelfTest/SurrogateCpps/catch_tags.cpp create mode 100644 projects/SelfTest/SurrogateCpps/catch_tags.h diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h index da3cf79a..58b45a4a 100644 --- a/include/internal/catch_interfaces_reporter.h +++ b/include/internal/catch_interfaces_reporter.h @@ -31,10 +31,21 @@ namespace Catch fullConfig( _fullConfig ) {} + ReporterConfig( const ReporterConfig& other ) + : name( other.name ), + stream( other.stream ), + includeSuccessfulResults( other.includeSuccessfulResults ), + fullConfig( other.fullConfig ) + {} + + std::string name; std::ostream& stream; bool includeSuccessfulResults; ConfigData fullConfig; + + private: + void operator=(const ReporterConfig&); }; class TestCaseInfo; diff --git a/include/internal/catch_tags.h b/include/internal/catch_tags.h new file mode 100644 index 00000000..f07ff62c --- /dev/null +++ b/include/internal/catch_tags.h @@ -0,0 +1,14 @@ +// +// catch_tags.h +// CatchSelfTest +// +// Created by Phil Nash on 19/09/2012. +// +// + +#ifndef __CatchSelfTest__catch_tags__ +#define __CatchSelfTest__catch_tags__ + +#include + +#endif /* defined(__CatchSelfTest__catch_tags__) */ diff --git a/include/internal/catch_tags.hpp b/include/internal/catch_tags.hpp new file mode 100644 index 00000000..2a012d8b --- /dev/null +++ b/include/internal/catch_tags.hpp @@ -0,0 +1,9 @@ +// +// catch_tags.hpp +// CatchSelfTest +// +// Created by Phil Nash on 19/09/2012. +// +// + +#include "catch_tags.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_tags.cpp b/projects/SelfTest/SurrogateCpps/catch_tags.cpp new file mode 100644 index 00000000..12967052 --- /dev/null +++ b/projects/SelfTest/SurrogateCpps/catch_tags.cpp @@ -0,0 +1,9 @@ +// +// catch_tags.cpp +// CatchSelfTest +// +// Created by Phil Nash on 19/09/2012. +// +// + +#include "catch_tags.h" diff --git a/projects/SelfTest/SurrogateCpps/catch_tags.h b/projects/SelfTest/SurrogateCpps/catch_tags.h new file mode 100644 index 00000000..f07ff62c --- /dev/null +++ b/projects/SelfTest/SurrogateCpps/catch_tags.h @@ -0,0 +1,14 @@ +// +// catch_tags.h +// CatchSelfTest +// +// Created by Phil Nash on 19/09/2012. +// +// + +#ifndef __CatchSelfTest__catch_tags__ +#define __CatchSelfTest__catch_tags__ + +#include + +#endif /* defined(__CatchSelfTest__catch_tags__) */ From 9d8570ff80f70aba53fba3302ac7e232d49b7350 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Thu, 20 Sep 2012 08:20:06 +0100 Subject: [PATCH 02/10] Removed spurious files --- include/internal/catch_tags.h | 14 -------------- projects/SelfTest/SurrogateCpps/catch_tags.h | 14 -------------- 2 files changed, 28 deletions(-) delete mode 100644 include/internal/catch_tags.h delete mode 100644 projects/SelfTest/SurrogateCpps/catch_tags.h diff --git a/include/internal/catch_tags.h b/include/internal/catch_tags.h deleted file mode 100644 index f07ff62c..00000000 --- a/include/internal/catch_tags.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// catch_tags.h -// CatchSelfTest -// -// Created by Phil Nash on 19/09/2012. -// -// - -#ifndef __CatchSelfTest__catch_tags__ -#define __CatchSelfTest__catch_tags__ - -#include - -#endif /* defined(__CatchSelfTest__catch_tags__) */ diff --git a/projects/SelfTest/SurrogateCpps/catch_tags.h b/projects/SelfTest/SurrogateCpps/catch_tags.h deleted file mode 100644 index f07ff62c..00000000 --- a/projects/SelfTest/SurrogateCpps/catch_tags.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// catch_tags.h -// CatchSelfTest -// -// Created by Phil Nash on 19/09/2012. -// -// - -#ifndef __CatchSelfTest__catch_tags__ -#define __CatchSelfTest__catch_tags__ - -#include - -#endif /* defined(__CatchSelfTest__catch_tags__) */ From 1840929c51409724d5787029322f40307f714c60 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Thu, 20 Sep 2012 08:36:38 +0100 Subject: [PATCH 03/10] Regenerated header --- single_include/catch.hpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/single_include/catch.hpp b/single_include/catch.hpp index ba9a0797..3b0a1274 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1,5 +1,5 @@ /* - * Generated: 2012-09-15 17:50:31.695760 + * Generated: 2012-09-20 08:36:21.042619 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -2007,10 +2007,20 @@ namespace Catch fullConfig( _fullConfig ) {} + ReporterConfig( const ReporterConfig& other ) + : name( other.name ), + stream( other.stream ), + includeSuccessfulResults( other.includeSuccessfulResults ), + fullConfig( other.fullConfig ) + {} + std::string name; std::ostream& stream; bool includeSuccessfulResults; ConfigData fullConfig; + + private: + void operator=(const ReporterConfig&); }; class TestCaseInfo; From 85c0e3d42b060f521af14698ce53ada3c6753f8a Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Fri, 21 Sep 2012 07:48:03 +0100 Subject: [PATCH 04/10] Tag command line parsing implementation --- include/internal/catch_commandline.hpp | 46 +++- include/internal/catch_impl.hpp | 4 + include/internal/catch_tags.hpp | 204 +++++++++++++++++- include/internal/catch_test_case_info.h | 1 + include/internal/catch_test_case_info.hpp | 42 +--- .../SelfTest/SurrogateCpps/catch_tags.cpp | 10 +- projects/SelfTest/TestMain.cpp | 21 ++ .../CatchSelfTest.xcodeproj/project.pbxproj | 14 ++ 8 files changed, 288 insertions(+), 54 deletions(-) diff --git a/include/internal/catch_commandline.hpp b/include/internal/catch_commandline.hpp index 38c60855..d6467b93 100644 --- a/include/internal/catch_commandline.hpp +++ b/include/internal/catch_commandline.hpp @@ -235,7 +235,7 @@ namespace Catch { "\n" "If spec is prefixed with exclude: or the ~ character then the pattern matches an exclusion. " "This means that tests matching the pattern are excluded from the set - even if a prior " - "inclusion spec included them. Subsequent inclusion specs will take precendence, however. " + "inclusion spec included them. Subsequent inclusion specs will take precedence, however. " "Inclusions and exclusions are evaluated in left-to-right order.\n" "\n" "Examples:\n" @@ -249,7 +249,7 @@ namespace Catch { " -t a/* ~a/b/* a/b/c \tMatches all tests that start with 'a/', except those " "that start with 'a/b/', except 'a/b/c', which is included"; } - + virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { std::string groupName; for( std::size_t i = 0; i < cmd.argsCount(); ++i ) { @@ -264,6 +264,42 @@ namespace Catch { } }; + class TagOptionParser : public OptionParser { + public: + TagOptionParser() : OptionParser( 1, -1 ) { + m_optionNames.push_back( "-g" ); + m_optionNames.push_back( "--tag" ); + } + virtual std::string argsSynopsis() const { + return " [,...]"; + } + virtual std::string optionSummary() const { + return "Matches test cases against tags or tag patterns"; + } + + // Lines are split at the nearest prior space char to the 80 char column. + // Tab chars are removed from the output but their positions are used to align + // subsequently wrapped lines + virtual std::string optionDescription() const { + return + "!TBD"; + } + + virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { +// std::string groupName; +// for( std::size_t i = 0; i < cmd.argsCount(); ++i ) { +// if( i != 0 ) +// groupName += " "; +// groupName += cmd[i]; +// } +// TestCaseFilters filters( groupName ); +// for( std::size_t i = 0; i < cmd.argsCount(); ++i ) +// filters.addFilter( TestCaseFilter( cmd[i] ) ); +// config.filters.push_back( filters ); + } + }; + + class ListOptionParser : public OptionParser { public: ListOptionParser() : OptionParser( 0, 2 ) { @@ -369,9 +405,9 @@ namespace Catch { virtual std::string optionDescription() const { return "Use this option to send all output to a file or a stream. By default output is " - "sent to stdout (note that uses ofstdout and stderr from within test cases are " + "sent to stdout (note that uses of stdout and stderr from within test cases are " "redirected and included in the report - so even stderr will effectively end up " - "on stdout). If the name begins with % it is interpretted as a stream. " + "on stdout). If the name begins with % it is interpreted as a stream. " "Otherwise it is treated as a filename.\n" "\n" "Examples are:\n" @@ -407,7 +443,7 @@ namespace Catch { return "Usually you only want to see reporting for failed tests. Sometimes it's useful " "to see all the output (especially when you don't trust that that test you just " - "added worked first time!). To see successul, as well as failing, test results " + "added worked first time!). To see successful, as well as failing, test results " "just pass this option."; } virtual void parseIntoConfig( const Command&, ConfigData& config ) { diff --git a/include/internal/catch_impl.hpp b/include/internal/catch_impl.hpp index 9815f271..68b9ece4 100644 --- a/include/internal/catch_impl.hpp +++ b/include/internal/catch_impl.hpp @@ -23,6 +23,7 @@ #include "catch_resultinfo.hpp" #include "catch_resultinfo_builder.hpp" #include "catch_test_case_info.hpp" +#include "catch_tags.hpp" #include "../reporters/catch_reporter_basic.hpp" #include "../reporters/catch_reporter_xml.hpp" @@ -53,6 +54,9 @@ namespace Catch { FreeFunctionTestCase::~FreeFunctionTestCase() {} IGeneratorInfo::~IGeneratorInfo() {} IGeneratorsForTest::~IGeneratorsForTest() {} + TagParser::~TagParser() {} + TagExtracter::~TagExtracter() {} + TagExpressionParser::~TagExpressionParser() {} void Config::dummy() {} diff --git a/include/internal/catch_tags.hpp b/include/internal/catch_tags.hpp index 2a012d8b..d94eaad2 100644 --- a/include/internal/catch_tags.hpp +++ b/include/internal/catch_tags.hpp @@ -1,9 +1,197 @@ -// -// catch_tags.hpp -// CatchSelfTest -// -// Created by Phil Nash on 19/09/2012. -// -// +/* + * Created by Phil on 14/08/2012. + * Copyright 2012 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_TAGS_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_TAGS_HPP_INCLUDED -#include "catch_tags.h" +#include +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + class TagParser { + public: + virtual ~TagParser(); + + void parse( const std::string& str ) { + std::size_t pos = 0; + while( pos < str.size() ) { + char c = str[pos]; + if( c == '[' ) { + std::size_t end = str.find_first_of( ']', pos ); + if( end != std::string::npos ) { + acceptTag( str.substr( pos+1, end-pos-1 ) ); + pos = end+1; + } + else { + acceptChar( c ); + pos++; + } + } + else { + acceptChar( c ); + pos++; + } + } + endParse(); + } + + protected: + virtual void acceptTag( const std::string& tag ) = 0; + virtual void acceptChar( char c ) = 0; + virtual void endParse() {} + + private: + }; + + class TagExtracter : public TagParser { + public: + + TagExtracter( std::set& tags ) + : m_tags( tags ) + {} + virtual ~TagExtracter(); + + void parse( std::string& description ) { + TagParser::parse( description ); + description = m_remainder; + } + + private: + virtual void acceptTag( const std::string& tag ) { + m_tags.insert( tag ); + } + virtual void acceptChar( char c ) { + m_remainder += c; + } + + std::set& m_tags; + std::string m_remainder; + }; + + class Tag + { + public: + Tag() + : m_isNegated( false ) + {} + + Tag( const std::string& name, bool isNegated ) + : m_name( name ), + m_isNegated( isNegated ) + {} + + std::string getName() const { + return m_name; + } + bool isNegated() const { + return m_isNegated; + } + + bool operator ! () const { + return m_name.empty(); + } + + private: + std::string m_name; + bool m_isNegated; + }; + + class TagSet + { + typedef std::map TagMap; + public: + void add( const Tag& tag ) { + m_tags.insert( std::make_pair( tag.getName(), tag ) ); + } + + // needed? + Tag find( const std::string& name ) const { + TagMap::const_iterator it = m_tags.find( name ); + if( it == m_tags.end() ) + return Tag(); + else + return it->second; + } + bool empty() const { + return m_tags.empty(); + } + + bool matches( const std::set& tags ) const { + TagMap::const_iterator it = m_tags.begin(); + TagMap::const_iterator itEnd = m_tags.end(); + for(; it != itEnd; ++it ) { + bool found = tags.find( it->first ) != tags.end(); + if( found == it->second.isNegated() ) + return false; + } + return true; + } + + private: + TagMap m_tags; + }; + + class TagExpression { + public: + bool matches( const std::set& tags ) const { + std::vector::const_iterator it = m_tagSets.begin(); + std::vector::const_iterator itEnd = m_tagSets.end(); + for(; it != itEnd; ++it ) + if( it->matches( tags ) ) + return true; + return false; + } + + private: + friend class TagExpressionParser; + + std::vector m_tagSets; + }; + + class TagExpressionParser : public TagParser { + public: + TagExpressionParser( TagExpression& exp ) + : m_isNegated( false ), + m_exp( exp ) + {} + + ~TagExpressionParser(); + + private: + virtual void acceptTag( const std::string& tag ) { + m_currentTagSet.add( Tag( tag, m_isNegated ) ); + m_isNegated = false; + } + virtual void acceptChar( char c ) { + switch( c ) { + case '~': + m_isNegated = true; + break; + case ',': + m_exp.m_tagSets.push_back( m_currentTagSet ); + break; + } + } + virtual void endParse() { + if( !m_currentTagSet.empty() ) + m_exp.m_tagSets.push_back( m_currentTagSet ); + } + + bool m_isNegated; + TagSet m_currentTagSet; + TagExpression& m_exp; + }; + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_TAGS_HPP_INCLUDED diff --git a/include/internal/catch_test_case_info.h b/include/internal/catch_test_case_info.h index 1aebf0cf..d09673b6 100644 --- a/include/internal/catch_test_case_info.h +++ b/include/internal/catch_test_case_info.h @@ -36,6 +36,7 @@ namespace Catch { const SourceLineInfo& getLineInfo() const; bool isHidden() const; bool hasTag( const std::string& tag ) const; + bool matchesTags( const std::string& tagPattern ) const; const std::set& tags() const; void swap( TestCaseInfo& other ); diff --git a/include/internal/catch_test_case_info.hpp b/include/internal/catch_test_case_info.hpp index be4db842..90693a5d 100644 --- a/include/internal/catch_test_case_info.hpp +++ b/include/internal/catch_test_case_info.hpp @@ -8,41 +8,13 @@ #ifndef TWOBLUECUBES_CATCH_TESTCASEINFO_HPP_INCLUDED #define TWOBLUECUBES_CATCH_TESTCASEINFO_HPP_INCLUDED +#include "catch_tags.hpp" #include "catch_test_case_info.h" #include "catch_interfaces_testcase.h" namespace Catch { - namespace { - void extractTags( std::string& str, std::set& tags ) { - std::string remainder; - std::size_t last = 0; - std::size_t begin = str.find_first_of( '[' ); - while( begin != std::string::npos ) { - std::size_t end = str.find_first_of( ']', begin ); - if( end != std::string::npos ) { - std::string tag = str.substr( begin+1, end-begin-1 ); - tags.insert( tag ); - if( begin - last > 0 ) - remainder += str.substr( last, begin-last ); - last = end+1; - } - else if( begin != str.size()-1 ) { - end = begin+1; - } - else { - break; - } - begin = str.find_first_of( '[', end ); - } - if( !tags.empty() ) { - if( last < str.size() ) - str = remainder + str.substr( last ); - else - str = remainder; - } - } - } + TestCaseInfo::TestCaseInfo( ITestCase* testCase, const char* name, const char* description, @@ -53,7 +25,7 @@ namespace Catch { m_lineInfo( lineInfo ), m_isHidden( startsWith( name, "./" ) ) { - extractTags( m_description, m_tags ); + TagExtracter( m_tags ).parse( m_description ); if( hasTag( "hide" ) ) m_isHidden = true; } @@ -106,6 +78,11 @@ namespace Catch { bool TestCaseInfo::hasTag( const std::string& tag ) const { return m_tags.find( tag ) != m_tags.end(); } + bool TestCaseInfo::matchesTags( const std::string& tagPattern ) const { + TagExpression exp; + TagExpressionParser( exp ).parse( tagPattern ); + return exp.matches( m_tags ); + } const std::set& TestCaseInfo::tags() const { return m_tags; } @@ -129,6 +106,7 @@ namespace Catch { swap( temp ); return *this; } -} + +} // end namespace Catch #endif // TWOBLUECUBES_CATCH_TESTCASEINFO_HPP_INCLUDED diff --git a/projects/SelfTest/SurrogateCpps/catch_tags.cpp b/projects/SelfTest/SurrogateCpps/catch_tags.cpp index 12967052..47664eff 100644 --- a/projects/SelfTest/SurrogateCpps/catch_tags.cpp +++ b/projects/SelfTest/SurrogateCpps/catch_tags.cpp @@ -1,9 +1 @@ -// -// catch_tags.cpp -// CatchSelfTest -// -// Created by Phil Nash on 19/09/2012. -// -// - -#include "catch_tags.h" +#include "catch_tags.hpp" diff --git a/projects/SelfTest/TestMain.cpp b/projects/SelfTest/TestMain.cpp index f745f69b..2c0c2514 100644 --- a/projects/SelfTest/TestMain.cpp +++ b/projects/SelfTest/TestMain.cpp @@ -333,6 +333,12 @@ TEST_CASE( "selftest/option parsers", "" ) } TEST_CASE( "selftest/tags", "" ) { + + std::string p1 = "[one]"; + std::string p2 = "[one],[two]"; + std::string p3 = "[one][two]"; + std::string p4 = "[one][two],[three]"; + std::string p5 = "[one][two]~[hide],[three]"; SECTION( "one tag", "" ) { Catch::TestCaseInfo oneTag( NULL, "test", "[one]", CATCH_INTERNAL_LINEINFO ); @@ -340,6 +346,12 @@ TEST_CASE( "selftest/tags", "" ) { CHECK( oneTag.getDescription() == "" ); CHECK( oneTag.hasTag( "one" ) ); CHECK( oneTag.tags().size() == 1 ); + + CHECK( oneTag.matchesTags( p1 ) == true ); + CHECK( oneTag.matchesTags( p2 ) == true ); + CHECK( oneTag.matchesTags( p3 ) == false ); + CHECK( oneTag.matchesTags( p4 ) == false ); + CHECK( oneTag.matchesTags( p5 ) == false ); } SECTION( "two tags", "" ) { @@ -350,6 +362,12 @@ TEST_CASE( "selftest/tags", "" ) { CHECK( twoTags.hasTag( "two" ) ); CHECK( twoTags.hasTag( "three" ) == false ); CHECK( twoTags.tags().size() == 2 ); + + CHECK( twoTags.matchesTags( p1 ) == true ); + CHECK( twoTags.matchesTags( p2 ) == true ); + CHECK( twoTags.matchesTags( p3 ) == true ); + CHECK( twoTags.matchesTags( p4 ) == true ); + CHECK( twoTags.matchesTags( p5 ) == true ); } SECTION( "one tag with characters either side", "" ) { @@ -376,6 +394,9 @@ TEST_CASE( "selftest/tags", "" ) { CHECK( oneTag.getDescription() == "" ); CHECK( oneTag.hasTag( "hide" ) ); CHECK( oneTag.isHidden() ); + + CHECK( oneTag.matchesTags( "~[hide]" ) == false ); + } } diff --git a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj index 0e2795e9..c851d77f 100644 --- a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj +++ b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 4A6D0C3D149B3D9E00DB3EAA /* MiscTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A6D0C34149B3D9E00DB3EAA /* MiscTests.cpp */; }; 4A6D0C3E149B3D9E00DB3EAA /* TestMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A6D0C35149B3D9E00DB3EAA /* TestMain.cpp */; }; 4A6D0C3F149B3D9E00DB3EAA /* TrickyTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A6D0C36149B3D9E00DB3EAA /* TrickyTests.cpp */; }; + 4A8E4DD2160A352200194CBD /* catch_tags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A8E4DD0160A352200194CBD /* catch_tags.cpp */; }; 4AA7FF4315F3E89E009AD7F9 /* BDDTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AA7FF4115F3E89D009AD7F9 /* BDDTests.cpp */; }; 4AE1840B14EE4F230066340D /* catch_self_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AE1840A14EE4F230066340D /* catch_self_test.cpp */; }; /* End PBXBuildFile section */ @@ -93,6 +94,8 @@ 4A6D0C67149B3E3D00DB3EAA /* catch_reporter_junit.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_reporter_junit.hpp; sourceTree = ""; }; 4A6D0C68149B3E3D00DB3EAA /* catch_reporter_xml.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_reporter_xml.hpp; sourceTree = ""; }; 4A7ADB4314F631E10094FE10 /* catch_totals.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_totals.hpp; sourceTree = ""; }; + 4A8E4DCC160A344100194CBD /* catch_tags.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_tags.hpp; sourceTree = ""; }; + 4A8E4DD0160A352200194CBD /* catch_tags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_tags.cpp; path = ../../../SelfTest/SurrogateCpps/catch_tags.cpp; sourceTree = ""; }; 4A90B59B15D0F61A00EF71BC /* catch_interfaces_generators.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_interfaces_generators.h; sourceTree = ""; }; 4A90B59D15D24FE900EF71BC /* catch_resultinfo.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_resultinfo.hpp; sourceTree = ""; }; 4A90B59E15D2521E00EF71BC /* catch_resultinfo_builder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_resultinfo_builder.hpp; sourceTree = ""; }; @@ -170,6 +173,7 @@ 4A6D0C41149B3DE900DB3EAA /* Catch */ = { isa = PBXGroup; children = ( + 4A8E4DCF160A34E200194CBD /* SurrogateCpps */, 4A6D0C44149B3E1500DB3EAA /* catch.hpp */, 4A6D0C42149B3E1500DB3EAA /* catch_runner.hpp */, 4A6D0C43149B3E1500DB3EAA /* catch_with_main.hpp */, @@ -206,6 +210,14 @@ path = ../../../../include/reporters; sourceTree = ""; }; + 4A8E4DCF160A34E200194CBD /* SurrogateCpps */ = { + isa = PBXGroup; + children = ( + 4A8E4DD0160A352200194CBD /* catch_tags.cpp */, + ); + name = SurrogateCpps; + sourceTree = ""; + }; 4AC91CB4155B9EBF00DC5117 /* impl */ = { isa = PBXGroup; children = ( @@ -265,6 +277,7 @@ 4AB77CB71553B72B00857BF0 /* catch_section_info.hpp */, 4AB77CB81553BB3800857BF0 /* catch_running_test.hpp */, 4A084F1D15DAD15F0027E631 /* catch_test_spec.h */, + 4A8E4DCC160A344100194CBD /* catch_tags.hpp */, ); name = "Test execution"; sourceTree = ""; @@ -377,6 +390,7 @@ 4A6D0C3F149B3D9E00DB3EAA /* TrickyTests.cpp in Sources */, 4AE1840B14EE4F230066340D /* catch_self_test.cpp in Sources */, 4AA7FF4315F3E89E009AD7F9 /* BDDTests.cpp in Sources */, + 4A8E4DD2160A352200194CBD /* catch_tags.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 5f66d1d0017b89d7e4e77d9cb80c2acdae58438c Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Fri, 21 Sep 2012 18:44:22 +0100 Subject: [PATCH 05/10] Added cleanup as per #125 --- include/catch_runner.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/catch_runner.hpp b/include/catch_runner.hpp index c849ccb2..0f768773 100644 --- a/include/catch_runner.hpp +++ b/include/catch_runner.hpp @@ -130,6 +130,7 @@ namespace Catch { // Handle list request if( config.listSpec != List::None ) { List( config ); + Catch::cleanUp(); return 0; } From ec2fccf6b80b4dbbc0f18f77ec9fee556cca34b5 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Mon, 24 Sep 2012 08:28:23 +0100 Subject: [PATCH 06/10] Fixed SCOPED_INFO (#123) --- include/internal/catch_capture.hpp | 4 ++-- include/internal/catch_runner_impl.hpp | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/internal/catch_capture.hpp b/include/internal/catch_capture.hpp index c4f28757..379cc24c 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -35,8 +35,8 @@ public: return *this; } - std::string getInfo () const { - return m_oss.str(); + ResultInfo getInfo () const { + return ResultInfo( "", ResultWas::Info, false, SourceLineInfo(), "SCOPED_INFO", m_oss.str().c_str() ); } private: diff --git a/include/internal/catch_runner_impl.hpp b/include/internal/catch_runner_impl.hpp index 578d26dd..314182f4 100644 --- a/include/internal/catch_runner_impl.hpp +++ b/include/internal/catch_runner_impl.hpp @@ -156,10 +156,18 @@ namespace Catch { else if( !result.ok() ) { m_totals.assertions.failed++; - std::vector::const_iterator it = m_info.begin(); - std::vector::const_iterator itEnd = m_info.end(); - for(; it != itEnd; ++it ) - m_reporter->Result( *it ); + { + std::vector::const_iterator it = m_scopedInfos.begin(); + std::vector::const_iterator itEnd = m_scopedInfos.end(); + for(; it != itEnd; ++it ) + m_reporter->Result( (*it)->getInfo() ); + } + { + std::vector::const_iterator it = m_info.begin(); + std::vector::const_iterator itEnd = m_info.end(); + for(; it != itEnd; ++it ) + m_reporter->Result( *it ); + } m_info.clear(); } From 799ecf96040d8c4c79972d45ab23df078a9ace68 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Mon, 24 Sep 2012 08:30:13 +0100 Subject: [PATCH 07/10] Regenerated single include --- single_include/catch.hpp | 299 ++++++++++++++++++++++++++++++++++----- 1 file changed, 261 insertions(+), 38 deletions(-) diff --git a/single_include/catch.hpp b/single_include/catch.hpp index ba9a0797..656b3d89 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1,5 +1,5 @@ /* - * Generated: 2012-09-15 17:50:31.695760 + * Generated: 2012-09-24 08:29:47.663979 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -1311,8 +1311,8 @@ public: return *this; } - std::string getInfo () const { - return m_oss.str(); + ResultInfo getInfo () const { + return ResultInfo( "", ResultWas::Info, false, SourceLineInfo(), "SCOPED_INFO", m_oss.str().c_str() ); } private: @@ -1668,6 +1668,7 @@ namespace Catch { const SourceLineInfo& getLineInfo() const; bool isHidden() const; bool hasTag( const std::string& tag ) const; + bool matchesTags( const std::string& tagPattern ) const; const std::set& tags() const; void swap( TestCaseInfo& other ); @@ -2007,10 +2008,20 @@ namespace Catch fullConfig( _fullConfig ) {} + ReporterConfig( const ReporterConfig& other ) + : name( other.name ), + stream( other.stream ), + includeSuccessfulResults( other.includeSuccessfulResults ), + fullConfig( other.fullConfig ) + {} + std::string name; std::ostream& stream; bool includeSuccessfulResults; ConfigData fullConfig; + + private: + void operator=(const ReporterConfig&); }; class TestCaseInfo; @@ -2754,7 +2765,7 @@ namespace Catch { "\n" "If spec is prefixed with exclude: or the ~ character then the pattern matches an exclusion. " "This means that tests matching the pattern are excluded from the set - even if a prior " - "inclusion spec included them. Subsequent inclusion specs will take precendence, however. " + "inclusion spec included them. Subsequent inclusion specs will take precedence, however. " "Inclusions and exclusions are evaluated in left-to-right order.\n" "\n" "Examples:\n" @@ -2783,6 +2794,41 @@ namespace Catch { } }; + class TagOptionParser : public OptionParser { + public: + TagOptionParser() : OptionParser( 1, -1 ) { + m_optionNames.push_back( "-g" ); + m_optionNames.push_back( "--tag" ); + } + virtual std::string argsSynopsis() const { + return " [,...]"; + } + virtual std::string optionSummary() const { + return "Matches test cases against tags or tag patterns"; + } + + // Lines are split at the nearest prior space char to the 80 char column. + // Tab chars are removed from the output but their positions are used to align + // subsequently wrapped lines + virtual std::string optionDescription() const { + return + "!TBD"; + } + + virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { +// std::string groupName; +// for( std::size_t i = 0; i < cmd.argsCount(); ++i ) { +// if( i != 0 ) +// groupName += " "; +// groupName += cmd[i]; +// } +// TestCaseFilters filters( groupName ); +// for( std::size_t i = 0; i < cmd.argsCount(); ++i ) +// filters.addFilter( TestCaseFilter( cmd[i] ) ); +// config.filters.push_back( filters ); + } + }; + class ListOptionParser : public OptionParser { public: ListOptionParser() : OptionParser( 0, 2 ) { @@ -2888,9 +2934,9 @@ namespace Catch { virtual std::string optionDescription() const { return "Use this option to send all output to a file or a stream. By default output is " - "sent to stdout (note that uses ofstdout and stderr from within test cases are " + "sent to stdout (note that uses of stdout and stderr from within test cases are " "redirected and included in the report - so even stderr will effectively end up " - "on stdout). If the name begins with % it is interpretted as a stream. " + "on stdout). If the name begins with % it is interpreted as a stream. " "Otherwise it is treated as a filename.\n" "\n" "Examples are:\n" @@ -2926,7 +2972,7 @@ namespace Catch { return "Usually you only want to see reporting for failed tests. Sometimes it's useful " "to see all the output (especially when you don't trust that that test you just " - "added worked first time!). To see successul, as well as failing, test results " + "added worked first time!). To see successful, as well as failing, test results " "just pass this option."; } virtual void parseIntoConfig( const Command&, ConfigData& config ) { @@ -3540,10 +3586,18 @@ namespace Catch { else if( !result.ok() ) { m_totals.assertions.failed++; - std::vector::const_iterator it = m_info.begin(); - std::vector::const_iterator itEnd = m_info.end(); - for(; it != itEnd; ++it ) - m_reporter->Result( *it ); + { + std::vector::const_iterator it = m_scopedInfos.begin(); + std::vector::const_iterator itEnd = m_scopedInfos.end(); + for(; it != itEnd; ++it ) + m_reporter->Result( (*it)->getInfo() ); + } + { + std::vector::const_iterator it = m_info.begin(); + std::vector::const_iterator itEnd = m_info.end(); + for(; it != itEnd; ++it ) + m_reporter->Result( *it ); + } m_info.clear(); } @@ -3801,6 +3855,7 @@ namespace Catch { // Handle list request if( config.listSpec != List::None ) { List( config ); + Catch::cleanUp(); return 0; } @@ -4744,38 +4799,197 @@ namespace Catch { // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TESTCASEINFO_HPP_INCLUDED -namespace Catch { +// #included from: catch_tags.hpp +#define TWOBLUECUBES_CATCH_TAGS_HPP_INCLUDED - namespace { - void extractTags( std::string& str, std::set& tags ) { - std::string remainder; - std::size_t last = 0; - std::size_t begin = str.find_first_of( '[' ); - while( begin != std::string::npos ) { - std::size_t end = str.find_first_of( ']', begin ); - if( end != std::string::npos ) { - std::string tag = str.substr( begin+1, end-begin-1 ); - tags.insert( tag ); - if( begin - last > 0 ) - remainder += str.substr( last, begin-last ); - last = end+1; - } - else if( begin != str.size()-1 ) { - end = begin+1; +#include +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + class TagParser { + public: + virtual ~TagParser(); + + void parse( const std::string& str ) { + std::size_t pos = 0; + while( pos < str.size() ) { + char c = str[pos]; + if( c == '[' ) { + std::size_t end = str.find_first_of( ']', pos ); + if( end != std::string::npos ) { + acceptTag( str.substr( pos+1, end-pos-1 ) ); + pos = end+1; + } + else { + acceptChar( c ); + pos++; + } } else { - break; + acceptChar( c ); + pos++; } - begin = str.find_first_of( '[', end ); } - if( !tags.empty() ) { - if( last < str.size() ) - str = remainder + str.substr( last ); - else - str = remainder; + endParse(); + } + + protected: + virtual void acceptTag( const std::string& tag ) = 0; + virtual void acceptChar( char c ) = 0; + virtual void endParse() {} + + private: + }; + + class TagExtracter : public TagParser { + public: + + TagExtracter( std::set& tags ) + : m_tags( tags ) + {} + virtual ~TagExtracter(); + + void parse( std::string& description ) { + TagParser::parse( description ); + description = m_remainder; + } + + private: + virtual void acceptTag( const std::string& tag ) { + m_tags.insert( tag ); + } + virtual void acceptChar( char c ) { + m_remainder += c; + } + + std::set& m_tags; + std::string m_remainder; + }; + + class Tag + { + public: + Tag() + : m_isNegated( false ) + {} + + Tag( const std::string& name, bool isNegated ) + : m_name( name ), + m_isNegated( isNegated ) + {} + + std::string getName() const { + return m_name; + } + bool isNegated() const { + return m_isNegated; + } + + bool operator ! () const { + return m_name.empty(); + } + + private: + std::string m_name; + bool m_isNegated; + }; + + class TagSet + { + typedef std::map TagMap; + public: + void add( const Tag& tag ) { + m_tags.insert( std::make_pair( tag.getName(), tag ) ); + } + + // needed? + Tag find( const std::string& name ) const { + TagMap::const_iterator it = m_tags.find( name ); + if( it == m_tags.end() ) + return Tag(); + else + return it->second; + } + bool empty() const { + return m_tags.empty(); + } + + bool matches( const std::set& tags ) const { + TagMap::const_iterator it = m_tags.begin(); + TagMap::const_iterator itEnd = m_tags.end(); + for(; it != itEnd; ++it ) { + bool found = tags.find( it->first ) != tags.end(); + if( found == it->second.isNegated() ) + return false; + } + return true; + } + + private: + TagMap m_tags; + }; + + class TagExpression { + public: + bool matches( const std::set& tags ) const { + std::vector::const_iterator it = m_tagSets.begin(); + std::vector::const_iterator itEnd = m_tagSets.end(); + for(; it != itEnd; ++it ) + if( it->matches( tags ) ) + return true; + return false; + } + + private: + friend class TagExpressionParser; + + std::vector m_tagSets; + }; + + class TagExpressionParser : public TagParser { + public: + TagExpressionParser( TagExpression& exp ) + : m_isNegated( false ), + m_exp( exp ) + {} + + ~TagExpressionParser(); + + private: + virtual void acceptTag( const std::string& tag ) { + m_currentTagSet.add( Tag( tag, m_isNegated ) ); + m_isNegated = false; + } + virtual void acceptChar( char c ) { + switch( c ) { + case '~': + m_isNegated = true; + break; + case ',': + m_exp.m_tagSets.push_back( m_currentTagSet ); + break; } } - } + virtual void endParse() { + if( !m_currentTagSet.empty() ) + m_exp.m_tagSets.push_back( m_currentTagSet ); + } + + bool m_isNegated; + TagSet m_currentTagSet; + TagExpression& m_exp; + }; + +} // end namespace Catch + +namespace Catch { + TestCaseInfo::TestCaseInfo( ITestCase* testCase, const char* name, const char* description, @@ -4786,7 +5000,7 @@ namespace Catch { m_lineInfo( lineInfo ), m_isHidden( startsWith( name, "./" ) ) { - extractTags( m_description, m_tags ); + TagExtracter( m_tags ).parse( m_description ); if( hasTag( "hide" ) ) m_isHidden = true; } @@ -4839,6 +5053,11 @@ namespace Catch { bool TestCaseInfo::hasTag( const std::string& tag ) const { return m_tags.find( tag ) != m_tags.end(); } + bool TestCaseInfo::matchesTags( const std::string& tagPattern ) const { + TagExpression exp; + TagExpressionParser( exp ).parse( tagPattern ); + return exp.matches( m_tags ); + } const std::set& TestCaseInfo::tags() const { return m_tags; } @@ -4862,7 +5081,8 @@ namespace Catch { swap( temp ); return *this; } -} + +} // end namespace Catch // #included from: ../reporters/catch_reporter_basic.hpp #define TWOBLUECUBES_CATCH_REPORTER_BASIC_HPP_INCLUDED @@ -5784,6 +6004,9 @@ namespace Catch { FreeFunctionTestCase::~FreeFunctionTestCase() {} IGeneratorInfo::~IGeneratorInfo() {} IGeneratorsForTest::~IGeneratorsForTest() {} + TagParser::~TagParser() {} + TagExtracter::~TagExtracter() {} + TagExpressionParser::~TagExpressionParser() {} void Config::dummy() {} From 60fb60f5e04954c697862de0af188db9e9e23da3 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Tue, 25 Sep 2012 07:43:37 +0100 Subject: [PATCH 08/10] Updated help help --- include/internal/catch_commandline.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/internal/catch_commandline.hpp b/include/internal/catch_commandline.hpp index d6467b93..fcb4732d 100644 --- a/include/internal/catch_commandline.hpp +++ b/include/internal/catch_commandline.hpp @@ -188,10 +188,10 @@ namespace Catch { m_optionNames.push_back( "--help" ); } virtual std::string argsSynopsis() const { - return "[