diff --git a/.gitignore b/.gitignore index f017e9e09f..588044080c 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ Build cmake-build-* benchmark-dir .conan/test_package/build +_*/ +.vscode/ +.history/ diff --git a/docs/matchers.md b/docs/matchers.md index bdb7dac473..91fff807b2 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -147,9 +147,9 @@ REQUIRE_THROWS_MATCHES(throwsDerivedException(), DerivedException, Message("De 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` - where `T` is the type being tested. +1. A matcher class, derived from `Catch::MatcherBase` - where `T` is the matcher class. 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 @@ -157,13 +157,13 @@ Here's an example for asserting that an integer falls within a given range ```c++ // The matcher class -class IntRange : public Catch::MatcherBase { +class IntRange : public Catch::MatcherBase { int m_begin, m_end; public: IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {} // Performs the test for this matcher - bool match( int const& i ) const override { + bool match( int const& i ) const { return i >= m_begin && i <= m_end; } @@ -171,7 +171,7 @@ public: // 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 override { + std::string describe() const { std::ostringstream ss; ss << "is between " << m_begin << " and " << m_end; return ss.str(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7e37c3b9be..7d4e3c3ca2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,7 +123,6 @@ set(IMPL_SOURCES ${SOURCES_DIR}/catch_approx.cpp ${SOURCES_DIR}/catch_assertionhandler.cpp ${SOURCES_DIR}/catch_assertionresult.cpp - ${SOURCES_DIR}/catch_capture_matchers.cpp ${SOURCES_DIR}/catch_commandline.cpp ${SOURCES_DIR}/catch_common.cpp ${SOURCES_DIR}/catch_config.cpp @@ -147,7 +146,6 @@ set(IMPL_SOURCES ${SOURCES_DIR}/catch_interfaces_testcase.cpp ${SOURCES_DIR}/catch_list.cpp ${SOURCES_DIR}/catch_leak_detector.cpp - ${SOURCES_DIR}/catch_matchers.cpp ${SOURCES_DIR}/catch_matchers_exception.cpp ${SOURCES_DIR}/catch_matchers_floating.cpp ${SOURCES_DIR}/catch_matchers_generic.cpp diff --git a/src/catch2/catch_capture_matchers.cpp b/src/catch2/catch_capture_matchers.cpp deleted file mode 100644 index ad51baca45..0000000000 --- a/src/catch2/catch_capture_matchers.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Created by Phil on 9/8/2017. - * Copyright 2017 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) - */ -#include -#include - -namespace Catch { - - using StringMatcher = Matchers::Impl::MatcherBase; - - // 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) - void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { - std::string exceptionMessage = Catch::translateActiveException(); - MatchExpr expr( exceptionMessage, matcher, matcherString ); - handler.handleExpr( expr ); - } - -} // namespace Catch diff --git a/src/catch2/catch_capture_matchers.h b/src/catch2/catch_capture_matchers.h index 7c8c8fa60c..f0a26fcec3 100644 --- a/src/catch2/catch_capture_matchers.h +++ b/src/catch2/catch_capture_matchers.h @@ -16,18 +16,19 @@ #include #include #include +#include namespace Catch { template 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(arg) ), m_matcher( matcher ), m_matcherString( matcherString ) {} @@ -42,13 +43,20 @@ namespace Catch { } }; - using StringMatcher = Matchers::Impl::MatcherBase; - - 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, StringMatcher>::value>::type> + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } template - auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { - return MatchExpr( arg, matcher, matcherString ); + auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { + return MatchExpr( std::forward(arg), matcher, matcherString ); } } // namespace Catch diff --git a/src/catch2/catch_common.h b/src/catch2/catch_common.h index 392576d6c0..827e1591c4 100644 --- a/src/catch2/catch_common.h +++ b/src/catch2/catch_common.h @@ -80,6 +80,23 @@ namespace Catch { T const& operator + ( T const& value, StreamEndStop ) { return value; } + + // backported index_sequnce + template + struct indexSequence + { }; + + template + struct indexSequenceHelper : public indexSequenceHelper + { }; + + template + struct indexSequenceHelper<0U, Next ... > + { using type = indexSequence; }; + + template + using makeIndexSequence = typename indexSequenceHelper::type; + } #define CATCH_INTERNAL_LINEINFO \ diff --git a/src/catch2/catch_matchers.cpp b/src/catch2/catch_matchers.cpp deleted file mode 100644 index df2807964d..0000000000 --- a/src/catch2/catch_matchers.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Created by Phil Nash on 19/07/2017. - * - * 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) - */ - -#include - -namespace Catch { -namespace Matchers { - namespace Impl { - - std::string MatcherUntypedBase::toString() const { - if( m_cachedToString.empty() ) - m_cachedToString = describe(); - return m_cachedToString; - } - - MatcherUntypedBase::~MatcherUntypedBase() = default; - - } // namespace Impl -} // namespace Matchers - -using namespace Matchers; -using Matchers::Impl::MatcherBase; - -} // namespace Catch diff --git a/src/catch2/catch_matchers.h b/src/catch2/catch_matchers.h index 78d9e84e81..67926d6437 100644 --- a/src/catch2/catch_matchers.h +++ b/src/catch2/catch_matchers.h @@ -11,153 +11,203 @@ #include #include -#include +#include namespace Catch { namespace Matchers { namespace Impl { - template struct MatchAllOf; - template struct MatchAnyOf; - template struct MatchNotOf; + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; - class MatcherUntypedBase { + template + class MatcherBase { public: - MatcherUntypedBase() = default; - MatcherUntypedBase ( MatcherUntypedBase const& ) = default; - MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + MatcherBase() = default; + MatcherBase ( MatcherBase const& ) = default; + MatcherBase& operator = ( MatcherBase const& ) = delete; std::string toString() const; + template + MatchAllOf operator && ( Other const& other ) const; + + template + MatchAnyOf operator || ( Other const& other ) const; + + MatchNotOf operator ! () const; + + Derived const& derived() const { return static_cast(*this); } + protected: - virtual ~MatcherUntypedBase(); - virtual std::string describe() const = 0; mutable std::string m_cachedToString; }; -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wnon-virtual-dtor" -#endif + template + std::string MatcherBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = derived().describe(); + return m_cachedToString; + } - template - struct MatcherMethod { - virtual bool match( ObjectT const& arg ) const = 0; - }; + template + using Tuple = std::tuple::type>::type ...>; -#if defined(__OBJC__) - // Hack to fix Catch GH issue #1661. Could use id for generic Object support. - // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation - template<> - struct MatcherMethod { - virtual bool match( NSString* arg ) const = 0; - }; -#endif + template + struct MatchAllOf : MatcherBase> { + MatchAllOf( Matchers const &... matchers ) : m_matchers{matchers...} + {} -#ifdef __clang__ -# pragma clang diagnostic pop -#endif + template + bool match_helper( ArgT && ) const { + return true; + } - template - struct MatcherBase : MatcherUntypedBase, MatcherMethod { + template + bool match_helper( ArgT && arg, Matcher &&matcher, Rest &&... rest ) const { + if (!matcher.match(std::forward(arg))) { + return false; + } + return match_helper(std::forward(arg), std::forward(rest)...); + } - MatchAllOf operator && ( MatcherBase const& other ) const; - MatchAnyOf operator || ( MatcherBase const& other ) const; - MatchNotOf operator ! () const; - }; + template + bool match_impl( ArgT && arg, indexSequence) const { + return match_helper(std::forward(arg), std::get(m_matchers)...); + } - template - struct MatchAllOf : MatcherBase { - bool match( ArgT const& arg ) const override { - for( auto matcher : m_matchers ) { - if (!matcher->match(arg)) - return false; - } - return true; + template + bool match( ArgT && arg ) const { + return match_impl(std::forward(arg), makeIndexSequence{}); } - std::string describe() const override { + + std::string describe_helper() const { return {}; } + + template + std::string describe_helper( Matcher &&matcher, Rest &&... rest ) const { + return " and " + matcher.describe() + describe_helper(std::forward(rest)...); + } + + template + std::string describe_impl( indexSequence) const { + // remove first and + return describe_helper(std::get(m_matchers)...).substr(5); + } + + std::string describe() const { std::string description; - description.reserve( 4 + m_matchers.size()*32 ); - description += "( "; - bool first = true; - for( auto matcher : m_matchers ) { - if( first ) - first = false; - else - description += " and "; - description += matcher->toString(); - } - description += " )"; + description.reserve( 4 + sizeof...(Matchers)*32 ); + description += "( " + describe_impl(makeIndexSequence{}) + " )"; return description; } - MatchAllOf& operator && ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; + template + MatchAllOf and_helper(Other const &other, indexSequence) { + return {std::get(m_matchers)..., other}; + } + + template + MatchAllOf operator && ( Other const& other ) { + return and_helper(other, makeIndexSequence()); } - std::vector const*> m_matchers; + Tuple m_matchers; }; - template - struct MatchAnyOf : MatcherBase { - - bool match( ArgT const& arg ) const override { - for( auto matcher : m_matchers ) { - if (matcher->match(arg)) - return true; - } - return false; + + template + struct MatchAnyOf : MatcherBase> { + MatchAnyOf( Matchers const &... matchers ) : m_matchers{matchers...} + {} + + template + bool match_helper( ArgT && ) const { + return false; + } + + template + bool match_helper( ArgT && arg, Matcher &&matcher, Rest &&... rest ) const { + if (matcher.match(std::forward(arg))) { + return true; + } + + return match_helper(std::forward(arg), std::forward(rest)...); + } + + template + bool match_impl( ArgT && arg, indexSequence) const { + return match_helper(std::forward(arg), std::get(m_matchers)...); } - std::string describe() const override { + + template + bool match( ArgT && arg ) const { + return match_impl(std::forward(arg), makeIndexSequence{}); + } + + std::string describe_helper() const { return {}; } + + template + std::string describe_helper( Matcher &&matcher, Rest &&... rest ) const { + return " or " + matcher.describe() + describe_helper(std::forward(rest)...); + } + + template + std::string describe_impl( indexSequence) const { + // remove first or + return describe_helper(std::get(m_matchers)...).substr(4); + } + + std::string describe() const { std::string description; - description.reserve( 4 + m_matchers.size()*32 ); - description += "( "; - bool first = true; - for( auto matcher : m_matchers ) { - if( first ) - first = false; - else - description += " or "; - description += matcher->toString(); - } - description += " )"; + description.reserve( 4 + sizeof...(Matchers)*32 ); + description += "( " + describe_impl(makeIndexSequence{}) + " )"; return description; } - MatchAnyOf& operator || ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; + template + MatchAnyOf or_helper(Other const &other, indexSequence) { + return {std::get(m_matchers)..., other}; + } + + template + MatchAnyOf operator || ( Other const& other ) { + return or_helper(other, makeIndexSequence()); } - std::vector const*> m_matchers; + Tuple m_matchers; }; - template - struct MatchNotOf : MatcherBase { + template + struct MatchNotOf : MatcherBase> { - MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + MatchNotOf( Matcher const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} - bool match( ArgT const& arg ) const override { - return !m_underlyingMatcher.match( arg ); + template + bool match( ArgT && arg ) const { + return !m_underlyingMatcher.match( std::forward(arg) ); } - std::string describe() const override { + std::string describe() const { return "not " + m_underlyingMatcher.toString(); } - MatcherBase const& m_underlyingMatcher; + Matcher const& m_underlyingMatcher; }; - template - MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { - return MatchAllOf() && *this && other; + template + template + MatchAllOf MatcherBase::operator && ( Other const& other ) const { + return {derived(), other}; } - template - MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { - return MatchAnyOf() || *this || other; + + template + template + MatchAnyOf MatcherBase::operator || ( Other const& other ) const { + return {derived(), other}; } - template - MatchNotOf MatcherBase::operator ! () const { - return MatchNotOf( *this ); + + template + MatchNotOf MatcherBase::operator ! () const { + return {derived()}; } } // namespace Impl diff --git a/src/catch2/catch_matchers_exception.hpp b/src/catch2/catch_matchers_exception.hpp index b0f3d32d67..dc30542d3d 100644 --- a/src/catch2/catch_matchers_exception.hpp +++ b/src/catch2/catch_matchers_exception.hpp @@ -13,7 +13,7 @@ namespace Catch { namespace Matchers { namespace Exception { -class ExceptionMessageMatcher : public MatcherBase { +class ExceptionMessageMatcher : public MatcherBase { std::string m_message; public: @@ -21,9 +21,9 @@ class ExceptionMessageMatcher : public MatcherBase { m_message(message) {} - bool match(std::exception const& ex) const override; + bool match(std::exception const& ex) const; - std::string describe() const override; + std::string describe() const; }; } // namespace Exception diff --git a/src/catch2/catch_matchers_floating.h b/src/catch2/catch_matchers_floating.h index dc1bebe040..0bf8e7e650 100644 --- a/src/catch2/catch_matchers_floating.h +++ b/src/catch2/catch_matchers_floating.h @@ -16,19 +16,19 @@ namespace Matchers { enum class FloatingPointKind : uint8_t; - struct WithinAbsMatcher : MatcherBase { + struct WithinAbsMatcher : MatcherBase { WithinAbsMatcher(double target, double margin); - bool match(double const& matchee) const override; - std::string describe() const override; + bool match(double const& matchee) const; + std::string describe() const; private: double m_target; double m_margin; }; - struct WithinUlpsMatcher : MatcherBase { + struct WithinUlpsMatcher : MatcherBase { WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType); - bool match(double const& matchee) const override; - std::string describe() const override; + bool match(double const& matchee) const; + std::string describe() const; private: double m_target; uint64_t m_ulps; @@ -41,10 +41,10 @@ namespace Matchers { // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get // the same result if we do this for floats, as if we do this for // doubles that were promoted from floats. - struct WithinRelMatcher : MatcherBase { + struct WithinRelMatcher : MatcherBase { WithinRelMatcher(double target, double epsilon); - bool match(double const& matchee) const override; - std::string describe() const override; + bool match(double const& matchee) const; + std::string describe() const; private: double m_target; double m_epsilon; diff --git a/src/catch2/catch_matchers_generic.hpp b/src/catch2/catch_matchers_generic.hpp index d39d1bbed8..a85955bd69 100644 --- a/src/catch2/catch_matchers_generic.hpp +++ b/src/catch2/catch_matchers_generic.hpp @@ -22,8 +22,8 @@ namespace Detail { std::string finalizeDescription(const std::string& desc); } -template -class PredicateMatcher : public MatcherBase { +template +class PredicateMatcher : public MatcherBase> { Predicate m_predicate; std::string m_description; public: @@ -33,11 +33,14 @@ class PredicateMatcher : public MatcherBase { m_description(Detail::finalizeDescription(descr)) {} - bool match( T const& item ) const override { - return m_predicate(item); + template + bool match( ArgT && item ) const { + static_assert(is_callable::value, "Predicate not callable with argument T"); + static_assert(std::is_same>::value, "Predicate does not return bool"); + return m_predicate(std::forward(item)); } - std::string describe() const override { + std::string describe() const { return m_description; } }; @@ -48,11 +51,9 @@ class PredicateMatcher : public MatcherBase { // The user has to explicitly specify type to the function, because // inferring std::function is hard (but possible) and // requires a lot of TMP. - template - Generic::PredicateMatcher Predicate(Pred&& predicate, std::string const& description = "") { - static_assert(is_callable::value, "Predicate not callable with argument T"); - static_assert(std::is_same>::value, "Predicate does not return bool"); - return Generic::PredicateMatcher(std::forward(predicate), description); + template + Generic::PredicateMatcher Predicate(Pred&& predicate, std::string const& description = "") { + return Generic::PredicateMatcher(std::forward(predicate), description); } } // namespace Matchers diff --git a/src/catch2/catch_matchers_string.cpp b/src/catch2/catch_matchers_string.cpp index 024cf54919..cc96040ba0 100644 --- a/src/catch2/catch_matchers_string.cpp +++ b/src/catch2/catch_matchers_string.cpp @@ -32,24 +32,6 @@ namespace Matchers { : std::string(); } - - StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) - : m_comparator( comparator ), - m_operation( operation ) { - } - - std::string StringMatcherBase::describe() const { - std::string description; - description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + - m_comparator.caseSensitivitySuffix().size()); - description += m_operation; - description += ": \""; - description += m_comparator.m_str; - description += "\""; - description += m_comparator.caseSensitivitySuffix(); - return description; - } - EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} bool EqualsMatcher::match( std::string const& source ) const { diff --git a/src/catch2/catch_matchers_string.h b/src/catch2/catch_matchers_string.h index 075a7e9498..51ac62223a 100644 --- a/src/catch2/catch_matchers_string.h +++ b/src/catch2/catch_matchers_string.h @@ -27,42 +27,63 @@ namespace Matchers { std::string m_str; }; - struct StringMatcherBase : MatcherBase { + template + struct StringMatcherBase : MatcherBase { StringMatcherBase( std::string const& operation, CasedString const& comparator ); - std::string describe() const override; + std::string describe() const ; CasedString m_comparator; std::string m_operation; }; - struct EqualsMatcher : StringMatcherBase { + + template + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + template + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + struct EqualsMatcher : StringMatcherBase { EqualsMatcher( CasedString const& comparator ); - bool match( std::string const& source ) const override; + bool match( std::string const& source ) const ; }; - struct ContainsMatcher : StringMatcherBase { + struct ContainsMatcher : StringMatcherBase { ContainsMatcher( CasedString const& comparator ); - bool match( std::string const& source ) const override; + bool match( std::string const& source ) const ; }; - struct StartsWithMatcher : StringMatcherBase { + struct StartsWithMatcher : StringMatcherBase { StartsWithMatcher( CasedString const& comparator ); - bool match( std::string const& source ) const override; + bool match( std::string const& source ) const ; }; - struct EndsWithMatcher : StringMatcherBase { + struct EndsWithMatcher : StringMatcherBase { EndsWithMatcher( CasedString const& comparator ); - bool match( std::string const& source ) const override; + bool match( std::string const& source ) const ; }; - struct RegexMatcher : MatcherBase { + struct RegexMatcher : MatcherBase { RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); - bool match( std::string const& matchee ) const override; - std::string describe() const override; + bool match( std::string const& matchee ) const ; + std::string describe() const ; private: std::string m_regex; CaseSensitive::Choice m_caseSensitivity; }; - } // namespace StdString + } // namespace StdString` // The following functions create the actual matcher objects. diff --git a/src/catch2/catch_matchers_vector.h b/src/catch2/catch_matchers_vector.h index 13ac907dab..72dc8c6410 100644 --- a/src/catch2/catch_matchers_vector.h +++ b/src/catch2/catch_matchers_vector.h @@ -18,11 +18,11 @@ namespace Matchers { namespace Vector { template - struct ContainsElementMatcher : MatcherBase> { + struct ContainsElementMatcher : MatcherBase> { ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} - bool match(std::vector const &v) const override { + bool match(std::vector const &v) const { for (auto const& el : v) { if (el == m_comparator) { return true; @@ -31,7 +31,7 @@ namespace Matchers { return false; } - std::string describe() const override { + std::string describe() const { return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } @@ -39,11 +39,11 @@ namespace Matchers { }; template - struct ContainsMatcher : MatcherBase> { + struct ContainsMatcher : MatcherBase> { ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const override { + bool match(std::vector const &v) const { // !TBD: see note in EqualsMatcher if (m_comparator.size() > v.size()) return false; @@ -61,7 +61,7 @@ namespace Matchers { } return true; } - std::string describe() const override { + std::string describe() const { return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } @@ -69,11 +69,11 @@ namespace Matchers { }; template - struct EqualsMatcher : MatcherBase> { + struct EqualsMatcher : MatcherBase> { EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const override { + bool match(std::vector const &v) const { // !TBD: This currently works if all elements can be compared using != // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc @@ -85,18 +85,18 @@ namespace Matchers { return false; return true; } - std::string describe() const override { + std::string describe() const { return "Equals: " + ::Catch::Detail::stringify( m_comparator ); } std::vector const& m_comparator; }; template - struct ApproxMatcher : MatcherBase> { + struct ApproxMatcher : MatcherBase> { ApproxMatcher(std::vector const& comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const override { + bool match(std::vector const &v) const { if (m_comparator.size() != v.size()) return false; for (std::size_t i = 0; i < v.size(); ++i) @@ -104,7 +104,7 @@ namespace Matchers { return false; return true; } - std::string describe() const override { + std::string describe() const { return "is approx: " + ::Catch::Detail::stringify( m_comparator ); } template ::value>::type> @@ -128,9 +128,9 @@ namespace Matchers { }; template - struct UnorderedEqualsMatcher : MatcherBase> { + struct UnorderedEqualsMatcher : MatcherBase> { UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} - bool match(std::vector const& vec) const override { + bool match(std::vector const& vec) const { // Note: This is a reimplementation of std::is_permutation, // because I don't want to include inside the common path if (m_target.size() != vec.size()) { @@ -139,7 +139,7 @@ namespace Matchers { return std::is_permutation(m_target.begin(), m_target.end(), vec.begin()); } - std::string describe() const override { + std::string describe() const { return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); } private: diff --git a/src/catch2/catch_objc.hpp b/src/catch2/catch_objc.hpp index 5b7c275ced..13ffcf3cd1 100644 --- a/src/catch2/catch_objc.hpp +++ b/src/catch2/catch_objc.hpp @@ -109,14 +109,14 @@ namespace Catch { namespace Impl { namespace NSStringMatchers { - struct StringHolder : MatcherBase{ + struct StringHolder : MatcherBase{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } - bool match( NSString* str ) const override { + bool match( NSString* str ) const { return false; } @@ -126,12 +126,12 @@ namespace Catch { struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - bool match( NSString* str ) const override { + bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - std::string describe() const override { + std::string describe() const { return "equals string: " + Catch::Detail::stringify( m_substr ); } }; @@ -144,7 +144,7 @@ namespace Catch { [str rangeOfString:m_substr].location != NSNotFound; } - std::string describe() const override { + std::string describe() const { return "contains string: " + Catch::Detail::stringify( m_substr ); } }; @@ -152,24 +152,24 @@ namespace Catch { struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - bool match( NSString* str ) const override { + bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - std::string describe() const override { + std::string describe() const { return "starts with: " + Catch::Detail::stringify( m_substr ); } }; struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - bool match( NSString* str ) const override { + bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - std::string describe() const override { + std::string describe() const { return "ends with: " + Catch::Detail::stringify( m_substr ); } }; diff --git a/src/catch2/reporters/catch_reporter_tap.hpp b/src/catch2/reporters/catch_reporter_tap.hpp index a9b72dcba7..512d3396ab 100644 --- a/src/catch2/reporters/catch_reporter_tap.hpp +++ b/src/catch2/reporters/catch_reporter_tap.hpp @@ -29,7 +29,7 @@ namespace Catch { return m_reporterPrefs; } - void noMatchingTestCases( std::string const& spec ) override { + void nomatchingTestCases( std::string const& spec ) { stream << "# No test cases matched '" << spec << "'" << std::endl; } diff --git a/src/catch2/reporters/catch_reporter_teamcity.hpp b/src/catch2/reporters/catch_reporter_teamcity.hpp index e94035befe..108e1374fd 100644 --- a/src/catch2/reporters/catch_reporter_teamcity.hpp +++ b/src/catch2/reporters/catch_reporter_teamcity.hpp @@ -47,7 +47,7 @@ namespace Catch { void skipTest( TestCaseInfo const& /* testInfo */ ) override {} - void noMatchingTestCases( std::string const& /* spec */ ) override {} + void nomatchingTestCases( std::string const& /* spec */ ) {} void testGroupStarting( GroupInfo const& groupInfo ) override { StreamingReporterBase::testGroupStarting( groupInfo ); diff --git a/tests/ExtraTests/X01-PrefixedMacros.cpp b/tests/ExtraTests/X01-PrefixedMacros.cpp index 3626fd103e..ecda665cb3 100644 --- a/tests/ExtraTests/X01-PrefixedMacros.cpp +++ b/tests/ExtraTests/X01-PrefixedMacros.cpp @@ -24,7 +24,7 @@ CATCH_TEST_CASE("PrefixedMacros") { CATCH_REQUIRE_THROWS(this_throws()); CATCH_REQUIRE_THROWS_AS(this_throws(), std::runtime_error); CATCH_REQUIRE_THROWS_WITH(this_throws(), "Some msg"); - CATCH_REQUIRE_THROWS_MATCHES(this_throws(), std::runtime_error, Predicate([](std::runtime_error const&) { return true; })); + CATCH_REQUIRE_THROWS_MATCHES(this_throws(), std::runtime_error, Predicate([](std::runtime_error const&) { return true; })); CATCH_REQUIRE_NOTHROW(this_doesnt_throw()); CATCH_CHECK( 1 == 1 ); @@ -40,7 +40,7 @@ CATCH_TEST_CASE("PrefixedMacros") { CATCH_CHECK_THROWS(this_throws()); CATCH_CHECK_THROWS_AS(this_throws(), std::runtime_error); CATCH_CHECK_THROWS_WITH(this_throws(), "Some msg"); - CATCH_CHECK_THROWS_MATCHES(this_throws(), std::runtime_error, Predicate([](std::runtime_error const&) { return true; })); + CATCH_CHECK_THROWS_MATCHES(this_throws(), std::runtime_error, Predicate([](std::runtime_error const&) { return true; })); CATCH_CHECK_NOTHROW(this_doesnt_throw()); CATCH_REQUIRE_THAT("abcd", Equals("abcd")); diff --git a/tests/SelfTest/Baselines/compact.sw.approved.txt b/tests/SelfTest/Baselines/compact.sw.approved.txt index 0b2436ce8c..3d2d7d9eae 100644 --- a/tests/SelfTest/Baselines/compact.sw.approved.txt +++ b/tests/SelfTest/Baselines/compact.sw.approved.txt @@ -234,10 +234,10 @@ Approx.tests.cpp:: passed: 0 == Approx( dZero) for: 0 == Approx( 0. Approx.tests.cpp:: passed: 0 == Approx( dSmall ).margin( 0.001 ) for: 0 == Approx( 0.00001 ) Approx.tests.cpp:: passed: 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 ) Approx.tests.cpp:: passed: dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 ) -Matchers.tests.cpp:: passed: 1, Predicate(alwaysTrue, "always true") for: 1 matches predicate: "always true" -Matchers.tests.cpp:: passed: 1, !Predicate(alwaysFalse, "always false") for: 1 not matches predicate: "always false" -Matchers.tests.cpp:: passed: "Hello olleH", Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") for: "Hello olleH" matches predicate: "First and last character should be equal" -Matchers.tests.cpp:: passed: "This wouldn't pass", !Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) for: "This wouldn't pass" not matches undescribed predicate +Matchers.tests.cpp:: passed: 1, Predicate(alwaysTrue, "always true") for: 1 matches predicate: "always true" +Matchers.tests.cpp:: passed: 1, !Predicate(alwaysFalse, "always false") for: 1 not matches predicate: "always false" +Matchers.tests.cpp:: passed: "Hello olleH", Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") for: "Hello olleH" matches predicate: "First and last character should be equal" +Matchers.tests.cpp:: passed: "This wouldn't pass", !Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) for: "This wouldn't pass" not matches undescribed predicate Tricky.tests.cpp:: passed: true Tricky.tests.cpp:: passed: true Tricky.tests.cpp:: passed: true @@ -1040,7 +1040,7 @@ ToStringGeneral.tests.cpp:: passed: str1.size() == 3 + 5 for: 8 == ToStringGeneral.tests.cpp:: passed: str2.size() == 3 + 10 for: 13 == 13 ToStringGeneral.tests.cpp:: passed: str1.size() == 2 + 5 for: 7 == 7 ToStringGeneral.tests.cpp:: passed: str2.size() == 2 + 15 for: 17 == 17 -Matchers.tests.cpp:: passed: "foo", Predicate([] (const char* const&) { return true; }) for: "foo" matches undescribed predicate +Matchers.tests.cpp:: passed: "foo", Predicate([] (const char* const&) { return true; }) for: "foo" matches undescribed predicate CmdLine.tests.cpp:: passed: result for: {?} CmdLine.tests.cpp:: passed: config.processName == "" for: "" == "" CmdLine.tests.cpp:: passed: result for: {?} @@ -1619,6 +1619,7 @@ Misc.tests.cpp:: passed: a != b for: 1 != 2 Misc.tests.cpp:: passed: b != a for: 2 != 1 Misc.tests.cpp:: passed: a != b for: 1 != 2 Tricky.tests.cpp:: passed: s == "7" for: "7" == "7" +Matchers.tests.cpp:: passed: s, Predicate([](S &s){ return s.value() == 42;}) for: {?} matches undescribed predicate Tricky.tests.cpp:: passed: ti == typeid(int) for: {?} == {?} Misc.tests.cpp:: passed: Message.tests.cpp:: passed: true with 1 message: 'this MAY be seen only for the FIRST assertion IF info is printed for passing assertions' diff --git a/tests/SelfTest/Baselines/console.std.approved.txt b/tests/SelfTest/Baselines/console.std.approved.txt index 3ae07e2386..72a7ba12c3 100644 --- a/tests/SelfTest/Baselines/console.std.approved.txt +++ b/tests/SelfTest/Baselines/console.std.approved.txt @@ -1380,6 +1380,6 @@ due to unexpected exception with message: Why would you throw a std::string? =============================================================================== -test cases: 305 | 231 passed | 70 failed | 4 failed as expected -assertions: 1654 | 1502 passed | 131 failed | 21 failed as expected +test cases: 306 | 232 passed | 70 failed | 4 failed as expected +assertions: 1655 | 1503 passed | 131 failed | 21 failed as expected diff --git a/tests/SelfTest/Baselines/console.sw.approved.txt b/tests/SelfTest/Baselines/console.sw.approved.txt index 42f94b55ae..bbdade262b 100644 --- a/tests/SelfTest/Baselines/console.sw.approved.txt +++ b/tests/SelfTest/Baselines/console.sw.approved.txt @@ -1862,12 +1862,12 @@ Matchers.tests.cpp: ............................................................................... Matchers.tests.cpp:: PASSED: - REQUIRE_THAT( 1, Predicate(alwaysTrue, "always true") ) + REQUIRE_THAT( 1, Predicate(alwaysTrue, "always true") ) with expansion: 1 matches predicate: "always true" Matchers.tests.cpp:: PASSED: - REQUIRE_THAT( 1, !Predicate(alwaysFalse, "always false") ) + REQUIRE_THAT( 1, !Predicate(alwaysFalse, "always false") ) with expansion: 1 not matches predicate: "always false" @@ -1879,12 +1879,12 @@ Matchers.tests.cpp: ............................................................................... Matchers.tests.cpp:: PASSED: - REQUIRE_THAT( "Hello olleH", Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") ) + REQUIRE_THAT( "Hello olleH", Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") ) with expansion: "Hello olleH" matches predicate: "First and last character should be equal" Matchers.tests.cpp:: PASSED: - REQUIRE_THAT( "This wouldn't pass", !Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) ) + REQUIRE_THAT( "This wouldn't pass", !Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) ) with expansion: "This wouldn't pass" not matches undescribed predicate @@ -7457,7 +7457,7 @@ Matchers.tests.cpp: ............................................................................... Matchers.tests.cpp:: PASSED: - REQUIRE_THAT( "foo", Predicate([] (const char* const&) { return true; }) ) + REQUIRE_THAT( "foo", Predicate([] (const char* const&) { return true; }) ) with expansion: "foo" matches undescribed predicate @@ -11969,6 +11969,17 @@ Tricky.tests.cpp:: PASSED: with expansion: "7" == "7" +------------------------------------------------------------------------------- +non-const access +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( s, Predicate([](S &s){ return s.value() == 42;}) ) +with expansion: + {?} matches undescribed predicate + ------------------------------------------------------------------------------- non-copyable objects ------------------------------------------------------------------------------- @@ -13216,6 +13227,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 305 | 215 passed | 86 failed | 4 failed as expected -assertions: 1671 | 1502 passed | 148 failed | 21 failed as expected +test cases: 306 | 216 passed | 86 failed | 4 failed as expected +assertions: 1672 | 1503 passed | 148 failed | 21 failed as expected diff --git a/tests/SelfTest/Baselines/junit.sw.approved.txt b/tests/SelfTest/Baselines/junit.sw.approved.txt index 9a04960fa8..06e266ac34 100644 --- a/tests/SelfTest/Baselines/junit.sw.approved.txt +++ b/tests/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -1551,6 +1551,7 @@ Misc.tests.cpp: + diff --git a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt index ca52e7f8e2..48be1891b8 100644 --- a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt +++ b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt @@ -1177,6 +1177,7 @@ with expansion: Matchers.tests.cpp: + diff --git a/tests/SelfTest/Baselines/xml.sw.approved.txt b/tests/SelfTest/Baselines/xml.sw.approved.txt index 934a593bd6..a2a3d34506 100644 --- a/tests/SelfTest/Baselines/xml.sw.approved.txt +++ b/tests/SelfTest/Baselines/xml.sw.approved.txt @@ -2078,7 +2078,7 @@ Nor would this
- 1, Predicate<int>(alwaysTrue, "always true") + 1, Predicate(alwaysTrue, "always true") 1 matches predicate: "always true" @@ -2086,7 +2086,7 @@ Nor would this - 1, !Predicate<int>(alwaysFalse, "always false") + 1, !Predicate(alwaysFalse, "always false") 1 not matches predicate: "always false" @@ -2097,7 +2097,7 @@ Nor would this
- "Hello olleH", Predicate<std::string>( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") + "Hello olleH", Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") "Hello olleH" matches predicate: "First and last character should be equal" @@ -2105,7 +2105,7 @@ Nor would this - "This wouldn't pass", !Predicate<std::string>( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) + "This wouldn't pass", !Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) "This wouldn't pass" not matches undescribed predicate @@ -9398,7 +9398,7 @@ Nor would this - "foo", Predicate<const char*>([] (const char* const&) { return true; }) + "foo", Predicate([] (const char* const&) { return true; }) "foo" matches undescribed predicate @@ -14417,6 +14417,17 @@ loose text artifact + + + + s, Predicate([](S &s){ return s.value() == 42;}) + + + {?} matches undescribed predicate + + + + @@ -15796,7 +15807,7 @@ loose text artifact
- + - + diff --git a/tests/SelfTest/UsageTests/Matchers.tests.cpp b/tests/SelfTest/UsageTests/Matchers.tests.cpp index 130b51b260..689a3c2c5e 100644 --- a/tests/SelfTest/UsageTests/Matchers.tests.cpp +++ b/tests/SelfTest/UsageTests/Matchers.tests.cpp @@ -75,16 +75,16 @@ namespace { namespace MatchersTests { throw DerivedException{}; } - class ExceptionMatcher : public Catch::MatcherBase { + class ExceptionMatcher : public Catch::MatcherBase { int m_expected; public: ExceptionMatcher(int i) : m_expected(i) {} - bool match(SpecialException const &se) const override { + bool match(SpecialException const &se) const { return se.i == m_expected; } - std::string describe() const override { + std::string describe() const { std::ostringstream ss; ss << "special exception has value of " << m_expected; return ss.str(); @@ -477,18 +477,18 @@ namespace { namespace MatchersTests { TEST_CASE("Arbitrary predicate matcher", "[matchers][generic]") { SECTION("Function pointer") { - REQUIRE_THAT(1, Predicate(alwaysTrue, "always true")); - REQUIRE_THAT(1, !Predicate(alwaysFalse, "always false")); + REQUIRE_THAT(1, Predicate(alwaysTrue, "always true")); + REQUIRE_THAT(1, !Predicate(alwaysFalse, "always false")); } SECTION("Lambdas + different type") { REQUIRE_THAT("Hello olleH", - Predicate( + Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") ); REQUIRE_THAT("This wouldn't pass", - !Predicate( + !Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) ); @@ -505,7 +505,7 @@ namespace { namespace MatchersTests { } TEST_CASE("Predicate matcher can accept const char*", "[matchers][compilation]") { - REQUIRE_THAT("foo", Predicate([] (const char* const&) { return true; })); + REQUIRE_THAT("foo", Predicate([] (const char* const&) { return true; })); } TEST_CASE("Vector Approx matcher", "[matchers][approx][vector]") { @@ -553,6 +553,17 @@ namespace { namespace MatchersTests { REQUIRE_THROWS_MATCHES(throwsSpecialException(2), SpecialException, Message("SpecialException::what")); } + TEST_CASE("non-const access") { + struct S{ + int value() { + return 42; + } + }; + + S s; + REQUIRE_THAT(s, Predicate([](S &s){ return s.value() == 42;})); + } + } } // namespace MatchersTests #endif // CATCH_CONFIG_DISABLE_MATCHERS