mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-11-04 05:59:32 +01:00 
			
		
		
		
	Feature: generic matchers (#1843)
This commit extends the Matchers feature with the ability to have type-independent (e.g. templated) matchers. This is done by adding a new base type that Matchers can extend, `MatcherGenericBase`, and overloads of operators `!`, `&&` and `||` that handle matchers extending `MatcherGenericBase` in a special manner. These new matchers can also take their arguments as values and non-const references. Closes #1307 Closes #1553 Closes #1554 Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
This commit is contained in:
		@@ -77,6 +77,7 @@ set(INTERNAL_HEADERS
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_floating.h
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_generic.hpp
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_string.h
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_templates.hpp
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_vector.h
 | 
			
		||||
    ${SOURCES_DIR}/catch_message.h
 | 
			
		||||
    ${SOURCES_DIR}/catch_meta.hpp
 | 
			
		||||
@@ -159,6 +160,7 @@ set(IMPL_SOURCES
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_floating.cpp
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_generic.cpp
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_string.cpp
 | 
			
		||||
    ${SOURCES_DIR}/catch_matchers_templates.cpp
 | 
			
		||||
    ${SOURCES_DIR}/catch_message.cpp
 | 
			
		||||
    ${SOURCES_DIR}/catch_output_redirect.cpp
 | 
			
		||||
    ${SOURCES_DIR}/catch_registry_hub.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ namespace Catch {
 | 
			
		||||
    // 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<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
 | 
			
		||||
        MatchExpr<std::string, StringMatcher const&> expr( std::move(exceptionMessage), matcher, matcherString );
 | 
			
		||||
        handler.handleExpr( expr );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,13 +16,13 @@ namespace Catch {
 | 
			
		||||
 | 
			
		||||
    template<typename ArgT, typename MatcherT>
 | 
			
		||||
    class MatchExpr : public ITransientExpression {
 | 
			
		||||
        ArgT const& m_arg;
 | 
			
		||||
        ArgT && m_arg;
 | 
			
		||||
        MatcherT m_matcher;
 | 
			
		||||
        StringRef m_matcherString;
 | 
			
		||||
    public:
 | 
			
		||||
        MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString )
 | 
			
		||||
        :   ITransientExpression{ true, matcher.match( arg ) },
 | 
			
		||||
            m_arg( arg ),
 | 
			
		||||
        MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString )
 | 
			
		||||
        :   ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose
 | 
			
		||||
            m_arg( std::forward<ArgT>(arg) ),
 | 
			
		||||
            m_matcher( matcher ),
 | 
			
		||||
            m_matcherString( matcherString )
 | 
			
		||||
        {}
 | 
			
		||||
@@ -42,8 +42,8 @@ namespace Catch {
 | 
			
		||||
    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  );
 | 
			
		||||
 | 
			
		||||
    template<typename ArgT, typename MatcherT>
 | 
			
		||||
    auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> {
 | 
			
		||||
        return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString );
 | 
			
		||||
    auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> {
 | 
			
		||||
        return MatchExpr<ArgT, MatcherT>( std::forward<ArgT>(arg), matcher, matcherString );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} // namespace Catch
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								src/catch2/catch_matchers_templates.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/catch2/catch_matchers_templates.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#include <catch2/catch_matchers_templates.hpp>
 | 
			
		||||
 | 
			
		||||
namespace Catch {
 | 
			
		||||
namespace Matchers {
 | 
			
		||||
    namespace Impl {
 | 
			
		||||
        MatcherGenericBase::~MatcherGenericBase() {}
 | 
			
		||||
 | 
			
		||||
        std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end) {
 | 
			
		||||
            std::string description;
 | 
			
		||||
            std::size_t combined_size = 4;
 | 
			
		||||
            for ( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) {
 | 
			
		||||
                combined_size += desc->size();
 | 
			
		||||
            }
 | 
			
		||||
            combined_size += (descriptions_end - descriptions_begin - 1) * combine.size();
 | 
			
		||||
 | 
			
		||||
            description.reserve(combined_size);
 | 
			
		||||
 | 
			
		||||
            description += "( ";
 | 
			
		||||
            bool first = true;
 | 
			
		||||
            for( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) {
 | 
			
		||||
                if( first )
 | 
			
		||||
                    first = false;
 | 
			
		||||
                else
 | 
			
		||||
                    description += combine;
 | 
			
		||||
                description += *desc;
 | 
			
		||||
            }
 | 
			
		||||
            description += " )";
 | 
			
		||||
            return description;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
} // namespace Matchers
 | 
			
		||||
} // namespace Catch
 | 
			
		||||
							
								
								
									
										262
									
								
								src/catch2/catch_matchers_templates.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								src/catch2/catch_matchers_templates.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,262 @@
 | 
			
		||||
#ifndef TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED
 | 
			
		||||
#define TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <catch2/catch_common.h>
 | 
			
		||||
#include <catch2/catch_matchers.h>
 | 
			
		||||
#include <catch2/catch_stringref.h>
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
namespace Catch {
 | 
			
		||||
namespace Matchers {
 | 
			
		||||
    namespace Impl {
 | 
			
		||||
 | 
			
		||||
        template<typename... MatcherTs> struct MatchAllOfGeneric;
 | 
			
		||||
        template<typename... MatcherTs> struct MatchAnyOfGeneric;
 | 
			
		||||
        template<typename MatcherT> struct MatchNotOfGeneric;
 | 
			
		||||
 | 
			
		||||
        struct MatcherGenericBase : MatcherUntypedBase {
 | 
			
		||||
            virtual ~MatcherGenericBase();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        template<std::size_t N, std::size_t M>
 | 
			
		||||
        std::array<void const*, N + M> array_cat(std::array<void const*, N> && lhs, std::array<void const*, M> && rhs) {
 | 
			
		||||
            std::array<void const*, N + M> arr{};
 | 
			
		||||
            std::copy_n(lhs.begin(), N, arr.begin());
 | 
			
		||||
            std::copy_n(rhs.begin(), M, arr.begin() + N);
 | 
			
		||||
            return arr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<std::size_t N>
 | 
			
		||||
        std::array<void const*, N+1> array_cat(std::array<void const*, N> && lhs, void const* rhs) {
 | 
			
		||||
            std::array<void const*, N+1> arr{};
 | 
			
		||||
            std::copy_n(lhs.begin(), N, arr.begin());
 | 
			
		||||
            arr[N] = rhs;
 | 
			
		||||
            return arr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<std::size_t N>
 | 
			
		||||
        std::array<void const*, N+1> array_cat(void const* lhs, std::array<void const*, N> && rhs) {
 | 
			
		||||
            std::array<void const*, N+1> arr{lhs};
 | 
			
		||||
            std::copy_n(rhs.begin(), N, arr.begin() + 1);
 | 
			
		||||
            return arr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #ifdef CATCH_CPP17_OR_GREATER
 | 
			
		||||
 | 
			
		||||
        using std::conjunction;
 | 
			
		||||
 | 
			
		||||
        #else // CATCH_CPP17_OR_GREATER
 | 
			
		||||
 | 
			
		||||
        template<typename... Cond>
 | 
			
		||||
        struct conjunction : std::true_type {};
 | 
			
		||||
 | 
			
		||||
        template<typename Cond, typename... Rest>
 | 
			
		||||
        struct conjunction<Cond, Rest...> : std::integral_constant<bool, Cond::value && conjunction<Rest...>::value> {};
 | 
			
		||||
 | 
			
		||||
        #endif // CATCH_CPP17_OR_GREATER
 | 
			
		||||
 | 
			
		||||
        template<typename T>
 | 
			
		||||
        using is_generic_matcher = std::is_base_of<
 | 
			
		||||
            Catch::Matchers::Impl::MatcherGenericBase,
 | 
			
		||||
            typename std::remove_cv<typename std::remove_reference<T>::type>::type
 | 
			
		||||
        >;
 | 
			
		||||
 | 
			
		||||
        template<typename... Ts>
 | 
			
		||||
        using are_generic_matchers = conjunction<is_generic_matcher<Ts>...>;
 | 
			
		||||
 | 
			
		||||
        template<typename T>
 | 
			
		||||
        using is_matcher = std::is_base_of<
 | 
			
		||||
            Catch::Matchers::Impl::MatcherUntypedBase,
 | 
			
		||||
            typename std::remove_cv<typename std::remove_reference<T>::type>::type
 | 
			
		||||
        >;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        template<std::size_t N, typename Arg>
 | 
			
		||||
        bool match_all_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices>
 | 
			
		||||
        bool match_all_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) {
 | 
			
		||||
            return static_cast<T const*>(matchers[Idx])->match(arg) && match_all_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{});
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        template<std::size_t N, typename Arg>
 | 
			
		||||
        bool match_any_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices>
 | 
			
		||||
        bool match_any_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) {
 | 
			
		||||
            return static_cast<T const*>(matchers[Idx])->match(arg) || match_any_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{});
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end);
 | 
			
		||||
 | 
			
		||||
        template<typename... MatcherTs, std::size_t... Idx>
 | 
			
		||||
        std::string describe_multi_matcher(StringRef combine, std::array<void const*, sizeof...(MatcherTs)> const& matchers, std::index_sequence<Idx...>) {
 | 
			
		||||
            std::array<std::string, sizeof...(MatcherTs)> descriptions {{
 | 
			
		||||
                static_cast<MatcherTs const*>(matchers[Idx])->toString()...
 | 
			
		||||
            }};
 | 
			
		||||
 | 
			
		||||
            return describe_multi_matcher(combine, descriptions.data(), descriptions.data() + descriptions.size());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        template<typename... MatcherTs>
 | 
			
		||||
        struct MatchAllOfGeneric : MatcherGenericBase {
 | 
			
		||||
            MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{std::addressof(matchers)...} {}
 | 
			
		||||
            explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
 | 
			
		||||
 | 
			
		||||
            template<typename Arg>
 | 
			
		||||
            bool match(Arg&& arg) const {
 | 
			
		||||
                return match_all_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{});
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::string describe() const override {
 | 
			
		||||
                return describe_multi_matcher<MatcherTs...>(" and "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::array<void const*, sizeof...(MatcherTs)> m_matchers;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        template<typename... MatcherTs>
 | 
			
		||||
        struct MatchAnyOfGeneric : MatcherGenericBase {
 | 
			
		||||
            MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{std::addressof(matchers)...} {}
 | 
			
		||||
            explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
 | 
			
		||||
 | 
			
		||||
            template<typename Arg>
 | 
			
		||||
            bool match(Arg&& arg) const {
 | 
			
		||||
                return match_any_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{});
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::string describe() const override {
 | 
			
		||||
                return describe_multi_matcher<MatcherTs...>(" or "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::array<void const*, sizeof...(MatcherTs)> m_matchers;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        template<typename MatcherT>
 | 
			
		||||
        struct MatchNotOfGeneric : MatcherGenericBase {
 | 
			
		||||
            explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {}
 | 
			
		||||
 | 
			
		||||
            template<typename Arg>
 | 
			
		||||
            bool match(Arg&& arg) const {
 | 
			
		||||
                return !m_matcher.match(arg);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::string describe() const override {
 | 
			
		||||
                return "not " + m_matcher.toString();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MatcherT const& m_matcher;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // compose only generic matchers
 | 
			
		||||
        template<typename MatcherLHS, typename MatcherRHS>
 | 
			
		||||
        typename std::enable_if<are_generic_matchers<MatcherLHS, MatcherRHS>::value, MatchAllOfGeneric<MatcherLHS, MatcherRHS>>::type
 | 
			
		||||
        operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
 | 
			
		||||
            return {lhs, rhs};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename MatcherLHS, typename MatcherRHS>
 | 
			
		||||
        typename std::enable_if<are_generic_matchers<MatcherLHS, MatcherRHS>::value, MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>::type
 | 
			
		||||
        operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
 | 
			
		||||
            return {lhs, rhs};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename MatcherT>
 | 
			
		||||
        typename std::enable_if<is_generic_matcher<MatcherT>::value, MatchNotOfGeneric<MatcherT>>::type
 | 
			
		||||
        operator ! (MatcherT const& matcher) {
 | 
			
		||||
            return MatchNotOfGeneric<MatcherT>{matcher};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // compose mixed generic and non-generic matchers
 | 
			
		||||
        template<typename MatcherLHS, typename ArgRHS>
 | 
			
		||||
        typename std::enable_if<is_generic_matcher<MatcherLHS>::value, MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>::type
 | 
			
		||||
        operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
 | 
			
		||||
            return {lhs, rhs};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename ArgLHS, typename MatcherRHS>
 | 
			
		||||
        typename std::enable_if<is_generic_matcher<MatcherRHS>::value, MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>::type
 | 
			
		||||
        operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
 | 
			
		||||
            return {lhs, rhs};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename MatcherLHS, typename ArgRHS>
 | 
			
		||||
        typename std::enable_if<is_generic_matcher<MatcherLHS>::value, MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>::type
 | 
			
		||||
        operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
 | 
			
		||||
            return {lhs, rhs};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename ArgLHS, typename MatcherRHS>
 | 
			
		||||
        typename std::enable_if<is_generic_matcher<MatcherRHS>::value, MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>::type
 | 
			
		||||
        operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
 | 
			
		||||
            return {lhs, rhs};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // avoid deep nesting of matcher templates
 | 
			
		||||
        template<typename... MatchersLHS, typename... MatchersRHS>
 | 
			
		||||
        MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>
 | 
			
		||||
        operator && (MatchAllOfGeneric<MatchersLHS...> && lhs, MatchAllOfGeneric<MatchersRHS...> && rhs) {
 | 
			
		||||
            return MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename... MatchersLHS, typename MatcherRHS>
 | 
			
		||||
        typename std::enable_if<is_matcher<MatcherRHS>::value, MatchAllOfGeneric<MatchersLHS..., MatcherRHS>>::type
 | 
			
		||||
        operator && (MatchAllOfGeneric<MatchersLHS...> && lhs, MatcherRHS const& rhs) {
 | 
			
		||||
            return MatchAllOfGeneric<MatchersLHS..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(&rhs))};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename MatcherLHS, typename... MatchersRHS>
 | 
			
		||||
        typename std::enable_if<is_matcher<MatcherLHS>::value, MatchAllOfGeneric<MatcherLHS, MatchersRHS...>>::type
 | 
			
		||||
        operator && (MatcherLHS const& lhs, MatchAllOfGeneric<MatchersRHS...> && rhs) {
 | 
			
		||||
            return MatchAllOfGeneric<MatcherLHS, MatchersRHS...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename... MatchersLHS, typename... MatchersRHS>
 | 
			
		||||
        MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>
 | 
			
		||||
        operator || (MatchAnyOfGeneric<MatchersLHS...> && lhs, MatchAnyOfGeneric<MatchersRHS...> && rhs) {
 | 
			
		||||
            return MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename... MatchersLHS, typename MatcherRHS>
 | 
			
		||||
        typename std::enable_if<is_matcher<MatcherRHS>::value, MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>>::type
 | 
			
		||||
        operator || (MatchAnyOfGeneric<MatchersLHS...> && lhs, MatcherRHS const& rhs) {
 | 
			
		||||
            return MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename MatcherLHS, typename... MatchersRHS>
 | 
			
		||||
        typename std::enable_if<is_matcher<MatcherLHS>::value, MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>>::type
 | 
			
		||||
        operator || (MatcherLHS const& lhs, MatchAnyOfGeneric<MatchersRHS...> && rhs) {
 | 
			
		||||
            return MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename MatcherT>
 | 
			
		||||
        MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
 | 
			
		||||
            return matcher.m_matcher;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    } // namespace Impl
 | 
			
		||||
 | 
			
		||||
} // namespace Matchers
 | 
			
		||||
 | 
			
		||||
using namespace Matchers;
 | 
			
		||||
using Matchers::Impl::MatcherGenericBase;
 | 
			
		||||
 | 
			
		||||
} // namespace Catch
 | 
			
		||||
 | 
			
		||||
#endif //TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED
 | 
			
		||||
@@ -85,6 +85,13 @@ 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 MatchAllOfGeneric does not nest
 | 
			
		||||
:test-result: PASS Combining MatchAnyOfGeneric does not nest
 | 
			
		||||
:test-result: PASS Combining MatchNotOfGeneric does not nest
 | 
			
		||||
:test-result: PASS Combining concrete matchers does not use templated matchers
 | 
			
		||||
:test-result: PASS Combining only templated matchers
 | 
			
		||||
:test-result: PASS Combining templated and concrete matchers
 | 
			
		||||
:test-result: PASS Combining templated matchers
 | 
			
		||||
:test-result: PASS Commas in various macros are allowed
 | 
			
		||||
:test-result: PASS Comparing function pointers
 | 
			
		||||
:test-result: PASS Comparison ops
 | 
			
		||||
@@ -147,6 +154,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
 | 
			
		||||
 
 | 
			
		||||
@@ -263,6 +263,37 @@ ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 2 == 2
 | 
			
		||||
ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 3 == 3
 | 
			
		||||
ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 4 == 4
 | 
			
		||||
ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 5 == 5
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB() && MatcherC()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB() || MatcherC()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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:<line number>: passed: with 1 message: 'std::is_same< decltype(!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: 0, !MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!MatcherA()), MatcherA const& >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: 1, !!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: 0, !!!MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: 1, !!!!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))), Catch::Matchers::Impl::MatchAnyOf<std::string> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: 1, MatcherA() && MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Impl::MatchNotOfGeneric<MatcherB>> >::value'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || !MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: vec, Predicate<std::vector<int>>([](auto const& vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) for: { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) for: "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) for: "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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 } )
 | 
			
		||||
Tricky.tests.cpp:<line number>: passed: std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}}
 | 
			
		||||
Tricky.tests.cpp:<line number>: passed: std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}}
 | 
			
		||||
Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3}
 | 
			
		||||
@@ -883,6 +914,10 @@ RandomNumberGeneration.tests.cpp:<line number>: passed: rng() == 0x<hex digits>
 | 
			
		||||
4261393167 (0x<hex digits>)
 | 
			
		||||
Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'Message from section one'
 | 
			
		||||
Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'Message from section two'
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: &EvilMatcher(), EvilAddressOfOperatorUsed
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: EvilMatcher() || EvilMatcher() && !EvilMatcher()
 | 
			
		||||
Matchers.tests.cpp:<line number>: passed: (EvilMatcher() && EvilMatcher()) || !EvilMatcher()
 | 
			
		||||
CmdLine.tests.cpp:<line number>: passed: spec.hasFilters() == false for: false == false
 | 
			
		||||
CmdLine.tests.cpp:<line number>: passed: spec.matches( *tcA ) == false for: false == false
 | 
			
		||||
CmdLine.tests.cpp:<line number>: passed: spec.matches( *tcB ) == false for: false == false
 | 
			
		||||
 
 | 
			
		||||
@@ -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: 1792 | 1640 passed | 131 failed | 21 failed as expected
 | 
			
		||||
test cases:  328 |  254 passed |  70 failed |  4 failed as expected
 | 
			
		||||
assertions: 1827 | 1675 passed | 131 failed | 21 failed as expected
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2116,6 +2116,217 @@ ToStringGeneral.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with expansion:
 | 
			
		||||
  5 == 5
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Combining MatchAllOfGeneric does not nest
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
...............................................................................
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(MatcherA() && MatcherB() && MatcherC()), Catch::
 | 
			
		||||
  Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()),
 | 
			
		||||
  Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC,
 | 
			
		||||
  MatcherD> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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 MatchAnyOfGeneric does not nest
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
...............................................................................
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(MatcherA() || MatcherB() || MatcherC()), Catch::
 | 
			
		||||
  Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()),
 | 
			
		||||
  Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC,
 | 
			
		||||
  MatcherD> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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 MatchNotOfGeneric does not nest
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
...............................................................................
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric
 | 
			
		||||
  <MatcherA> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( 0, !MatcherA() )
 | 
			
		||||
with expansion:
 | 
			
		||||
  0 not equals: (int) 1 or (float) 1.0f
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(!!MatcherA()), MatcherA const& >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( 1, !!MatcherA() )
 | 
			
		||||
with expansion:
 | 
			
		||||
  1 equals: (int) 1 or (float) 1.0f
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Impl::
 | 
			
		||||
  MatchNotOfGeneric<MatcherA> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( 0, !!!MatcherA() )
 | 
			
		||||
with expansion:
 | 
			
		||||
  0 not equals: (int) 1 or (float) 1.0f
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( 1, !!!!MatcherA() )
 | 
			
		||||
with expansion:
 | 
			
		||||
  1 equals: (int) 1 or (float) 1.0f
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Combining concrete matchers does not use templated matchers
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
...............................................................................
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith
 | 
			
		||||
  ("bar") && !EndsWith("foo"))), Catch::Matchers::Impl::MatchAnyOf<std::string>
 | 
			
		||||
  >::value
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Combining only templated matchers
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
...............................................................................
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Impl::
 | 
			
		||||
  MatchAnyOfGeneric<MatcherA, MatcherB> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( 1, MatcherA() || MatcherB() )
 | 
			
		||||
with expansion:
 | 
			
		||||
  1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(MatcherA() && MatcherB()), Catch::Matchers::Impl::
 | 
			
		||||
  MatchAllOfGeneric<MatcherA, MatcherB> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( 1, MatcherA() && MatcherB() )
 | 
			
		||||
with expansion:
 | 
			
		||||
  1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
with message:
 | 
			
		||||
  std::is_same< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Impl::
 | 
			
		||||
  MatchAnyOfGeneric<MatcherA, Catch::Matchers::Impl::MatchNotOfGeneric
 | 
			
		||||
  <MatcherB>> >::value
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( 1, MatcherA() || !MatcherB() )
 | 
			
		||||
with expansion:
 | 
			
		||||
  1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 )
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Combining templated and concrete matchers
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
...............................................................................
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( vec, Predicate<std::vector<int>>([](auto const& vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) )
 | 
			
		||||
with expansion:
 | 
			
		||||
  { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5,
 | 
			
		||||
  3, 1 } )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") )
 | 
			
		||||
with expansion:
 | 
			
		||||
  "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' }
 | 
			
		||||
  and ends with: "bar" )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") )
 | 
			
		||||
with expansion:
 | 
			
		||||
  "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r'
 | 
			
		||||
  } and ends with: "bar" )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") )
 | 
			
		||||
with expansion:
 | 
			
		||||
  "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo"
 | 
			
		||||
  and ends with: "bar" )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") )
 | 
			
		||||
with expansion:
 | 
			
		||||
  "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with:
 | 
			
		||||
  "foo" and ends with: "bar" )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) )
 | 
			
		||||
with expansion:
 | 
			
		||||
  "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo"
 | 
			
		||||
  and ends with: "bar" ) )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THAT( str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) )
 | 
			
		||||
