From 3bc47ea8fc0c767672f8e41103984ccebcdad438 Mon Sep 17 00:00:00 2001 From: Dvir Yitzchaki Date: Wed, 27 Feb 2019 23:01:57 +0200 Subject: [PATCH 1/3] CRTP matchers, Closes #1553 --- .gitignore | 3 + docs/matchers.md | 10 +- src/CMakeLists.txt | 2 - src/catch2/catch_capture_matchers.cpp | 24 -- src/catch2/catch_capture_matchers.h | 24 +- src/catch2/catch_common.h | 17 ++ src/catch2/catch_matchers.cpp | 28 -- src/catch2/catch_matchers.h | 250 +++++++++++------- src/catch2/catch_matchers_exception.hpp | 6 +- src/catch2/catch_matchers_floating.h | 18 +- src/catch2/catch_matchers_generic.hpp | 21 +- src/catch2/catch_matchers_string.cpp | 18 -- src/catch2/catch_matchers_string.h | 49 +++- src/catch2/catch_matchers_vector.h | 30 +-- src/catch2/catch_objc.hpp | 18 +- src/catch2/reporters/catch_reporter_tap.hpp | 2 +- .../reporters/catch_reporter_teamcity.hpp | 2 +- tests/ExtraTests/X01-PrefixedMacros.cpp | 4 +- .../Baselines/automake.sw.approved.txt | 1 + .../Baselines/compact.sw.approved.txt | 11 +- .../Baselines/console.std.approved.txt | 4 +- .../Baselines/console.sw.approved.txt | 25 +- .../SelfTest/Baselines/junit.sw.approved.txt | 3 +- .../Baselines/sonarqube.sw.approved.txt | 1 + tests/SelfTest/Baselines/tap.sw.approved.txt | 14 +- .../Baselines/teamcity.sw.approved.txt | 2 + tests/SelfTest/Baselines/xml.sw.approved.txt | 25 +- tests/SelfTest/UsageTests/Matchers.tests.cpp | 27 +- 28 files changed, 354 insertions(+), 285 deletions(-) delete mode 100644 src/catch2/catch_capture_matchers.cpp delete mode 100644 src/catch2/catch_matchers.cpp 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 5ccdeb69a6..9c48bd1981 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -129,7 +129,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 @@ -153,7 +152,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 3f883a3914..d9945005cf 100644 --- a/src/catch2/catch_capture_matchers.h +++ b/src/catch2/catch_capture_matchers.h @@ -11,18 +11,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 ) {} @@ -37,13 +38,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 86fb2214c5..c4e1fd2284 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>> @@ -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 6484e321e5..159fd97dd0 100644 --- a/src/catch2/catch_objc.hpp +++ b/src/catch2/catch_objc.hpp @@ -108,14 +108,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; } @@ -125,12 +125,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 ); } }; @@ -143,7 +143,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 ); } }; @@ -151,24 +151,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 1ede76fb2c..aee33aea6e 100644 --- a/src/catch2/reporters/catch_reporter_tap.hpp +++ b/src/catch2/reporters/catch_reporter_tap.hpp @@ -24,7 +24,7 @@ namespace Catch { return m_reporterPrefs; } - void noMatchingTestCases(std::string const& spec) override; + void noMatchingTestCases(std::string const& spec); void assertionStarting( AssertionInfo const& ) override {} diff --git a/src/catch2/reporters/catch_reporter_teamcity.hpp b/src/catch2/reporters/catch_reporter_teamcity.hpp index da54c69a7c..77d74f9747 100644 --- a/src/catch2/reporters/catch_reporter_teamcity.hpp +++ b/src/catch2/reporters/catch_reporter_teamcity.hpp @@ -33,7 +33,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; void testGroupEnded(TestGroupStats const& testGroupStats) override; diff --git a/tests/ExtraTests/X01-PrefixedMacros.cpp b/tests/ExtraTests/X01-PrefixedMacros.cpp index 40b9897806..e7958bb4ca 100644 --- a/tests/ExtraTests/X01-PrefixedMacros.cpp +++ b/tests/ExtraTests/X01-PrefixedMacros.cpp @@ -26,7 +26,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 ); @@ -42,7 +42,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/automake.sw.approved.txt b/tests/SelfTest/Baselines/automake.sw.approved.txt index 035a92f2f2..7169655cf3 100644 --- a/tests/SelfTest/Baselines/automake.sw.approved.txt +++ b/tests/SelfTest/Baselines/automake.sw.approved.txt @@ -260,6 +260,7 @@ loose text artifact :test-result: FAIL more nested SECTION tests :test-result: PASS nested SECTION tests :test-result: PASS non streamable - with conv. op +:test-result: PASS non-const access :test-result: PASS non-copyable objects :test-result: PASS normal_cdf :test-result: PASS normal_quantile diff --git a/tests/SelfTest/Baselines/compact.sw.approved.txt b/tests/SelfTest/Baselines/compact.sw.approved.txt index 5f9e6a9fb0..b8ef5e8400 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: {?} @@ -1692,6 +1692,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: {?} == {?} InternalBenchmark.tests.cpp:: passed: normal_cdf(0.000000) == Approx(0.50000000000000000) for: 0.5 == Approx( 0.5 ) InternalBenchmark.tests.cpp:: passed: normal_cdf(1.000000) == Approx(0.84134474606854293) for: 0.8413447461 == Approx( 0.8413447461 ) diff --git a/tests/SelfTest/Baselines/console.std.approved.txt b/tests/SelfTest/Baselines/console.std.approved.txt index 140a0b8a1b..b5dd9229f0 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: 320 | 246 passed | 70 failed | 4 failed as expected -assertions: 1776 | 1624 passed | 131 failed | 21 failed as expected +test cases: 321 | 247 passed | 70 failed | 4 failed as expected +assertions: 1777 | 1625 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 ea8481d2cf..b6c2d54521 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 @@ -12420,6 +12420,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 ------------------------------------------------------------------------------- @@ -13960,6 +13971,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 320 | 230 passed | 86 failed | 4 failed as expected -assertions: 1793 | 1624 passed | 148 failed | 21 failed as expected +test cases: 321 | 231 passed | 86 failed | 4 failed as expected +assertions: 1794 | 1625 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 336b7a79c5..59e1200f2c 100644 --- a/tests/SelfTest/Baselines/junit.sw.approved.txt +++ b/tests/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -1564,6 +1564,7 @@ Misc.tests.cpp: + diff --git a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt index b54c147bb3..09ed3f70b2 100644 --- a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt +++ b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt @@ -1200,6 +1200,7 @@ with expansion: Matchers.tests.cpp: + diff --git a/tests/SelfTest/Baselines/tap.sw.approved.txt b/tests/SelfTest/Baselines/tap.sw.approved.txt index 1e679cec1c..aef42abe3e 100644 --- a/tests/SelfTest/Baselines/tap.sw.approved.txt +++ b/tests/SelfTest/Baselines/tap.sw.approved.txt @@ -467,13 +467,13 @@ ok {test-number} - 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 ) # Approximate comparisons with mixed numeric types ok {test-number} - dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 ) # Arbitrary predicate matcher -ok {test-number} - 1, Predicate(alwaysTrue, "always true") for: 1 matches predicate: "always true" +ok {test-number} - 1, Predicate(alwaysTrue, "always true") for: 1 matches predicate: "always true" # Arbitrary predicate matcher -ok {test-number} - 1, !Predicate(alwaysFalse, "always false") for: 1 not matches predicate: "always false" +ok {test-number} - 1, !Predicate(alwaysFalse, "always false") for: 1 not matches predicate: "always false" # Arbitrary predicate matcher -ok {test-number} - "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" +ok {test-number} - "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" # Arbitrary predicate matcher -ok {test-number} - "This wouldn't pass", !Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) for: "This wouldn't pass" not matches undescribed predicate +ok {test-number} - "This wouldn't pass", !Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) for: "This wouldn't pass" not matches undescribed predicate # Assertions then sections ok {test-number} - true # Assertions then sections @@ -2003,7 +2003,7 @@ ok {test-number} - str1.size() == 2 + 5 for: 7 == 7 # Precision of floating point stringification can be set ok {test-number} - str2.size() == 2 + 15 for: 17 == 17 # Predicate matcher can accept const char* -ok {test-number} - "foo", Predicate([] (const char* const&) { return true; }) for: "foo" matches undescribed predicate +ok {test-number} - "foo", Predicate([] (const char* const&) { return true; }) for: "foo" matches undescribed predicate # Process can be configured on command line ok {test-number} - result for: {?} # Process can be configured on command line @@ -3214,6 +3214,8 @@ ok {test-number} - b != a for: 2 != 1 ok {test-number} - a != b for: 1 != 2 # non streamable - with conv. op ok {test-number} - s == "7" for: "7" == "7" +# non-const access +ok {test-number} - s, Predicate([](S &s){ return s.value() == 42;}) for: {?} matches undescribed predicate # non-copyable objects ok {test-number} - ti == typeid(int) for: {?} == {?} # normal_cdf @@ -3578,5 +3580,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..1785 +1..1786 diff --git a/tests/SelfTest/Baselines/teamcity.sw.approved.txt b/tests/SelfTest/Baselines/teamcity.sw.approved.txt index 76d6218306..fe61882124 100644 --- a/tests/SelfTest/Baselines/teamcity.sw.approved.txt +++ b/tests/SelfTest/Baselines/teamcity.sw.approved.txt @@ -656,6 +656,8 @@ Misc.tests.cpp:|nexpression failed|n REQUIRE( a == b )|nwith expan ##teamcity[testFinished name='nested SECTION tests' duration="{duration}"] ##teamcity[testStarted name='non streamable - with conv. op'] ##teamcity[testFinished name='non streamable - with conv. op' duration="{duration}"] +##teamcity[testStarted name='non-const access'] +##teamcity[testFinished name='non-const access' duration="{duration}"] ##teamcity[testStarted name='non-copyable objects'] ##teamcity[testFinished name='non-copyable objects' duration="{duration}"] ##teamcity[testStarted name='normal_cdf'] diff --git a/tests/SelfTest/Baselines/xml.sw.approved.txt b/tests/SelfTest/Baselines/xml.sw.approved.txt index a27f88e582..60bbaa6187 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 @@ -15046,6 +15046,17 @@ loose text artifact + + + + s, Predicate([](S &s){ return s.value() == 42;}) + + + {?} matches undescribed predicate + + + + @@ -16841,7 +16852,7 @@ loose text artifact
- + - + diff --git a/tests/SelfTest/UsageTests/Matchers.tests.cpp b/tests/SelfTest/UsageTests/Matchers.tests.cpp index 0efc922158..0da3d85a67 100644 --- a/tests/SelfTest/UsageTests/Matchers.tests.cpp +++ b/tests/SelfTest/UsageTests/Matchers.tests.cpp @@ -76,16 +76,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(); @@ -478,18 +478,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(); } ) ); @@ -506,7 +506,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]") { @@ -554,6 +554,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 #ifdef __clang__ From 6cf1e3d3880bb2439815968b268df35e1f32c840 Mon Sep 17 00:00:00 2001 From: Dvir Yitzchaki Date: Sun, 9 Feb 2020 21:09:26 +0200 Subject: [PATCH 2/3] rename generic matcher to MatcherBaseGeneirc and restore MatcherBase for back compat --- docs/matchers.md | 4 +- src/catch2/catch_capture_matchers.h | 2 +- src/catch2/catch_matchers.h | 57 ++-- src/catch2/catch_matchers_exception.hpp | 2 +- src/catch2/catch_matchers_floating.h | 6 +- src/catch2/catch_matchers_generic.hpp | 14 +- src/catch2/catch_matchers_string.h | 4 +- src/catch2/catch_matchers_vector.h | 10 +- .../reporters/catch_reporter_teamcity.hpp | 2 +- .../Baselines/automake.sw.approved.txt | 9 + .../Baselines/compact.sw.approved.txt | 30 ++ .../Baselines/console.std.approved.txt | 4 +- .../Baselines/console.sw.approved.txt | 192 ++++++++++++- .../SelfTest/Baselines/junit.sw.approved.txt | 11 +- .../Baselines/sonarqube.sw.approved.txt | 9 + tests/SelfTest/Baselines/tap.sw.approved.txt | 76 ++++- .../Baselines/teamcity.sw.approved.txt | 18 ++ tests/SelfTest/Baselines/xml.sw.approved.txt | 183 ++++++++++++- tests/SelfTest/UsageTests/Matchers.tests.cpp | 259 +++++++++++++++++- 19 files changed, 841 insertions(+), 51 deletions(-) diff --git a/docs/matchers.md b/docs/matchers.md index 91fff807b2..8a6d979117 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -147,7 +147,7 @@ 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 matcher class. +1. A matcher class, derived from `Catch::MatcherBaseGeneric` - where `T` is the matcher class. The constructor takes and stores any arguments needed (e.g. something to compare against) and you must provide two methods: `match()` and `describe()`. 2. A simple builder function. This is what is actually called from the test code and allows overloading. @@ -157,7 +157,7 @@ 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::MatcherBaseGeneric { int m_begin, m_end; public: IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {} diff --git a/src/catch2/catch_capture_matchers.h b/src/catch2/catch_capture_matchers.h index d9945005cf..d96126d91b 100644 --- a/src/catch2/catch_capture_matchers.h +++ b/src/catch2/catch_capture_matchers.h @@ -42,7 +42,7 @@ namespace Catch { // 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> + typename = typename std::enable_if, StringMatcher>::value>::type> void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { std::string exceptionMessage = Catch::translateActiveException(); MatchExpr expr( exceptionMessage, matcher, matcherString ); diff --git a/src/catch2/catch_matchers.h b/src/catch2/catch_matchers.h index 67926d6437..bccbc752c2 100644 --- a/src/catch2/catch_matchers.h +++ b/src/catch2/catch_matchers.h @@ -22,11 +22,11 @@ namespace Matchers { template struct MatchNotOf; template - class MatcherBase { + class MatcherBaseGeneric { public: - MatcherBase() = default; - MatcherBase ( MatcherBase const& ) = default; - MatcherBase& operator = ( MatcherBase const& ) = delete; + MatcherBaseGeneric() = default; + MatcherBaseGeneric ( MatcherBaseGeneric const& ) = default; + MatcherBaseGeneric& operator = ( MatcherBaseGeneric const& ) = delete; std::string toString() const; template @@ -44,17 +44,14 @@ namespace Matchers { }; template - std::string MatcherBase::toString() const { + std::string MatcherBaseGeneric::toString() const { if( m_cachedToString.empty() ) m_cachedToString = derived().describe(); return m_cachedToString; } template - using Tuple = std::tuple::type>::type ...>; - - template - struct MatchAllOf : MatcherBase> { + struct MatchAllOf : MatcherBaseGeneric> { MatchAllOf( Matchers const &... matchers ) : m_matchers{matchers...} {} @@ -112,11 +109,11 @@ namespace Matchers { return and_helper(other, makeIndexSequence()); } - Tuple m_matchers; + std::tuple m_matchers; }; template - struct MatchAnyOf : MatcherBase> { + struct MatchAnyOf : MatcherBaseGeneric> { MatchAnyOf( Matchers const &... matchers ) : m_matchers{matchers...} {} @@ -174,11 +171,11 @@ namespace Matchers { return or_helper(other, makeIndexSequence()); } - Tuple m_matchers; + std::tuple m_matchers; }; template - struct MatchNotOf : MatcherBase> { + struct MatchNotOf : MatcherBaseGeneric> { MatchNotOf( Matcher const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} @@ -195,26 +192,54 @@ namespace Matchers { template template - MatchAllOf MatcherBase::operator && ( Other const& other ) const { + MatchAllOf MatcherBaseGeneric::operator && ( Other const& other ) const { return {derived(), other}; } template template - MatchAnyOf MatcherBase::operator || ( Other const& other ) const { + MatchAnyOf MatcherBaseGeneric::operator || ( Other const& other ) const { return {derived(), other}; } template - MatchNotOf MatcherBase::operator ! () const { + MatchNotOf MatcherBaseGeneric::operator ! () const { return {derived()}; } + template + Derived const& operator ! (MatchNotOf const& matcher) { + return matcher.m_underlyingMatcher; + } + + + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + +#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 + + // for backward compatibility + template + struct MatcherBase : MatcherBaseGeneric>, MatcherMethod { + virtual ~MatcherBase() = default; + virtual std::string describe() const = 0; + }; + } // namespace Impl } // namespace Matchers using namespace Matchers; +using Matchers::Impl::MatcherBaseGeneric; using Matchers::Impl::MatcherBase; } // namespace Catch diff --git a/src/catch2/catch_matchers_exception.hpp b/src/catch2/catch_matchers_exception.hpp index dc30542d3d..38696babd7 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 MatcherBaseGeneric { std::string m_message; public: diff --git a/src/catch2/catch_matchers_floating.h b/src/catch2/catch_matchers_floating.h index 0bf8e7e650..6057b69311 100644 --- a/src/catch2/catch_matchers_floating.h +++ b/src/catch2/catch_matchers_floating.h @@ -16,7 +16,7 @@ namespace Matchers { enum class FloatingPointKind : uint8_t; - struct WithinAbsMatcher : MatcherBase { + struct WithinAbsMatcher : MatcherBaseGeneric { WithinAbsMatcher(double target, double margin); bool match(double const& matchee) const; std::string describe() const; @@ -25,7 +25,7 @@ namespace Matchers { double m_margin; }; - struct WithinUlpsMatcher : MatcherBase { + struct WithinUlpsMatcher : MatcherBaseGeneric { WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType); bool match(double const& matchee) const; std::string describe() const; @@ -41,7 +41,7 @@ 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 : MatcherBaseGeneric { WithinRelMatcher(double target, double epsilon); bool match(double const& matchee) const; std::string describe() const; diff --git a/src/catch2/catch_matchers_generic.hpp b/src/catch2/catch_matchers_generic.hpp index a85955bd69..903b9b62fc 100644 --- a/src/catch2/catch_matchers_generic.hpp +++ b/src/catch2/catch_matchers_generic.hpp @@ -23,13 +23,14 @@ namespace Detail { } template -class PredicateMatcher : public MatcherBase> { +class PredicateMatcher : public MatcherBaseGeneric> { Predicate m_predicate; std::string m_description; public: - PredicateMatcher(Predicate&& elem, std::string const& descr) - :m_predicate(std::forward(elem)), + template + PredicateMatcher(_Predicate&& elem, std::string const& descr) + :m_predicate(std::forward<_Predicate>(elem)), m_description(Detail::finalizeDescription(descr)) {} @@ -51,6 +52,13 @@ 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); diff --git a/src/catch2/catch_matchers_string.h b/src/catch2/catch_matchers_string.h index 51ac62223a..8225859427 100644 --- a/src/catch2/catch_matchers_string.h +++ b/src/catch2/catch_matchers_string.h @@ -28,7 +28,7 @@ namespace Matchers { }; template - struct StringMatcherBase : MatcherBase { + struct StringMatcherBase : MatcherBaseGeneric { StringMatcherBase( std::string const& operation, CasedString const& comparator ); std::string describe() const ; @@ -73,7 +73,7 @@ namespace Matchers { bool match( std::string const& source ) const ; }; - struct RegexMatcher : MatcherBase { + struct RegexMatcher : MatcherBaseGeneric { RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); bool match( std::string const& matchee ) const ; std::string describe() const ; diff --git a/src/catch2/catch_matchers_vector.h b/src/catch2/catch_matchers_vector.h index c4e1fd2284..a987ec2923 100644 --- a/src/catch2/catch_matchers_vector.h +++ b/src/catch2/catch_matchers_vector.h @@ -18,7 +18,7 @@ namespace Matchers { namespace Vector { template - struct ContainsElementMatcher : MatcherBase> { + struct ContainsElementMatcher : MatcherBaseGeneric> { ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} @@ -39,7 +39,7 @@ namespace Matchers { }; template - struct ContainsMatcher : MatcherBase> { + struct ContainsMatcher : MatcherBaseGeneric> { ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} @@ -69,7 +69,7 @@ namespace Matchers { }; template - struct EqualsMatcher : MatcherBase> { + struct EqualsMatcher : MatcherBaseGeneric> { EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} @@ -92,7 +92,7 @@ namespace Matchers { }; template - struct ApproxMatcher : MatcherBase> { + struct ApproxMatcher : MatcherBaseGeneric> { ApproxMatcher(std::vector const& comparator) : m_comparator( comparator ) {} @@ -128,7 +128,7 @@ namespace Matchers { }; template - struct UnorderedEqualsMatcher : MatcherBase> { + struct UnorderedEqualsMatcher : MatcherBaseGeneric> { UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} bool match(std::vector const& vec) const { // Note: This is a reimplementation of std::is_permutation, diff --git a/src/catch2/reporters/catch_reporter_teamcity.hpp b/src/catch2/reporters/catch_reporter_teamcity.hpp index 77d74f9747..42f9518fd0 100644 --- a/src/catch2/reporters/catch_reporter_teamcity.hpp +++ b/src/catch2/reporters/catch_reporter_teamcity.hpp @@ -33,7 +33,7 @@ namespace Catch { void skipTest( TestCaseInfo const& /* testInfo */ ) override {} - void nomatchingTestCases( std::string const& /* spec */ ) {} + void noMatchingTestCases( std::string const& /* spec */ ) {} void testGroupStarting(GroupInfo const& groupInfo) override; void testGroupEnded(TestGroupStats const& testGroupStats) override; diff --git a/tests/SelfTest/Baselines/automake.sw.approved.txt b/tests/SelfTest/Baselines/automake.sw.approved.txt index 7169655cf3..42b5ac6e0f 100644 --- a/tests/SelfTest/Baselines/automake.sw.approved.txt +++ b/tests/SelfTest/Baselines/automake.sw.approved.txt @@ -85,6 +85,12 @@ Nor would this :test-result: PASS CAPTURE parses string and character constants :test-result: PASS Capture and info messages :test-result: PASS Character pretty printing +:test-result: PASS Combining MatchAllOf does not nest +:test-result: PASS Combining MatchAnyOf does not nest +:test-result: PASS Combining MatchNotOf does not nest +:test-result: PASS Combining generic and non-generic matchers +:test-result: PASS Combining generic matchers +:test-result: PASS Combining only templated matchers :test-result: PASS Commas in various macros are allowed :test-result: PASS Comparing function pointers :test-result: PASS Comparison ops @@ -127,10 +133,12 @@ Nor would this :test-result: FAIL INFO gets logged on failure :test-result: FAIL INFO gets logged on failure, even if captured before successful assertions :test-result: FAIL INFO is reset for each loop +:test-result: PASS Immovable matchers can be used :test-result: XFAIL Inequality checks that should fail :test-result: PASS Inequality checks that should succeed :test-result: PASS Less-than inequalities with different epsilons :test-result: PASS ManuallyRegistered +:test-result: PASS Matchers are not moved or copied :test-result: PASS Matchers can be (AllOf) composed with the && operator :test-result: PASS Matchers can be (AnyOf) composed with the || operator :test-result: PASS Matchers can be composed with both && and || @@ -147,6 +155,7 @@ Nor would this :test-result: PASS Ordering comparison checks that should succeed :test-result: PASS Our PCG implementation provides expected results for known seeds :test-result: FAIL Output from all sections is reported +:test-result: PASS Overloaded comma or address-of operators are not used :test-result: PASS Parse test names and tags :test-result: PASS Pointers can be compared to null :test-result: PASS Precision of floating point stringification can be set diff --git a/tests/SelfTest/Baselines/compact.sw.approved.txt b/tests/SelfTest/Baselines/compact.sw.approved.txt index b8ef5e8400..2b37612223 100644 --- a/tests/SelfTest/Baselines/compact.sw.approved.txt +++ b/tests/SelfTest/Baselines/compact.sw.approved.txt @@ -263,6 +263,30 @@ ToStringGeneral.tests.cpp:: passed: c == i for: 2 == 2 ToStringGeneral.tests.cpp:: passed: c == i for: 3 == 3 ToStringGeneral.tests.cpp:: passed: c == i for: 4 == 4 ToStringGeneral.tests.cpp:: passed: c == i for: 5 == 5 +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, MatcherA() && MatcherB() && MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 ) +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, MatcherA() && MatcherB() && MatcherC() && MatcherD() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true ) +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, MatcherA() || MatcherB() || MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 ) +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, MatcherA() || MatcherB() || MatcherC() || MatcherD() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true ) +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 0, !MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, !!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 0, !!!MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, !!!!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f +Matchers.tests.cpp:: passed: vec, AllOf([](int elem) { return elem % 2 == 1; }, "odd") && !EqualsRange(a) for: { 1, 3, 5 } ( all of are odd and not Equals: { 5, 3, 1 } ) +Matchers.tests.cpp:: passed: container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) for: { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } ) +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, MatcherA() || MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 ) +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, MatcherA() && MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 ) +Matchers.tests.cpp:: passed: with 1 message: 'std::is_same::value' +Matchers.tests.cpp:: passed: 1, MatcherA() || !MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 ) Tricky.tests.cpp:: passed: std::vector{constructor_throws{}, constructor_throws{}} Tricky.tests.cpp:: passed: std::vector{constructor_throws{}, constructor_throws{}} Tricky.tests.cpp:: passed: std::vector{1, 2, 3} == std::vector{1, 2, 3} @@ -747,6 +771,7 @@ Message.tests.cpp:: passed: i < 10 for: 7 < 10 with 2 messages: 'cu Message.tests.cpp:: passed: i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and 'i := 8' Message.tests.cpp:: passed: i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' Message.tests.cpp:: failed: i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +Matchers.tests.cpp:: passed: 123, (ImmovableMatcher() && ImmovableMatcher()) || !ImmovableMatcher() for: 123 ( ( always false and always false ) or not always false ) Condition.tests.cpp:: failed: data.int_seven != 7 for: 7 != 7 Condition.tests.cpp:: failed: data.float_nine_point_one != Approx( 9.1f ) for: 9.1f != Approx( 9.1000003815 ) Condition.tests.cpp:: failed: data.double_pi != Approx( 3.1415926535 ) for: 3.1415926535 != Approx( 3.1415926535 ) @@ -768,6 +793,7 @@ Approx.tests.cpp:: passed: d <= Approx( 1.23 ) for: 1.23 <= Approx( Approx.tests.cpp:: passed: !(d <= Approx( 1.22 )) for: !(1.23 <= Approx( 1.22 )) Approx.tests.cpp:: passed: d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 ) Misc.tests.cpp:: passed: with 1 message: 'was called' +Matchers.tests.cpp:: passed: (ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher() Matchers.tests.cpp:: passed: testStringForMatching(), Contains("string") && Contains("abc") && Contains("substring") && Contains("contains") for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" ) Matchers.tests.cpp:: passed: testStringForMatching(), Contains("string") || Contains("different") || Contains("random") for: "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" ) Matchers.tests.cpp:: passed: testStringForMatching2(), Contains("string") || Contains("different") || Contains("random") for: "some completely different text that contains one common word" ( contains: "string" or contains: "different" or contains: "random" ) @@ -883,6 +909,10 @@ RandomNumberGeneration.tests.cpp:: passed: rng() == 0x 4261393167 (0x) Message.tests.cpp:: failed: explicitly with 1 message: 'Message from section one' Message.tests.cpp:: failed: explicitly with 1 message: 'Message from section two' +Matchers.tests.cpp:: passed: (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed +Matchers.tests.cpp:: passed: &EvilMatcher(), EvilAddressOfOperatorUsed +Matchers.tests.cpp:: passed: EvilMatcher() || EvilMatcher() && !EvilMatcher() +Matchers.tests.cpp:: passed: (EvilMatcher() && EvilMatcher()) || !EvilMatcher() CmdLine.tests.cpp:: passed: spec.hasFilters() == false for: false == false CmdLine.tests.cpp:: passed: spec.matches( *tcA ) == false for: false == false CmdLine.tests.cpp:: passed: spec.matches( *tcB ) == false for: false == false diff --git a/tests/SelfTest/Baselines/console.std.approved.txt b/tests/SelfTest/Baselines/console.std.approved.txt index b5dd9229f0..efe0d54577 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: 321 | 247 passed | 70 failed | 4 failed as expected -assertions: 1777 | 1625 passed | 131 failed | 21 failed as expected +test cases: 330 | 256 passed | 70 failed | 4 failed as expected +assertions: 1807 | 1655 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 b6c2d54521..ae2a8d9239 100644 --- a/tests/SelfTest/Baselines/console.sw.approved.txt +++ b/tests/SelfTest/Baselines/console.sw.approved.txt @@ -2116,6 +2116,156 @@ ToStringGeneral.tests.cpp:: PASSED: with expansion: 5 == 5 +------------------------------------------------------------------------------- +Combining MatchAllOf does not nest +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, MatcherA() && MatcherB() && MatcherC() ) +with expansion: + 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) + 1 ) + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, MatcherA() && MatcherB() && MatcherC() && MatcherD() ) +with expansion: + 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) + 1 and equals: true ) + +------------------------------------------------------------------------------- +Combining MatchAnyOf does not nest +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, MatcherA() || MatcherB() || MatcherC() ) +with expansion: + 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 + ) + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, MatcherA() || MatcherB() || MatcherC() || MatcherD() ) +with expansion: + 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 + or equals: true ) + +------------------------------------------------------------------------------- +Combining MatchNotOf does not nest +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 0, !MatcherA() ) +with expansion: + 0 not equals: (int) 1 or (float) 1.0f + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, !!MatcherA() ) +with expansion: + 1 equals: (int) 1 or (float) 1.0f + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 0, !!!MatcherA() ) +with expansion: + 0 not equals: (int) 1 or (float) 1.0f + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, !!!!MatcherA() ) +with expansion: + 1 equals: (int) 1 or (float) 1.0f + +------------------------------------------------------------------------------- +Combining generic and non-generic matchers +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( vec, AllOf([](int elem) { return elem % 2 == 1; }, "odd") && !EqualsRange(a) ) +with expansion: + { 1, 3, 5 } ( all of are odd and not Equals: { 5, 3, 1 } ) + +------------------------------------------------------------------------------- +Combining generic matchers +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) ) +with expansion: + { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 + } ) + +------------------------------------------------------------------------------- +Combining only templated matchers +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, MatcherA() || MatcherB() ) +with expansion: + 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 ) + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, MatcherA() && MatcherB() ) +with expansion: + 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 ) + +Matchers.tests.cpp:: PASSED: +with message: + std::is_same::value + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 1, MatcherA() || !MatcherB() ) +with expansion: + 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 ) + ------------------------------------------------------------------------------- Commas in various macros are allowed ------------------------------------------------------------------------------- @@ -5628,6 +5778,17 @@ with messages: current counter 10 i := 10 +------------------------------------------------------------------------------- +Immovable matchers can be used +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THAT( 123, (ImmovableMatcher() && ImmovableMatcher()) || !ImmovableMatcher() ) +with expansion: + 123 ( ( always false and always false ) or not always false ) + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -5756,6 +5917,15 @@ Misc.tests.cpp:: PASSED: with message: was called +------------------------------------------------------------------------------- +Matchers are not moved or copied +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_NOTHROW( (ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher() ) + ------------------------------------------------------------------------------- Matchers can be (AllOf) composed with the && operator ------------------------------------------------------------------------------- @@ -6414,6 +6584,24 @@ Message.tests.cpp:: FAILED: explicitly with message: Message from section two +------------------------------------------------------------------------------- +Overloaded comma or address-of operators are not used +------------------------------------------------------------------------------- +Matchers.tests.cpp: +............................................................................... + +Matchers.tests.cpp:: PASSED: + REQUIRE_THROWS_AS( (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed ) + +Matchers.tests.cpp:: PASSED: + REQUIRE_THROWS_AS( &EvilMatcher(), EvilAddressOfOperatorUsed ) + +Matchers.tests.cpp:: PASSED: + REQUIRE_NOTHROW( EvilMatcher() || EvilMatcher() && !EvilMatcher() ) + +Matchers.tests.cpp:: PASSED: + REQUIRE_NOTHROW( (EvilMatcher() && EvilMatcher()) || !EvilMatcher() ) + ------------------------------------------------------------------------------- Parse test names and tags Empty test spec should have no filters @@ -13971,6 +14159,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 321 | 231 passed | 86 failed | 4 failed as expected -assertions: 1794 | 1625 passed | 148 failed | 21 failed as expected +test cases: 330 | 240 passed | 86 failed | 4 failed as expected +assertions: 1824 | 1655 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 59e1200f2c..e2d9a6e1e4 100644 --- a/tests/SelfTest/Baselines/junit.sw.approved.txt +++ b/tests/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -350,6 +350,12 @@ Exception.tests.cpp: + + + + + + @@ -715,6 +721,7 @@ i := 10 Message.tests.cpp: + FAILED: @@ -755,6 +762,7 @@ Condition.tests.cpp: + @@ -950,6 +958,7 @@ Message from section two Message.tests.cpp: + diff --git a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt index 09ed3f70b2..82013eb5f1 100644 --- a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt +++ b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt @@ -912,6 +912,12 @@ Exception.tests.cpp: + + + + + + FAILED: @@ -1017,6 +1023,8 @@ Matchers.tests.cpp: + + @@ -1039,6 +1047,7 @@ with expansion: Matchers.tests.cpp: + diff --git a/tests/SelfTest/Baselines/tap.sw.approved.txt b/tests/SelfTest/Baselines/tap.sw.approved.txt index aef42abe3e..9051d4dc9c 100644 --- a/tests/SelfTest/Baselines/tap.sw.approved.txt +++ b/tests/SelfTest/Baselines/tap.sw.approved.txt @@ -524,6 +524,54 @@ ok {test-number} - c == i for: 3 == 3 ok {test-number} - c == i for: 4 == 4 # Character pretty printing ok {test-number} - c == i for: 5 == 5 +# Combining MatchAllOf does not nest +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining MatchAllOf does not nest +ok {test-number} - 1, MatcherA() && MatcherB() && MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 ) +# Combining MatchAllOf does not nest +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining MatchAllOf does not nest +ok {test-number} - 1, MatcherA() && MatcherB() && MatcherC() && MatcherD() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true ) +# Combining MatchAnyOf does not nest +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining MatchAnyOf does not nest +ok {test-number} - 1, MatcherA() || MatcherB() || MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 ) +# Combining MatchAnyOf does not nest +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining MatchAnyOf does not nest +ok {test-number} - 1, MatcherA() || MatcherB() || MatcherC() || MatcherD() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true ) +# Combining MatchNotOf does not nest +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining MatchNotOf does not nest +ok {test-number} - 0, !MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f +# Combining MatchNotOf does not nest +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining MatchNotOf does not nest +ok {test-number} - 1, !!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f +# Combining MatchNotOf does not nest +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining MatchNotOf does not nest +ok {test-number} - 0, !!!MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f +# Combining MatchNotOf does not nest +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining MatchNotOf does not nest +ok {test-number} - 1, !!!!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f +# Combining generic and non-generic matchers +ok {test-number} - vec, AllOf([](int elem) { return elem % 2 == 1; }, "odd") && !EqualsRange(a) for: { 1, 3, 5 } ( all of are odd and not Equals: { 5, 3, 1 } ) +# Combining generic matchers +ok {test-number} - container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) for: { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } ) +# Combining only templated matchers +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining only templated matchers +ok {test-number} - 1, MatcherA() || MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 ) +# Combining only templated matchers +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining only templated matchers +ok {test-number} - 1, MatcherA() && MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 ) +# Combining only templated matchers +ok {test-number} - with 1 message: 'std::is_same::value' +# Combining only templated matchers +ok {test-number} - 1, MatcherA() || !MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 ) # Commas in various macros are allowed ok {test-number} - std::vector{constructor_throws{}, constructor_throws{}} # Commas in various macros are allowed @@ -795,7 +843,7 @@ not ok {test-number} - explicitly # FAIL_CHECK does not abort the test not ok {test-number} - explicitly with 1 message: 'This is a failure' # FAIL_CHECK does not abort the test -warning 397 - 'This message appears in the output' +warning 421 - 'This message appears in the output' # Factorials are computed ok {test-number} - Factorial(0) == 1 for: 1 == 1 # Factorials are computed @@ -1443,7 +1491,7 @@ ok {test-number} - !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 )) # Greater-than inequalities with different epsilons ok {test-number} - d >= Approx( 1.24 ).epsilon(0.1) for: 1.23 >= Approx( 1.24 ) # INFO and WARN do not abort tests -warning 721 - 'this is a message' with 1 message: 'this is a warning' +warning 745 - 'this is a message' with 1 message: 'this is a warning' # INFO gets logged on failure not ok {test-number} - a == 1 for: 2 == 1 with 2 messages: 'this message should be logged' and 'so should this' # INFO gets logged on failure, even if captured before successful assertions @@ -1476,6 +1524,8 @@ ok {test-number} - i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and ' ok {test-number} - i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' # INFO is reset for each loop not ok {test-number} - i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +# Immovable matchers can be used +ok {test-number} - 123, (ImmovableMatcher() && ImmovableMatcher()) || !ImmovableMatcher() for: 123 ( ( always false and always false ) or not always false ) # Inequality checks that should fail not ok {test-number} - data.int_seven != 7 for: 7 != 7 # Inequality checks that should fail @@ -1518,6 +1568,8 @@ ok {test-number} - !(d <= Approx( 1.22 )) for: !(1.23 <= Approx( 1.22 )) ok {test-number} - d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 ) # ManuallyRegistered ok {test-number} - with 1 message: 'was called' +# Matchers are not moved or copied +ok {test-number} - (ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher() # Matchers can be (AllOf) composed with the && operator ok {test-number} - testStringForMatching(), Contains("string") && Contains("abc") && Contains("substring") && Contains("contains") for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" ) # Matchers can be (AnyOf) composed with the || operator @@ -1569,7 +1621,7 @@ ok {test-number} - values > -6 for: 98 > -6 # Nested generators and captured variables ok {test-number} - values > -6 for: 99 > -6 # Nice descriptive name -warning 784 - 'This one ran' +warning 810 - 'This one ran' # Non-std exceptions can be translated not ok {test-number} - unexpected exception with message: 'custom exception' # Objects that evaluated in boolean contexts can be checked @@ -1688,6 +1740,14 @@ ok {test-number} - rng() == 0x for: 4261393167 (0x) == 4 not ok {test-number} - explicitly with 1 message: 'Message from section one' # Output from all sections is reported not ok {test-number} - explicitly with 1 message: 'Message from section two' +# Overloaded comma or address-of operators are not used +ok {test-number} - (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed +# Overloaded comma or address-of operators are not used +ok {test-number} - &EvilMatcher(), EvilAddressOfOperatorUsed +# Overloaded comma or address-of operators are not used +ok {test-number} - EvilMatcher() || EvilMatcher() && !EvilMatcher() +# Overloaded comma or address-of operators are not used +ok {test-number} - (EvilMatcher() && EvilMatcher()) || !EvilMatcher() # Parse test names and tags ok {test-number} - spec.hasFilters() == false for: false == false # Parse test names and tags @@ -2928,9 +2988,9 @@ not ok {test-number} - unexpected exception with message: 'expected exception'; # When unchecked exceptions are thrown from sections they are always failures not ok {test-number} - unexpected exception with message: 'unexpected exception' # Where the LHS is not a simple value -warning 1461 - 'Uncomment the code in this test to check that it gives a sensible compiler error' +warning 1491 - 'Uncomment the code in this test to check that it gives a sensible compiler error' # Where there is more to the expression after the RHS -warning 1462 - 'Uncomment the code in this test to check that it gives a sensible compiler error' +warning 1492 - 'Uncomment the code in this test to check that it gives a sensible compiler error' # X/level/0/a ok {test-number} - # X/level/0/b @@ -3197,9 +3257,9 @@ ok {test-number} - s.result == 17 for: 17 == 17 # measure ok {test-number} - s.iterations == 1 for: 1 == 1 # mix info, unscoped info and warning -warning 1595 - 'info' with 2 messages: 'unscoped info' and 'and warn may mix' +warning 1625 - 'info' with 2 messages: 'unscoped info' and 'and warn may mix' # mix info, unscoped info and warning -warning 1596 - 'info' with 2 messages: 'unscoped info' and 'they are not cleared after warnings' +warning 1626 - 'info' with 2 messages: 'unscoped info' and 'they are not cleared after warnings' # more nested SECTION tests not ok {test-number} - a == b for: 1 == 2 # more nested SECTION tests @@ -3580,5 +3640,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..1786 +1..1816 diff --git a/tests/SelfTest/Baselines/teamcity.sw.approved.txt b/tests/SelfTest/Baselines/teamcity.sw.approved.txt index fe61882124..ffa8343877 100644 --- a/tests/SelfTest/Baselines/teamcity.sw.approved.txt +++ b/tests/SelfTest/Baselines/teamcity.sw.approved.txt @@ -203,6 +203,18 @@ Exception.tests.cpp:|nunexpected exception with message:|n "unexpe ##teamcity[testFinished name='Capture and info messages' duration="{duration}"] ##teamcity[testStarted name='Character pretty printing'] ##teamcity[testFinished name='Character pretty printing' duration="{duration}"] +##teamcity[testStarted name='Combining MatchAllOf does not nest'] +##teamcity[testFinished name='Combining MatchAllOf does not nest' duration="{duration}"] +##teamcity[testStarted name='Combining MatchAnyOf does not nest'] +##teamcity[testFinished name='Combining MatchAnyOf does not nest' duration="{duration}"] +##teamcity[testStarted name='Combining MatchNotOf does not nest'] +##teamcity[testFinished name='Combining MatchNotOf does not nest' duration="{duration}"] +##teamcity[testStarted name='Combining generic and non-generic matchers'] +##teamcity[testFinished name='Combining generic and non-generic matchers' duration="{duration}"] +##teamcity[testStarted name='Combining generic matchers'] +##teamcity[testFinished name='Combining generic matchers' duration="{duration}"] +##teamcity[testStarted name='Combining only templated matchers'] +##teamcity[testFinished name='Combining only templated matchers' duration="{duration}"] ##teamcity[testStarted name='Commas in various macros are allowed'] ##teamcity[testFinished name='Commas in various macros are allowed' duration="{duration}"] ##teamcity[testStarted name='Comparing function pointers'] @@ -325,6 +337,8 @@ Message.tests.cpp:|nexpression failed with messages:|n "this messa ##teamcity[testStarted name='INFO is reset for each loop'] Message.tests.cpp:|nexpression failed with messages:|n "current counter 10"|n "i := 10"|n REQUIRE( i < 10 )|nwith expansion:|n 10 < 10|n'] ##teamcity[testFinished name='INFO is reset for each loop' duration="{duration}"] +##teamcity[testStarted name='Immovable matchers can be used'] +##teamcity[testFinished name='Immovable matchers can be used' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should fail'] Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven != 7 )|nwith expansion:|n 7 != 7|n- failure ignore as test marked as |'ok to fail|'|n'] Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one != Approx( 9.1f ) )|nwith expansion:|n 9.1f != Approx( 9.1000003815 )|n- failure ignore as test marked as |'ok to fail|'|n'] @@ -338,6 +352,8 @@ Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello.si ##teamcity[testFinished name='Less-than inequalities with different epsilons' duration="{duration}"] ##teamcity[testStarted name='ManuallyRegistered'] ##teamcity[testFinished name='ManuallyRegistered' duration="{duration}"] +##teamcity[testStarted name='Matchers are not moved or copied'] +##teamcity[testFinished name='Matchers are not moved or copied' duration="{duration}"] ##teamcity[testStarted name='Matchers can be (AllOf) composed with the && operator'] ##teamcity[testFinished name='Matchers can be (AllOf) composed with the && operator' duration="{duration}"] ##teamcity[testStarted name='Matchers can be (AnyOf) composed with the |||| operator'] @@ -395,6 +411,8 @@ Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello <= Message.tests.cpp:|nexplicit failure with message:|n "Message from section one"'] Message.tests.cpp:|nexplicit failure with message:|n "Message from section two"'] ##teamcity[testFinished name='Output from all sections is reported' duration="{duration}"] +##teamcity[testStarted name='Overloaded comma or address-of operators are not used'] +##teamcity[testFinished name='Overloaded comma or address-of operators are not used' duration="{duration}"] ##teamcity[testStarted name='Parse test names and tags'] ##teamcity[testFinished name='Parse test names and tags' duration="{duration}"] ##teamcity[testStarted name='Pointers can be compared to null'] diff --git a/tests/SelfTest/Baselines/xml.sw.approved.txt b/tests/SelfTest/Baselines/xml.sw.approved.txt index 60bbaa6187..907f4ec8a5 100644 --- a/tests/SelfTest/Baselines/xml.sw.approved.txt +++ b/tests/SelfTest/Baselines/xml.sw.approved.txt @@ -2417,6 +2417,128 @@ Nor would this
+ + + + 1, MatcherA() && MatcherB() && MatcherC() + + + 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 ) + + + + + 1, MatcherA() && MatcherB() && MatcherC() && MatcherD() + + + 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true ) + + + + + + + + 1, MatcherA() || MatcherB() || MatcherC() + + + 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 ) + + + + + 1, MatcherA() || MatcherB() || MatcherC() || MatcherD() + + + 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true ) + + + + + + + + 0, !MatcherA() + + + 0 not equals: (int) 1 or (float) 1.0f + + + + + 1, !!MatcherA() + + + 1 equals: (int) 1 or (float) 1.0f + + + + + 0, !!!MatcherA() + + + 0 not equals: (int) 1 or (float) 1.0f + + + + + 1, !!!!MatcherA() + + + 1 equals: (int) 1 or (float) 1.0f + + + + + + + + vec, AllOf([](int elem) { return elem % 2 == 1; }, "odd") && !EqualsRange(a) + + + { 1, 3, 5 } ( all of are odd and not Equals: { 5, 3, 1 } ) + + + + + + + + container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) + + + { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } ) + + + + + + + + 1, MatcherA() || MatcherB() + + + 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 ) + + + + + 1, MatcherA() && MatcherB() + + + 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 ) + + + + + 1, MatcherA() || !MatcherB() + + + 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 ) + + + + @@ -7119,6 +7241,17 @@ Nor would this + + + + 123, (ImmovableMatcher() && ImmovableMatcher()) || !ImmovableMatcher() + + + 123 ( ( always false and always false ) or not always false ) + + + + @@ -7291,6 +7424,17 @@ Nor would this + + + + (ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher() + + + (ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher() + + + + @@ -8025,6 +8169,41 @@ Nor would this + + + + (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed + + + (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed + + + + + &EvilMatcher(), EvilAddressOfOperatorUsed + + + &EvilMatcher(), EvilAddressOfOperatorUsed + + + + + EvilMatcher() || EvilMatcher() && !EvilMatcher() + + + EvilMatcher() || EvilMatcher() && !EvilMatcher() + + + + + (EvilMatcher() && EvilMatcher()) || !EvilMatcher() + + + (EvilMatcher() && EvilMatcher()) || !EvilMatcher() + + + +
@@ -16852,7 +17031,7 @@ loose text artifact
- + - + diff --git a/tests/SelfTest/UsageTests/Matchers.tests.cpp b/tests/SelfTest/UsageTests/Matchers.tests.cpp index 0da3d85a67..cbbf5fee9c 100644 --- a/tests/SelfTest/UsageTests/Matchers.tests.cpp +++ b/tests/SelfTest/UsageTests/Matchers.tests.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #ifdef __clang__ #pragma clang diagnostic push @@ -76,7 +78,7 @@ namespace { namespace MatchersTests { throw DerivedException{}; } - class ExceptionMatcher : public Catch::MatcherBase { + class ExceptionMatcher : public Catch::MatcherBaseGeneric { int m_expected; public: ExceptionMatcher(int i) : m_expected(i) {} @@ -565,7 +567,260 @@ namespace { namespace MatchersTests { REQUIRE_THAT(s, Predicate([](S &s){ return s.value() == 42;})); } -} } // namespace MatchersTests + + template + struct EqualsRangeMatcher : Catch::MatcherBaseGeneric> { + + EqualsRangeMatcher(Range const& range) : range{ range } {} + + template + bool match(OtherRange const& other) const { + using std::begin; + using std::end; + + return std::equal(begin(range), end(range), begin(other), end(other)); + } + + std::string describe() const { + return "Equals: " + Catch::rangeToString(range); + } + + private: + Range const& range; + }; + + template + auto EqualsRange(const Range& range) -> EqualsRangeMatcher { + return EqualsRangeMatcher{range}; + } + + TEST_CASE("Combining generic matchers", "[matchers][templated]") { + std::array container{{ 1,2,3 }}; + + std::array a{{ 1,2,3 }}; + std::vector b{ 0,1,2 }; + std::list c{ 4,5,6 }; + + REQUIRE_THAT(container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c)); + } + + template + class AllOfMatcher : public Catch::MatcherBase> { + Predicate m_predicate; + std::string m_description; + + public: + template + AllOfMatcher(_Predicate &&predicate, const std::string &description) + : m_predicate{std::forward<_Predicate>(predicate)}, + m_description{description} + {} + + bool match(const std::vector &vec) const override { + return std::all_of(vec.begin(), vec.end(), m_predicate); + } + + std::string describe() const override { + return "all of are " + m_description; + } + + }; + + template + AllOfMatcher AllOf(Predicate &&predicate, const std::string &description) { + return {predicate, description}; + } + + TEST_CASE("Combining generic and non-generic matchers", "[matchers][templated]") { + using namespace Catch::Matchers; + std::vector vec{ 1, 3, 5 }; + + std::array a{{ 5, 3, 1 }}; + + REQUIRE_THAT(vec, AllOf([](int elem) { + return elem % 2 == 1; + }, "odd") && + !EqualsRange(a)); + } + + struct MatcherA : Catch::MatcherBaseGeneric { + std::string describe() const { return "equals: (int) 1 or (float) 1.0f"; } + bool match(int i) const { return i == 1; } + bool match(float f) const { return f == 1.0f; } + }; + + struct MatcherB : Catch::MatcherBaseGeneric { + std::string describe() const { return "equals: (long long) 1"; } + bool match(long long l) const { return l == 1ll; } + }; + + struct MatcherC : Catch::MatcherBaseGeneric { + std::string describe() const { return "equals: (T) 1"; } + template + bool match(T t) const { return t == T{1}; } + }; + + struct MatcherD : Catch::MatcherBaseGeneric { + std::string describe() const { return "equals: true"; } + bool match(bool b) const { return b == true; } + }; + + template + void has_type(Actual &&) + { + STATIC_REQUIRE(std::is_same::value); + } + + TEST_CASE("Combining only templated matchers", "[matchers][templated]") { + has_type>(MatcherA() || MatcherB()); + + REQUIRE_THAT(1, MatcherA() || MatcherB()); + + has_type>(MatcherA() && MatcherB()); + + REQUIRE_THAT(1, MatcherA() && MatcherB()); + + has_type>>(MatcherA() || !MatcherB()); + + REQUIRE_THAT(1, MatcherA() || !MatcherB()); + } + + TEST_CASE("Combining MatchAnyOf does not nest", "[matchers][templated]") { + has_type>(MatcherA() || MatcherB() || MatcherC()); + + REQUIRE_THAT(1, MatcherA() || MatcherB() || MatcherC()); + + has_type>(MatcherA() || MatcherB() || MatcherC() || MatcherD()); + + REQUIRE_THAT(1, MatcherA() || MatcherB() || MatcherC() || MatcherD()); + } + + TEST_CASE("Combining MatchAllOf does not nest", "[matchers][templated]") { + has_type>(MatcherA() && MatcherB() && MatcherC()); + + REQUIRE_THAT(1, MatcherA() && MatcherB() && MatcherC()); + + has_type>(MatcherA() && MatcherB() && MatcherC() && MatcherD()); + + REQUIRE_THAT(1, MatcherA() && MatcherB() && MatcherC() && MatcherD()); + } + + TEST_CASE("Combining MatchNotOf does not nest", "[matchers][templated]") { + has_type>(!MatcherA()); + + REQUIRE_THAT(0, !MatcherA()); + + has_type(!!MatcherA()); + + REQUIRE_THAT(1, !!MatcherA()); + + has_type>(!!!MatcherA()); + + REQUIRE_THAT(0, !!!MatcherA()); + + has_type(!!!!MatcherA()); + + REQUIRE_THAT(1, !!!!MatcherA()); + } + + struct EvilAddressOfOperatorUsed : std::exception { + EvilAddressOfOperatorUsed() {} + const char* what() const noexcept override { + return "overloaded address-of operator of matcher was used instead of std::addressof"; + } + }; + + struct EvilCommaOperatorUsed : std::exception { + EvilCommaOperatorUsed() {} + const char* what() const noexcept override { + return "overloaded comma operator of matcher was used"; + } + }; + + struct EvilMatcher : Catch::MatcherBaseGeneric { + std::string describe() const { + return "equals: 45"; + } + + bool match(int i) const { + return i == 45; + } + + EvilMatcher const* operator& () const { + throw EvilAddressOfOperatorUsed(); + } + + int operator,(EvilMatcher const&) const { + throw EvilCommaOperatorUsed(); + } + }; + + TEST_CASE("Overloaded comma or address-of operators are not used", "[matchers][templated]") { + REQUIRE_THROWS_AS((EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed); + REQUIRE_THROWS_AS(&EvilMatcher(), EvilAddressOfOperatorUsed); + REQUIRE_NOTHROW(EvilMatcher() || EvilMatcher() && !EvilMatcher()); + REQUIRE_NOTHROW((EvilMatcher() && EvilMatcher()) || !EvilMatcher()); + } + + struct ImmovableMatcher : Catch::MatcherBaseGeneric { + ImmovableMatcher() = default; + ImmovableMatcher(ImmovableMatcher const&) = delete; + ImmovableMatcher(ImmovableMatcher &&) = delete; + ImmovableMatcher& operator=(ImmovableMatcher const&) = delete; + ImmovableMatcher& operator=(ImmovableMatcher &&) = delete; + + std::string describe() const { + return "always false"; + } + + template + bool match(T&&) const { + return false; + } + }; + + struct MatcherWasMovedOrCopied : std::exception { + MatcherWasMovedOrCopied() {} + const char* what() const noexcept override { + return "attempted to copy or move a matcher"; + } + }; + + struct ThrowOnCopyOrMoveMatcher : Catch::MatcherBaseGeneric { + ThrowOnCopyOrMoveMatcher() = default; + ThrowOnCopyOrMoveMatcher(ThrowOnCopyOrMoveMatcher const&) { + throw MatcherWasMovedOrCopied(); + } + ThrowOnCopyOrMoveMatcher(ThrowOnCopyOrMoveMatcher &&) { + throw MatcherWasMovedOrCopied(); + } + ThrowOnCopyOrMoveMatcher& operator=(ThrowOnCopyOrMoveMatcher const&) { + throw MatcherWasMovedOrCopied(); + } + ThrowOnCopyOrMoveMatcher& operator=(ThrowOnCopyOrMoveMatcher &&) { + throw MatcherWasMovedOrCopied(); + } + + std::string describe() const { + return "always false"; + } + + template + bool match(T&&) const { + return false; + } + }; + + TEST_CASE("Matchers are not moved or copied", "[matchers][templated]") { + REQUIRE_NOTHROW((ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher()); + } + + TEST_CASE("Immovable matchers can be used", "[matchers][templated]") { + REQUIRE_THAT(123, (ImmovableMatcher() && ImmovableMatcher()) || !ImmovableMatcher()); + } + +} +} // namespace MatchersTests #ifdef __clang__ #pragma clang diagnostic pop From e9fc5ecc98a4cee6ec3462f40e7ff75c433c1a3e Mon Sep 17 00:00:00 2001 From: Dvir Yitzchaki Date: Mon, 10 Feb 2020 11:06:41 +0200 Subject: [PATCH 3/3] fix build --- src/catch2/catch_matchers.h | 4 ++-- src/catch2/reporters/catch_reporter_tap.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/catch2/catch_matchers.h b/src/catch2/catch_matchers.h index bccbc752c2..ee935ea60b 100644 --- a/src/catch2/catch_matchers.h +++ b/src/catch2/catch_matchers.h @@ -109,7 +109,7 @@ namespace Matchers { return and_helper(other, makeIndexSequence()); } - std::tuple m_matchers; + std::tuple m_matchers; }; template @@ -171,7 +171,7 @@ namespace Matchers { return or_helper(other, makeIndexSequence()); } - std::tuple m_matchers; + std::tuple m_matchers; }; template diff --git a/src/catch2/reporters/catch_reporter_tap.hpp b/src/catch2/reporters/catch_reporter_tap.hpp index aee33aea6e..1ede76fb2c 100644 --- a/src/catch2/reporters/catch_reporter_tap.hpp +++ b/src/catch2/reporters/catch_reporter_tap.hpp @@ -24,7 +24,7 @@ namespace Catch { return m_reporterPrefs; } - void noMatchingTestCases(std::string const& spec); + void noMatchingTestCases(std::string const& spec) override; void assertionStarting( AssertionInfo const& ) override {}