From eca5637c5823b13314129132f266d9c8086e20b3 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Sun, 4 Mar 2012 11:14:21 +0000 Subject: [PATCH] First cut of Matcher support --- include/catch.hpp | 3 ++ include/internal/catch_capture.hpp | 58 ++++++++++++++++++++++ include/internal/catch_resultinfo.hpp | 6 ++- include/reporters/catch_reporter_basic.hpp | 8 +-- projects/SelfTest/MiscTests.cpp | 31 ++++++++++++ 5 files changed, 100 insertions(+), 6 deletions(-) diff --git a/include/catch.hpp b/include/catch.hpp index 7697c231..8b0e688d 100644 --- a/include/catch.hpp +++ b/include/catch.hpp @@ -60,6 +60,9 @@ #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, false, "CHECK_THROWS_AS" ) #define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, false, "CHECK_NOTHROW" ) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, false, "CHECK_THAT" ) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, true, "REQUIRE_THAT" ) + #define INFO( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::Info, false, "INFO" ) #define WARN( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::Warning, false, "WARN" ) #define FAIL( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::ExplicitFailure, true, "FAIL" ) diff --git a/include/internal/catch_capture.hpp b/include/internal/catch_capture.hpp index a152962e..5cd41844 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -266,6 +266,33 @@ public: m_line = line; } + /////////////////////////////////////////////////////////////////////////// + void setLhs + ( + const std::string& lhs + ) + { + m_lhs = lhs; + } + + /////////////////////////////////////////////////////////////////////////// + void setRhs + ( + const std::string& rhs + ) + { + m_rhs = rhs; + } + + /////////////////////////////////////////////////////////////////////////// + void setOp + ( + const std::string& op + ) + { + m_op = op; + } + /////////////////////////////////////////////////////////////////////////// template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || @@ -513,6 +540,7 @@ private: MutableResultInfo* m_result; const LhsT* m_lhs; }; + class ResultBuilder { @@ -599,6 +627,25 @@ public: return *this; } + /////////////////////////////////////////////////////////////////////////// + template + ResultBuilder& acceptMatcher + ( + const MatcherT& matcher, + const ArgT& arg, + const std::string& matcherCallAsString + ) + { + std::string matcherAsString = Catch::toString( matcher ); + if( matcherAsString == "{?}" ) + matcherAsString = matcherCallAsString; + m_result.setLhs( Catch::toString( arg ) ); + m_result.setRhs( matcherAsString ); + m_result.setOp( "matches" ); + m_result.setResultType( matcher( arg ) ? ResultWas::Ok : ResultWas::ExpressionFailed ); + return *this; + } + /////////////////////////////////////////////////////////////////////////// ResultBuilder& setResultType ( @@ -749,4 +796,15 @@ inline bool isTrue Catch::ScopedInfo INTERNAL_CATCH_UNIQUE_NAME( info ); \ INTERNAL_CATCH_UNIQUE_NAME( info ) << log +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( arg, matcher, stopOnFailure, macroName ) \ + do{ try{ \ + INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ResultBuilder( __FILE__, __LINE__, macroName, #arg " " #matcher, false ).acceptMatcher( matcher, arg, #matcher ) ), stopOnFailure ); \ + }catch( Catch::TestFailureException& ){ \ + throw; \ + } catch( ... ){ \ + INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ResultBuilder( __FILE__, __LINE__, macroName, #arg " " #matcher ) << Catch::Hub::getExceptionTranslatorRegistry().translateActiveException() ).setResultType( Catch::ResultWas::ThrewException ), false ); \ + throw; \ + }}while( Catch::isTrue( false ) ) + #endif // TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED diff --git a/include/internal/catch_resultinfo.hpp b/include/internal/catch_resultinfo.hpp index f9da25a8..1e504e4a 100644 --- a/include/internal/catch_resultinfo.hpp +++ b/include/internal/catch_resultinfo.hpp @@ -51,8 +51,8 @@ namespace Catch m_filename( filename ), m_line( line ), m_expr( expr ), - m_lhs(), - m_rhs(), + m_lhs(), + m_rhs(), m_op( isNotExpression( expr ) ? "!" : "" ), m_message( message ), m_result( result ), @@ -157,6 +157,8 @@ namespace Catch { if( m_op == "" || m_isNot ) return m_lhs.empty() ? m_expr : m_op + m_lhs; + else if( m_op == "matches" ) + return m_lhs + " " + m_rhs; else if( m_op != "!" ) return m_lhs + " " + m_op + " " + m_rhs; else diff --git a/include/reporters/catch_reporter_basic.hpp b/include/reporters/catch_reporter_basic.hpp index 990d2200..6bca6b90 100644 --- a/include/reporters/catch_reporter_basic.hpp +++ b/include/reporters/catch_reporter_basic.hpp @@ -118,8 +118,8 @@ namespace Catch { TextColour colour( TextColour::ResultSuccess ); m_config.stream() << "All tests passed (" - << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")"; + << pluralise( totals.assertions.passed, "assertion" ) << " in " + << pluralise( totals.testCases.passed, "test case" ) << ")"; } } @@ -206,7 +206,7 @@ namespace Catch SpanInfo& sectionSpan = m_sectionSpans.back(); if( sectionSpan.emitted && !sectionSpan.name.empty() ) { - m_config.stream() << "[End of section: '" << sectionName << "'"; + m_config.stream() << "[End of section: '" << sectionName << "' "; if( assertions.failed ) { @@ -217,7 +217,7 @@ namespace Catch { TextColour colour( TextColour::ResultSuccess ); m_config.stream() << ( assertions.passed > 1 ? "All " : "" ) - << pluralise( assertions.passed, "assertion" ) << "passed" ; + << pluralise( assertions.passed, "assertion" ) << "passed" ; } m_config.stream() << "]\n" << std::endl; } diff --git a/projects/SelfTest/MiscTests.cpp b/projects/SelfTest/MiscTests.cpp index 957578fd..33e41cf5 100644 --- a/projects/SelfTest/MiscTests.cpp +++ b/projects/SelfTest/MiscTests.cpp @@ -185,3 +185,34 @@ TEST_CASE("./succeeding/atomic if", "") else REQUIRE(x == 0); } + +namespace Matchers +{ + struct ContainsStdString + { + ContainsStdString( const std::string& substr ) : m_substr( substr ){} + + bool operator()( const std::string& str ) const + { + return str.find( m_substr ) != std::string::npos; + } + + friend std::ostream& operator<<( std::ostream& os, const ContainsStdString& matcher ) + { + os << "contains: \"" << matcher.m_substr << "\""; + return os; + } + std::string m_substr; + }; +} + +inline Matchers::ContainsStdString Contains( const std::string& substr ){ return Matchers::ContainsStdString( substr ); } + + +TEST_CASE("./succeeding/matcher", "") +{ + const char* actualStr = "this string contains 'abc' as a substring"; + REQUIRE_THAT( actualStr, Contains( "string" ) ); + CHECK_THAT( actualStr, Contains( "not there" ) ); + CHECK_THAT( actualStr, Contains( "a2bc" ) ); +}