with expansion:
 | 
			
		||||
  "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o',
 | 
			
		||||
  'f', 'b', 'a', 'r' } )
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Combining templated matchers
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
...............................................................................
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: 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
 | 
			
		||||
  } )
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Commas in various macros are allowed
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
@@ -6414,6 +6625,24 @@ Message.tests.cpp:<line number>: FAILED:
 | 
			
		||||
explicitly with message:
 | 
			
		||||
  Message from section two
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Overloaded comma or address-of operators are not used
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
...............................................................................
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THROWS_AS( (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_THROWS_AS( &EvilMatcher(), EvilAddressOfOperatorUsed )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_NOTHROW( EvilMatcher() || EvilMatcher() && !EvilMatcher() )
 | 
			
		||||
 | 
			
		||||
Matchers.tests.cpp:<line number>: PASSED:
 | 
			
		||||
  REQUIRE_NOTHROW( (EvilMatcher() && EvilMatcher()) || !EvilMatcher() )
 | 
			
		||||
 | 
			
		||||
-------------------------------------------------------------------------------
 | 
			
		||||
Parse test names and tags
 | 
			
		||||
  Empty test spec should have no filters
 | 
			
		||||
@@ -14053,6 +14282,6 @@ Misc.tests.cpp:<line number>
 | 
			
		||||
Misc.tests.cpp:<line number>: PASSED:
 | 
			
		||||
 | 
			
		||||
===============================================================================
 | 
			
		||||
test cases:  320 |  230 passed |  86 failed |  4 failed as expected
 | 
			
		||||
assertions: 1809 | 1640 passed | 148 failed | 21 failed as expected
 | 
			
		||||
test cases:  328 |  238 passed |  86 failed |  4 failed as expected
 | 
			
		||||
assertions: 1844 | 1675 passed | 148 failed | 21 failed as expected
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<testsuitesloose text artifact
 | 
			
		||||
>
 | 
			
		||||
  <testsuite name="<exe-name>" errors="17" failures="132" tests="1810" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
 | 
			
		||||
  <testsuite name="<exe-name>" errors="17" failures="132" tests="1845" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
 | 
			
		||||
    <properties>
 | 
			
		||||
      <property name="filters" value="~[!nonportable]~[!benchmark]~[approvals] *"/>
 | 
			
		||||
      <property name="random-seed" value="1"/>
 | 
			
		||||
@@ -350,6 +350,13 @@ Exception.tests.cpp:<line number>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Character pretty printing/Specifically escaped" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Character pretty printing/General chars" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Character pretty printing/Low ASCII" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Combining MatchAllOfGeneric does not nest" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Combining MatchAnyOfGeneric does not nest" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Combining MatchNotOfGeneric does not nest" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Combining concrete matchers does not use templated matchers" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Combining only templated matchers" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Combining templated and concrete matchers" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Combining templated matchers" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Commas in various macros are allowed" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Comparing function pointers" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Comparison ops" time="{duration}"/>
 | 
			
		||||
@@ -950,6 +957,7 @@ Message from section two
 | 
			
		||||
Message.tests.cpp:<line number>
 | 
			
		||||
      </failure>
 | 
			
		||||
    </testcase>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Overloaded comma or address-of operators are not used" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Parse test names and tags/Empty test spec should have no filters" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Parse test names and tags/Test spec from empty string should have no filters" time="{duration}"/>
 | 
			
		||||
    <testcase classname="<exe-name>.global" name="Parse test names and tags/Test spec from just a comma should have no filters" time="{duration}"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -915,6 +915,13 @@ Exception.tests.cpp:<line number>
 | 
			
		||||
  <file path="tests/<exe-name>/UsageTests/Matchers.tests.cpp">
 | 
			
		||||
    <testCase name="Arbitrary predicate matcher/Function pointer" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Arbitrary predicate matcher/Lambdas + different type" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Combining MatchAllOfGeneric does not nest" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Combining MatchAnyOfGeneric does not nest" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Combining MatchNotOfGeneric does not nest" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Combining concrete matchers does not use templated matchers" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Combining only templated matchers" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Combining templated and concrete matchers" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Combining templated matchers" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Contains string matcher" duration="{duration}">
 | 
			
		||||
      <failure message="CHECK_THAT(testStringForMatching(), Contains("not there", Catch::CaseSensitive::No))">
 | 
			
		||||
FAILED:
 | 
			
		||||
@@ -1042,6 +1049,7 @@ with expansion:
 | 
			
		||||
Matchers.tests.cpp:<line number>
 | 
			
		||||
      </failure>
 | 
			
		||||
    </testCase>
 | 
			
		||||
    <testCase name="Overloaded comma or address-of operators are not used" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Predicate matcher can accept const char*" duration="{duration}"/>
 | 
			
		||||
    <testCase name="Regex string matcher" duration="{duration}">
 | 
			
		||||
      <failure message="CHECK_THAT(testStringForMatching(), Matches("this STRING contains 'abc' as a substring"))">
 | 
			
		||||
 
 | 
			
		||||
@@ -524,6 +524,68 @@ 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 MatchAllOfGeneric does not nest
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB() && MatcherC()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
 | 
			
		||||
# Combining MatchAllOfGeneric 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 MatchAllOfGeneric does not nest
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
 | 
			
		||||
# Combining MatchAllOfGeneric 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 MatchAnyOfGeneric does not nest
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB() || MatcherC()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
 | 
			
		||||
# Combining MatchAnyOfGeneric 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 MatchAnyOfGeneric does not nest
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
 | 
			
		||||
# Combining MatchAnyOfGeneric 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 MatchNotOfGeneric does not nest
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> >::value'
 | 
			
		||||
# Combining MatchNotOfGeneric does not nest
 | 
			
		||||
ok {test-number} - 0, !MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f
 | 
			
		||||
# Combining MatchNotOfGeneric does not nest
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(!!MatcherA()), MatcherA const& >::value'
 | 
			
		||||
# Combining MatchNotOfGeneric does not nest
 | 
			
		||||
ok {test-number} - 1, !!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f
 | 
			
		||||
# Combining MatchNotOfGeneric does not nest
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> >::value'
 | 
			
		||||
# Combining MatchNotOfGeneric does not nest
 | 
			
		||||
ok {test-number} - 0, !!!MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f
 | 
			
		||||
# Combining MatchNotOfGeneric does not nest
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value'
 | 
			
		||||
# Combining MatchNotOfGeneric does not nest
 | 
			
		||||
ok {test-number} - 1, !!!!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f
 | 
			
		||||
# Combining concrete matchers does not use templated matchers
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))), Catch::Matchers::Impl::MatchAnyOf<std::string> >::value'
 | 
			
		||||
