Skip to content

Commit

Permalink
CRTP matchers, Closes catchorg#1553
Browse files Browse the repository at this point in the history
  • Loading branch information
Dvir Yitzchaki committed Feb 27, 2019
1 parent 29b3b7a commit d23941f
Show file tree
Hide file tree
Showing 22 changed files with 381 additions and 258 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -28,3 +28,5 @@ Build
cmake-build-*
benchmark-dir
.conan/test_package/build
_*/
.vscode/
10 changes: 5 additions & 5 deletions docs/matchers.md
Expand Up @@ -76,31 +76,31 @@ used only during reporting of the result.
It's easy to provide your own matchers to extend Catch or just to work with your own types.

You need to provide two things:
1. A matcher class, derived from `Catch::MatcherBase<T>` - where `T` is the type being tested.
1. A matcher class, derived from `Catch::MatcherBase`.
The constructor takes and stores any arguments needed (e.g. something to compare against) and you must
override two methods: `match()` and `describe()`.
provide two methods: `match()` and `describe()`.
2. A simple builder function. This is what is actually called from the test code and allows overloading.

Here's an example for asserting that an integer falls within a given range
(note that it is all inline for the sake of keeping the example short):

```c++
// The matcher class
class IntRange : public Catch::MatcherBase<int> {
class IntRange : public Catch::MatcherBase<IntRange> {
int m_begin, m_end;
public:
IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {}

// Performs the test for this matcher
virtual bool match( int const& i ) const override {
bool match( int const& i ) const {
return i >= m_begin && i <= m_end;
}

// Produces a string describing what this matcher does. It should
// include any provided data (the begin/ end in this case) and
// be written as if it were stating a fact (in the output it will be
// preceded by the value under test).
virtual std::string describe() const {
std::string describe() const {
std::ostringstream ss;
ss << "is between " << m_begin << " and " << m_end;
return ss.str();
Expand Down
11 changes: 3 additions & 8 deletions examples/207-Rpt-TeamCityReporter.cpp
Expand Up @@ -85,30 +85,25 @@ struct MyException: public std::runtime_error
// prevent -Wweak-vtables:
MyException::~MyException() = default;

struct MyExceptionMatcher : Catch::MatcherBase< std::runtime_error >
struct MyExceptionMatcher : Catch::MatcherBase<MyExceptionMatcher>
{
std::string m_text;

MyExceptionMatcher( char const * text )
: m_text( text )
{}

~MyExceptionMatcher() override;

bool match( std::runtime_error const & arg ) const override
bool match( std::runtime_error const & arg ) const
{
return m_text == arg.what() ;
}

std::string describe() const override
std::string describe() const
{
return "it's me";
}
};

// prevent -Wweak-vtables:
MyExceptionMatcher::~MyExceptionMatcher() = default;

TEST_CASE( "TeamCity failing check-throws-matches", "[teamcity]" ) {

CHECK_THROWS_MATCHES( throw MyException("hello"), MyException, MyExceptionMatcher("world") );
Expand Down
24 changes: 0 additions & 24 deletions include/internal/catch_capture_matchers.cpp

This file was deleted.

24 changes: 16 additions & 8 deletions include/internal/catch_capture_matchers.h
Expand Up @@ -15,18 +15,19 @@
#include "catch_matchers_string.h"
#include "catch_matchers_vector.h"
#include "catch_stringref.h"
#include "catch_interfaces_registry_hub.h"

namespace Catch {

template<typename ArgT, typename MatcherT>
class MatchExpr : public ITransientExpression {
ArgT const& m_arg;
ArgT m_arg;
MatcherT m_matcher;
StringRef m_matcherString;
public:
MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString )
MatchExpr( ArgT arg, MatcherT const& matcher, StringRef const& matcherString )
: ITransientExpression{ true, matcher.match( arg ) },
m_arg( arg ),
m_arg( std::forward<ArgT>(arg) ),
m_matcher( matcher ),
m_matcherString( matcherString )
{}
Expand All @@ -41,13 +42,20 @@ namespace Catch {
}
};

using StringMatcher = Matchers::Impl::MatcherBase<std::string>;

void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString );
// This is the general overload that takes a any string matcher
// There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
// the Equals matcher (so the header does not mention matchers)
template<typename StringMatcher,
typename = typename std::enable_if<std::is_base_of<MatcherBase<StringMatcher>, StringMatcher>::value>::type>
void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) {
std::string exceptionMessage = Catch::translateActiveException();
MatchExpr<std::string const&, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
handler.handleExpr( expr );
}

template<typename ArgT, typename MatcherT>
auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT, MatcherT> {
return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString );
auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT&&, MatcherT> {
return MatchExpr<ArgT&&, MatcherT>( std::forward<ArgT>(arg), matcher, matcherString );
}

} // namespace Catch
Expand Down
17 changes: 17 additions & 0 deletions include/internal/catch_common.h
Expand Up @@ -83,6 +83,23 @@ namespace Catch {
T const& operator + ( T const& value, StreamEndStop ) {
return value;
}

// backported index_sequnce
template <std::size_t ...>
struct indexSequence
{ };

template <std::size_t N, std::size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
{ };

template <std::size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
{ using type = indexSequence<Next ... >; };

template <std::size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;

}

#define CATCH_INTERNAL_LINEINFO \
Expand Down
28 changes: 0 additions & 28 deletions include/internal/catch_matchers.cpp

This file was deleted.

0 comments on commit d23941f

Please sign in to comment.