From 9444bbcb7bd0ba2eee6717903490ef7b8bc783a0 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Fri, 12 Oct 2012 07:58:17 +0100 Subject: [PATCH] Added AllOf and AnyOf matchers --- include/internal/catch_expression_builder.hpp | 8 +- include/internal/catch_impl.hpp | 5 + include/internal/catch_matchers.hpp | 197 +++++++++++++---- include/internal/catch_ptr.hpp | 2 +- include/reporters/catch_reporter_basic.hpp | 4 + projects/SelfTest/MiscTests.cpp | 10 + single_include/catch.hpp | 206 ++++++++++++++---- 7 files changed, 345 insertions(+), 87 deletions(-) diff --git a/include/internal/catch_expression_builder.hpp b/include/internal/catch_expression_builder.hpp index 45a7664c..86f68ecb 100644 --- a/include/internal/catch_expression_builder.hpp +++ b/include/internal/catch_expression_builder.hpp @@ -56,14 +56,14 @@ public: ExpressionBuilder& acceptMatcher( const MatcherT& matcher, const ArgT& arg, const std::string& matcherCallAsString ) { - std::string matcherAsString = Catch::toString( matcher ); + std::string matcherAsString = matcher.toString(); if( matcherAsString == "{?}" ) matcherAsString = matcherCallAsString; m_result .setLhs( Catch::toString( arg ) ) .setRhs( matcherAsString ) .setOp( "matches" ) - .setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); + .setResultType( matcher.match( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); return *this; } @@ -71,14 +71,14 @@ public: ExpressionBuilder& acceptMatcher( const MatcherT& matcher, ArgT* arg, const std::string& matcherCallAsString ) { - std::string matcherAsString = Catch::toString( matcher ); + std::string matcherAsString = matcher.toString(); if( matcherAsString == "{?}" ) matcherAsString = matcherCallAsString; m_result .setLhs( Catch::toString( arg ) ) .setRhs( matcherAsString ) .setOp( "matches" ) - .setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); + .setResultType( matcher.match( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); return *this; } diff --git a/include/internal/catch_impl.hpp b/include/internal/catch_impl.hpp index 93650761..b4c4662b 100644 --- a/include/internal/catch_impl.hpp +++ b/include/internal/catch_impl.hpp @@ -60,6 +60,11 @@ namespace Catch { TagExtracter::~TagExtracter() {} TagExpressionParser::~TagExpressionParser() {} + Matchers::Impl::StdString::Equals::~Equals() {} + Matchers::Impl::StdString::Contains::~Contains() {} + Matchers::Impl::StdString::StartsWith::~StartsWith() {} + Matchers::Impl::StdString::EndsWith::~EndsWith() {} + void Config::dummy() {} INTERNAL_CATCH_REGISTER_REPORTER( "basic", BasicReporter ) diff --git a/include/internal/catch_matchers.hpp b/include/internal/catch_matchers.hpp index 79f9a2af..b0a697e5 100644 --- a/include/internal/catch_matchers.hpp +++ b/include/internal/catch_matchers.hpp @@ -11,74 +11,189 @@ namespace Catch { namespace Matchers { namespace Impl { + + template + struct Matcher : SharedImpl + { + virtual ~Matcher() {} + virtual Ptr clone() const = 0; + virtual bool match( const ExpressionT& expr ) const = 0; + virtual std::string toString() const = 0; + }; + + template + struct MatcherImpl : Matcher { + + virtual Ptr > clone() const { + return Ptr >( new DerivedT( static_cast( *this ) ) ); + } + }; + + namespace Generic { + + template + class AllOf : public MatcherImpl, ExpressionT> { + public: + + AllOf() {} + AllOf( const AllOf& other ) : m_matchers( other.m_matchers ) {} + + AllOf& add( const Matcher& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( const ExpressionT& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( !m_matchers[i]->match( expr ) ) + return false; + return true; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " and "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + private: + std::vector > > m_matchers; + }; + + template + class AnyOf : public MatcherImpl, ExpressionT> { + public: + + AnyOf() {} + AnyOf( const AnyOf& other ) : m_matchers( other.m_matchers ) {} + + AnyOf& add( const Matcher& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( const ExpressionT& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( m_matchers[i]->match( expr ) ) + return true; + return false; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " or "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + private: + std::vector > > m_matchers; + }; + + } + namespace StdString { - struct Equals { + struct Equals : MatcherImpl { Equals( const std::string& str ) : m_str( str ){} - - bool operator()( const std::string& str ) const - { - return str == m_str; + Equals( const Equals& other ) : m_str( other.m_str ){} + + virtual ~Equals(); + + virtual bool match( const std::string& expr ) const { + return m_str == expr; } - - friend std::ostream& operator<<( std::ostream& os, const Equals& matcher ) - { - os << "equals: \"" << matcher.m_str << "\""; - return os; + virtual std::string toString() const { + return "equals: \"" + m_str + "\""; } + std::string m_str; }; - struct Contains { + struct Contains : MatcherImpl { Contains( const std::string& substr ) : m_substr( substr ){} - - bool operator()( const std::string& str ) const - { - return str.find( m_substr ) != std::string::npos; + Contains( const Contains& other ) : m_substr( other.m_substr ){} + + virtual ~Contains(); + + virtual bool match( const std::string& expr ) const { + return expr.find( m_substr ) != std::string::npos; } - - friend std::ostream& operator<<( std::ostream& os, const Contains& matcher ) - { - os << "contains: \"" << matcher.m_substr << "\""; - return os; + virtual std::string toString() const { + return "contains: \"" + m_substr + "\""; } + std::string m_substr; }; - struct StartsWith { + struct StartsWith : MatcherImpl { StartsWith( const std::string& substr ) : m_substr( substr ){} - - bool operator()( const std::string& str ) const - { - return str.find( m_substr ) == 0; + StartsWith( const StartsWith& other ) : m_substr( other.m_substr ){} + + virtual ~StartsWith(); + + virtual bool match( const std::string& expr ) const { + return expr.find( m_substr ) == 0; } - - friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher ) - { - os << "starts with: \"" << matcher.m_substr << "\""; - return os; + virtual std::string toString() const { + return "starts with: \"" + m_substr + "\""; } + std::string m_substr; }; - struct EndsWith { + struct EndsWith : MatcherImpl { EndsWith( const std::string& substr ) : m_substr( substr ){} - - bool operator()( const std::string& str ) const - { - return str.find( m_substr ) == str.size() - m_substr.size(); + EndsWith( const EndsWith& other ) : m_substr( other.m_substr ){} + + virtual ~EndsWith(); + + virtual bool match( const std::string& expr ) const { + return expr.find( m_substr ) == expr.size() - m_substr.size(); } - - friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher ) - { - os << "ends with: \"" << matcher.m_substr << "\""; - return os; + virtual std::string toString() const { + return "ends with: \"" + m_substr + "\""; } + std::string m_substr; }; } // namespace StdString } // namespace Impl - + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + template + inline Impl::Generic::AllOf AllOf( const Impl::Matcher& m1, + const Impl::Matcher& m2 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AllOf AllOf( const Impl::Matcher& m1, + const Impl::Matcher& m2, + const Impl::Matcher& m3 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); + } + template + inline Impl::Generic::AnyOf AnyOf( const Impl::Matcher& m1, + const Impl::Matcher& m2 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AnyOf AnyOf( const Impl::Matcher& m1, + const Impl::Matcher& m2, + const Impl::Matcher& m3 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); + } + inline Impl::StdString::Equals Equals( const std::string& str ){ return Impl::StdString::Equals( str ); } inline Impl::StdString::Contains Contains( const std::string& substr ){ return Impl::StdString::Contains( substr ); } inline Impl::StdString::StartsWith StartsWith( const std::string& substr ){ return Impl::StdString::StartsWith( substr ); } diff --git a/include/internal/catch_ptr.hpp b/include/internal/catch_ptr.hpp index a688bc4f..19e92a8d 100644 --- a/include/internal/catch_ptr.hpp +++ b/include/internal/catch_ptr.hpp @@ -78,7 +78,7 @@ namespace Catch { struct SharedImpl : T { SharedImpl() : m_rc( 0 ){} - + virtual void addRef(){ ++m_rc; } diff --git a/include/reporters/catch_reporter_basic.hpp b/include/reporters/catch_reporter_basic.hpp index 33d381f0..2729510c 100644 --- a/include/reporters/catch_reporter_basic.hpp +++ b/include/reporters/catch_reporter_basic.hpp @@ -234,6 +234,10 @@ namespace Catch { if( resultInfo.hasExpandedExpression() ) { m_config.stream << " for: "; + if( resultInfo.getExpandedExpression().size() > 40 ) + m_config.stream << "\n"; + if( resultInfo.getExpandedExpression().size() < 70 ) + m_config.stream << "\t"; TextColour colour( TextColour::ReconstructedExpression ); m_config.stream << resultInfo.getExpandedExpression(); } diff --git a/projects/SelfTest/MiscTests.cpp b/projects/SelfTest/MiscTests.cpp index d7596586..34915069 100644 --- a/projects/SelfTest/MiscTests.cpp +++ b/projects/SelfTest/MiscTests.cpp @@ -270,6 +270,16 @@ TEST_CASE("./failing/matchers/Equals", "") CHECK_THAT( testStringForMatching(), Equals( "something else" ) ); } +TEST_CASE("/succeeding/matchers/AllOf", "") +{ + CHECK_THAT( testStringForMatching(), AllOf( Catch::Contains( "string" ), Catch::Contains( "abc" ) ) ); +} +TEST_CASE("/succeeding/matchers/AnyOf", "") +{ + CHECK_THAT( testStringForMatching(), AnyOf( Catch::Contains( "string" ), Catch::Contains( "not there" ) ) ); + CHECK_THAT( testStringForMatching(), AnyOf( Catch::Contains( "not there" ), Catch::Contains( "string" ) ) ); +} + TEST_CASE("./succeeding/matchers/Equals", "") { CHECK_THAT( testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) ); diff --git a/single_include/catch.hpp b/single_include/catch.hpp index 4c98bae5..0f84bfe9 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1,5 +1,5 @@ /* - * Generated: 2012-10-09 20:58:02.234458 + * Generated: 2012-10-12 07:57:48.487873 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -1041,14 +1041,14 @@ public: ExpressionBuilder& acceptMatcher( const MatcherT& matcher, const ArgT& arg, const std::string& matcherCallAsString ) { - std::string matcherAsString = Catch::toString( matcher ); + std::string matcherAsString = matcher.toString(); if( matcherAsString == "{?}" ) matcherAsString = matcherCallAsString; m_result .setLhs( Catch::toString( arg ) ) .setRhs( matcherAsString ) .setOp( "matches" ) - .setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); + .setResultType( matcher.match( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); return *this; } @@ -1056,14 +1056,14 @@ public: ExpressionBuilder& acceptMatcher( const MatcherT& matcher, ArgT* arg, const std::string& matcherCallAsString ) { - std::string matcherAsString = Catch::toString( matcher ); + std::string matcherAsString = matcher.toString(); if( matcherAsString == "{?}" ) matcherAsString = matcherCallAsString; m_result .setLhs( Catch::toString( arg ) ) .setRhs( matcherAsString ) .setOp( "matches" ) - .setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); + .setResultType( matcher.match( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); return *this; } @@ -2521,74 +2521,189 @@ inline std::string toString( const Detail::Approx& value ) { namespace Catch { namespace Matchers { namespace Impl { + + template + struct Matcher : SharedImpl + { + virtual ~Matcher() {} + virtual Ptr clone() const = 0; + virtual bool match( const ExpressionT& expr ) const = 0; + virtual std::string toString() const = 0; + }; + + template + struct MatcherImpl : Matcher { + + virtual Ptr > clone() const { + return Ptr >( new DerivedT( static_cast( *this ) ) ); + } + }; + + namespace Generic { + + template + class AllOf : public MatcherImpl, ExpressionT> { + public: + + AllOf() {} + AllOf( const AllOf& other ) : m_matchers( other.m_matchers ) {} + + AllOf& add( const Matcher& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( const ExpressionT& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( !m_matchers[i]->match( expr ) ) + return false; + return true; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " and "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + private: + std::vector > > m_matchers; + }; + + template + class AnyOf : public MatcherImpl, ExpressionT> { + public: + + AnyOf() {} + AnyOf( const AnyOf& other ) : m_matchers( other.m_matchers ) {} + + AnyOf& add( const Matcher& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( const ExpressionT& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( m_matchers[i]->match( expr ) ) + return true; + return false; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " or "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + private: + std::vector > > m_matchers; + }; + + } + namespace StdString { - struct Equals { + struct Equals : MatcherImpl { Equals( const std::string& str ) : m_str( str ){} + Equals( const Equals& other ) : m_str( other.m_str ){} - bool operator()( const std::string& str ) const - { - return str == m_str; + virtual ~Equals(); + + virtual bool match( const std::string& expr ) const { + return m_str == expr; + } + virtual std::string toString() const { + return "equals: \"" + m_str + "\""; } - friend std::ostream& operator<<( std::ostream& os, const Equals& matcher ) - { - os << "equals: \"" << matcher.m_str << "\""; - return os; - } std::string m_str; }; - struct Contains { + struct Contains : MatcherImpl { Contains( const std::string& substr ) : m_substr( substr ){} + Contains( const Contains& other ) : m_substr( other.m_substr ){} - bool operator()( const std::string& str ) const - { - return str.find( m_substr ) != std::string::npos; + virtual ~Contains(); + + virtual bool match( const std::string& expr ) const { + return expr.find( m_substr ) != std::string::npos; + } + virtual std::string toString() const { + return "contains: \"" + m_substr + "\""; } - friend std::ostream& operator<<( std::ostream& os, const Contains& matcher ) - { - os << "contains: \"" << matcher.m_substr << "\""; - return os; - } std::string m_substr; }; - struct StartsWith { + struct StartsWith : MatcherImpl { StartsWith( const std::string& substr ) : m_substr( substr ){} + StartsWith( const StartsWith& other ) : m_substr( other.m_substr ){} - bool operator()( const std::string& str ) const - { - return str.find( m_substr ) == 0; + virtual ~StartsWith(); + + virtual bool match( const std::string& expr ) const { + return expr.find( m_substr ) == 0; + } + virtual std::string toString() const { + return "starts with: \"" + m_substr + "\""; } - friend std::ostream& operator<<( std::ostream& os, const StartsWith& matcher ) - { - os << "starts with: \"" << matcher.m_substr << "\""; - return os; - } std::string m_substr; }; - struct EndsWith { + struct EndsWith : MatcherImpl { EndsWith( const std::string& substr ) : m_substr( substr ){} + EndsWith( const EndsWith& other ) : m_substr( other.m_substr ){} - bool operator()( const std::string& str ) const - { - return str.find( m_substr ) == str.size() - m_substr.size(); + virtual ~EndsWith(); + + virtual bool match( const std::string& expr ) const { + return expr.find( m_substr ) == expr.size() - m_substr.size(); + } + virtual std::string toString() const { + return "ends with: \"" + m_substr + "\""; } - friend std::ostream& operator<<( std::ostream& os, const EndsWith& matcher ) - { - os << "ends with: \"" << matcher.m_substr << "\""; - return os; - } std::string m_substr; }; } // namespace StdString } // namespace Impl + // The following functions create the actual matcher objects. + // This allows the types to be inferred + template + inline Impl::Generic::AllOf AllOf( const Impl::Matcher& m1, + const Impl::Matcher& m2 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AllOf AllOf( const Impl::Matcher& m1, + const Impl::Matcher& m2, + const Impl::Matcher& m3 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); + } + template + inline Impl::Generic::AnyOf AnyOf( const Impl::Matcher& m1, + const Impl::Matcher& m2 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AnyOf AnyOf( const Impl::Matcher& m1, + const Impl::Matcher& m2, + const Impl::Matcher& m3 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); + } + inline Impl::StdString::Equals Equals( const std::string& str ){ return Impl::StdString::Equals( str ); } inline Impl::StdString::Contains Contains( const std::string& substr ){ return Impl::StdString::Contains( substr ); } inline Impl::StdString::StartsWith StartsWith( const std::string& substr ){ return Impl::StdString::StartsWith( substr ); } @@ -5374,6 +5489,10 @@ namespace Catch { if( resultInfo.hasExpandedExpression() ) { m_config.stream << " for: "; + if( resultInfo.getExpandedExpression().size() > 40 ) + m_config.stream << "\n"; + if( resultInfo.getExpandedExpression().size() < 70 ) + m_config.stream << "\t"; TextColour colour( TextColour::ReconstructedExpression ); m_config.stream << resultInfo.getExpandedExpression(); } @@ -6045,6 +6164,11 @@ namespace Catch { TagExtracter::~TagExtracter() {} TagExpressionParser::~TagExpressionParser() {} + Matchers::Impl::StdString::Equals::~Equals() {} + Matchers::Impl::StdString::Contains::~Contains() {} + Matchers::Impl::StdString::StartsWith::~StartsWith() {} + Matchers::Impl::StdString::EndsWith::~EndsWith() {} + void Config::dummy() {} INTERNAL_CATCH_REGISTER_REPORTER( "basic", BasicReporter )