# Combining only templated matchers
 | 
			
		||||
ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB> >::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< decltype(MatcherA() && MatcherB()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB> >::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< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Impl::MatchNotOfGeneric<MatcherB>> >::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 )
 | 
			
		||||
# Combining templated and concrete matchers
 | 
			
		||||
ok {test-number} - vec, Predicate<std::vector<int>>([](auto const& vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) for: { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
 | 
			
		||||
# Combining templated and concrete matchers
 | 
			
		||||
ok {test-number} - str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
 | 
			
		||||
# Combining templated and concrete matchers
 | 
			
		||||
ok {test-number} - str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
 | 
			
		||||
# Combining templated and concrete matchers
 | 
			
		||||
ok {test-number} - str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
 | 
			
		||||
# Combining templated and concrete matchers
 | 
			
		||||
ok {test-number} - str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
 | 
			
		||||
# Combining templated and concrete matchers
 | 
			
		||||
ok {test-number} - str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) for: "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
 | 
			
		||||
# Combining templated and concrete matchers
 | 
			
		||||
ok {test-number} - str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) for: "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
 | 
			
		||||
# Combining templated 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 } )
 | 
			
		||||
# Commas in various macros are allowed
 | 
			
		||||
ok {test-number} - std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}}
 | 
			
		||||
# Commas in various macros are allowed
 | 
			
		||||
@@ -1688,6 +1750,14 @@ ok {test-number} - rng() == 0x<hex digits> for: 4261393167 (0x<hex digits>) == 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
 | 
			
		||||
@@ -3610,5 +3680,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
 | 
			
		||||
ok {test-number} -
 | 
			
		||||
# xmlentitycheck
 | 
			
		||||
ok {test-number} -
 | 
			
		||||
1..1801
 | 
			
		||||
1..1836
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -203,6 +203,20 @@ Exception.tests.cpp:<line number>|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 MatchAllOfGeneric does not nest']
 | 
			
		||||
##teamcity[testFinished name='Combining MatchAllOfGeneric does not nest' duration="{duration}"]
 | 
			
		||||
##teamcity[testStarted name='Combining MatchAnyOfGeneric does not nest']
 | 
			
		||||
##teamcity[testFinished name='Combining MatchAnyOfGeneric does not nest' duration="{duration}"]
 | 
			
		||||
##teamcity[testStarted name='Combining MatchNotOfGeneric does not nest']
 | 
			
		||||
##teamcity[testFinished name='Combining MatchNotOfGeneric does not nest' duration="{duration}"]
 | 
			
		||||
##teamcity[testStarted name='Combining concrete matchers does not use templated matchers']
 | 
			
		||||
##teamcity[testFinished name='Combining concrete matchers does not use templated matchers' duration="{duration}"]
 | 
			
		||||
##teamcity[testStarted name='Combining only templated matchers']
 | 
			
		||||
##teamcity[testFinished name='Combining only templated matchers' duration="{duration}"]
 | 
			
		||||
##teamcity[testStarted name='Combining templated and concrete matchers']
 | 
			
		||||
##teamcity[testFinished name='Combining templated and concrete matchers' duration="{duration}"]
 | 
			
		||||
##teamcity[testStarted name='Combining templated matchers']
 | 
			
		||||
##teamcity[testFinished name='Combining 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']
 | 
			
		||||
@@ -395,6 +409,8 @@ Condition.tests.cpp:<line number>|nexpression failed|n  CHECK( data.str_hello <=
 | 
			
		||||
Message.tests.cpp:<line number>|nexplicit failure with message:|n  "Message from section one"']
 | 
			
		||||
Message.tests.cpp:<line number>|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']
 | 
			
		||||
 
 | 
			
		||||
@@ -2417,6 +2417,179 @@ Nor would this
 | 
			
		||||
      </Section>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Combining MatchAllOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, MatcherA() && MatcherB() && MatcherC()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, MatcherA() && MatcherB() && MatcherC() && MatcherD()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Combining MatchAnyOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, MatcherA() || MatcherB() || MatcherC()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, MatcherA() || MatcherB() || MatcherC() || MatcherD()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Combining MatchNotOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          0, !MatcherA()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          0 not equals: (int) 1 or (float) 1.0f
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, !!MatcherA()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 equals: (int) 1 or (float) 1.0f
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          0, !!!MatcherA()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          0 not equals: (int) 1 or (float) 1.0f
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, !!!!MatcherA()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 equals: (int) 1 or (float) 1.0f
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Combining concrete matchers does not use templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Combining only templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, MatcherA() || MatcherB()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, MatcherA() && MatcherB()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          1, MatcherA() || !MatcherB()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Combining templated and concrete matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          vec, Predicate<std::vector<int>>([](auto const& vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a)
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar")
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar")
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar")
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar")
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar"))
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr)
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Combining templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c)
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } )
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Commas in various macros are allowed" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
@@ -8025,6 +8198,41 @@ Nor would this
 | 
			
		||||
      </Section>
 | 
			
		||||
      <OverallResult success="false"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Overloaded comma or address-of operators are not used" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          &EvilMatcher(), EvilAddressOfOperatorUsed
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          &EvilMatcher(), EvilAddressOfOperatorUsed
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          EvilMatcher() || EvilMatcher() && !EvilMatcher()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          EvilMatcher() || EvilMatcher() && !EvilMatcher()
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
 | 
			
		||||
        <Original>
 | 
			
		||||
          (EvilMatcher() && EvilMatcher()) || !EvilMatcher()
 | 
			
		||||
        </Original>
 | 
			
		||||
        <Expanded>
 | 
			
		||||
          (EvilMatcher() && EvilMatcher()) || !EvilMatcher()
 | 
			
		||||
        </Expanded>
 | 
			
		||||
      </Expression>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <TestCase name="Parse test names and tags" tags="[command-line][test-spec]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
 | 
			
		||||
      <Section name="Empty test spec should have no filters" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
 | 
			
		||||
        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
 | 
			
		||||
@@ -16909,7 +17117,7 @@ loose text artifact
 | 
			
		||||
      </Section>
 | 
			
		||||
      <OverallResult success="true"/>
 | 
			
		||||
    </TestCase>
 | 
			
		||||
    <OverallResults successes="1640" failures="149" expectedFailures="21"/>
 | 
			
		||||
    <OverallResults successes="1675" failures="149" expectedFailures="21"/>
 | 
			
		||||
  </Group>
 | 
			
		||||
  <OverallResults successes="1640" failures="148" expectedFailures="21"/>
 | 
			
		||||
  <OverallResults successes="1675" failures="148" expectedFailures="21"/>
 | 
			
		||||
</Catch>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,10 +9,12 @@
 | 
			
		||||
#include <catch2/catch_matchers_generic.hpp>
 | 
			
		||||
#include <catch2/catch_matchers_string.h>
 | 
			
		||||
#include <catch2/catch_matchers_vector.h>
 | 
			
		||||
#include <catch2/catch_matchers_templates.hpp>
 | 
			
		||||
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#ifdef __clang__
 | 
			
		||||
#pragma clang diagnostic push
 | 
			
		||||
@@ -554,6 +556,302 @@ namespace { namespace MatchersTests {
 | 
			
		||||
            REQUIRE_THROWS_MATCHES(throwsSpecialException(2), SpecialException,  Message("SpecialException::what"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    template<typename Range>
 | 
			
		||||
    struct EqualsRangeMatcher : Catch::MatcherGenericBase {
 | 
			
		||||
 | 
			
		||||
        EqualsRangeMatcher(Range const& range) : range{ range } {}
 | 
			
		||||
 | 
			
		||||
        template<typename OtherRange>
 | 
			
		||||
        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 override {
 | 
			
		||||
            return "Equals: " + Catch::rangeToString(range);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        Range const& range;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template<typename Range>
 | 
			
		||||
    auto EqualsRange(const Range& range) -> EqualsRangeMatcher<Range> {
 | 
			
		||||
        return EqualsRangeMatcher<Range>{range};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Combining templated matchers", "[matchers][templated]") {
 | 
			
		||||
        std::array<int, 3> container{{ 1,2,3 }};
 | 
			
		||||
 | 
			
		||||
        std::array<int, 3> a{{ 1,2,3 }};
 | 
			
		||||
        std::vector<int> b{ 0,1,2 };
 | 
			
		||||
        std::list<int> c{ 4,5,6 };
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Combining templated and concrete matchers", "[matchers][templated]") {
 | 
			
		||||
        using namespace Catch::Matchers;
 | 
			
		||||
        std::vector<int> vec{ 1, 3, 5 };
 | 
			
		||||
 | 
			
		||||
        std::array<int, 3> a{{ 5, 3, 1 }};
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(vec,
 | 
			
		||||
                    Predicate<std::vector<int>>([](auto const& vec) {
 | 
			
		||||
                        return std::all_of(vec.begin(), vec.end(), [](int elem) {
 | 
			
		||||
                            return elem % 2 == 1;
 | 
			
		||||
                        });
 | 
			
		||||
                    }, "All elements are odd") &&
 | 
			
		||||
                    !EqualsRange(a));
 | 
			
		||||
 | 
			
		||||
        const std::string str = "foobar";
 | 
			
		||||
        const std::array<char, 6> arr{{ 'f', 'o', 'o', 'b', 'a', 'r' }};
 | 
			
		||||
        const std::array<char, 6> bad_arr{{ 'o', 'o', 'f', 'b', 'a', 'r' }};
 | 
			
		||||
 | 
			
		||||
        using Catch::Matchers::StartsWith;
 | 
			
		||||
        using Catch::Matchers::EndsWith;
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar"));
 | 
			
		||||
        REQUIRE_THAT(str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar"));
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar"));
 | 
			
		||||
        REQUIRE_THAT(str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar"));
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")));
 | 
			
		||||
        REQUIRE_THAT(str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Combining concrete matchers does not use templated matchers", "[matchers][templated]") {
 | 
			
		||||
        using Catch::Matchers::StartsWith;
 | 
			
		||||
        using Catch::Matchers::EndsWith;
 | 
			
		||||
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))),
 | 
			
		||||
            Catch::Matchers::Impl::MatchAnyOf<std::string>
 | 
			
		||||
        >::value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct MatcherA : Catch::MatcherGenericBase {
 | 
			
		||||
        std::string describe() const override { 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::MatcherGenericBase {
 | 
			
		||||
        std::string describe() const override { return "equals: (long long) 1"; }
 | 
			
		||||
        bool match(long long l) const { return l == 1ll; }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct MatcherC : Catch::MatcherGenericBase {
 | 
			
		||||
        std::string describe() const override { return "equals: (T) 1"; }
 | 
			
		||||
        template<typename T>
 | 
			
		||||
        bool match(T t) const { return t == T{1}; }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct MatcherD : Catch::MatcherGenericBase {
 | 
			
		||||
        std::string describe() const override { return "equals: true"; }
 | 
			
		||||
        bool match(bool b) const { return b == true; }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Combining only templated matchers", "[matchers][templated]") {
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(MatcherA() || MatcherB()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(1, MatcherA() || MatcherB());
 | 
			
		||||
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(MatcherA() && MatcherB()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(1, MatcherA() && MatcherB());
 | 
			
		||||
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(MatcherA() || !MatcherB()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Impl::MatchNotOfGeneric<MatcherB>>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(1, MatcherA() || !MatcherB());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Combining MatchAnyOfGeneric does not nest", "[matchers][templated]") {
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(MatcherA() || MatcherB() || MatcherC()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(1, MatcherA() || MatcherB() || MatcherC());
 | 
			
		||||
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(1, MatcherA() || MatcherB() || MatcherC() || MatcherD());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Combining MatchAllOfGeneric does not nest", "[matchers][templated]") {
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(MatcherA() && MatcherB() && MatcherC()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(1, MatcherA() && MatcherB() && MatcherC());
 | 
			
		||||
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(1, MatcherA() && MatcherB() && MatcherC() && MatcherD());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Combining MatchNotOfGeneric does not nest", "[matchers][templated]") {
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(!MatcherA()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(0, !MatcherA());
 | 
			
		||||
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(!!MatcherA()),
 | 
			
		||||
            MatcherA const&
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(1, !!MatcherA());
 | 
			
		||||
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(!!!MatcherA()),
 | 
			
		||||
            Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA>
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        REQUIRE_THAT(0, !!!MatcherA());
 | 
			
		||||
 | 
			
		||||
        STATIC_REQUIRE(std::is_same<
 | 
			
		||||
            decltype(!!!!MatcherA()),
 | 
			
		||||
            MatcherA const &
 | 
			
		||||
        >::value);
 | 
			
		||||
 | 
			
		||||
        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::MatcherGenericBase {
 | 
			
		||||
        std::string describe() const override {
 | 
			
		||||
            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::MatcherGenericBase {
 | 
			
		||||
        ImmovableMatcher() = default;
 | 
			
		||||
        ImmovableMatcher(ImmovableMatcher const&) = delete;
 | 
			
		||||
        ImmovableMatcher(ImmovableMatcher &&) = delete;
 | 
			
		||||
        ImmovableMatcher& operator=(ImmovableMatcher const&) = delete;
 | 
			
		||||
        ImmovableMatcher& operator=(ImmovableMatcher &&) = delete;
 | 
			
		||||
 | 
			
		||||
        std::string describe() const override {
 | 
			
		||||
            return "always false";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename T>
 | 
			
		||||
        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::MatcherGenericBase {
 | 
			
		||||
        ThrowOnCopyOrMoveMatcher() = default;
 | 
			
		||||
        [[noreturn]]
 | 
			
		||||
        ThrowOnCopyOrMoveMatcher(ThrowOnCopyOrMoveMatcher const&) {
 | 
			
		||||
            throw MatcherWasMovedOrCopied();
 | 
			
		||||
        }
 | 
			
		||||
        [[noreturn]]
 | 
			
		||||
        ThrowOnCopyOrMoveMatcher(ThrowOnCopyOrMoveMatcher &&) {
 | 
			
		||||
            throw MatcherWasMovedOrCopied();
 | 
			
		||||
        }
 | 
			
		||||
        ThrowOnCopyOrMoveMatcher& operator=(ThrowOnCopyOrMoveMatcher const&) {
 | 
			
		||||
            throw MatcherWasMovedOrCopied();
 | 
			
		||||
        }
 | 
			
		||||
        ThrowOnCopyOrMoveMatcher& operator=(ThrowOnCopyOrMoveMatcher &&) {
 | 
			
		||||
            throw MatcherWasMovedOrCopied();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string describe() const override {
 | 
			
		||||
            return "always false";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        template<typename T>
 | 
			
		||||
        bool match(T&&) const {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Matchers are not moved or copied", "[matchers][templated][approvals]") {
 | 
			
		||||
        REQUIRE_NOTHROW((ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Immovable matchers can be used", "[matchers][templated][approvals]") {
 | 
			
		||||
        REQUIRE_THAT(123, (ImmovableMatcher() && ImmovableMatcher()) || !ImmovableMatcher());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct ReferencingMatcher : Catch::MatcherGenericBase {
 | 
			
		||||
        std::string describe() const override {
 | 
			
		||||
            return "takes reference";
 | 
			
		||||
        }
 | 
			
		||||
        bool match(int& i) const {
 | 
			
		||||
            return i == 22;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    TEST_CASE("Matchers can take references", "[matchers][templated][approvals]") {
 | 
			
		||||
        REQUIRE_THAT(22, ReferencingMatcher{});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} } // namespace MatchersTests
 | 
			
		||||
 | 
			
		||||
#ifdef __clang__
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user