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:
melak47 2020-02-16 11:19:10 +01:00 committed by GitHub
parent db1a0465dc
commit 17c4b2d093
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1192 additions and 16 deletions

View File

@ -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

View File

@ -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 );
}

View File

@ -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

View 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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}"/>

View File

@ -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(&quot;not there&quot;, 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(&quot;this STRING contains 'abc' as a substring&quot;))">

View File

@ -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

View File

@ -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']

View File

@ -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() &amp;&amp; MatcherB() &amp;&amp; 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() &amp;&amp; MatcherB() &amp;&amp; MatcherC() &amp;&amp; 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() &amp;&amp; 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&lt;std::vector&lt;int>>([](auto const&amp; vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") &amp;&amp; !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") &amp;&amp; EqualsRange(arr) &amp;&amp; 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") &amp;&amp; !EqualsRange(bad_arr) &amp;&amp; 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) &amp;&amp; StartsWith("foo") &amp;&amp; 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) &amp;&amp; StartsWith("foo") &amp;&amp; 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") &amp;&amp; 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") &amp;&amp; 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>
&amp;EvilMatcher(), EvilAddressOfOperatorUsed
</Original>
<Expanded>
&amp;EvilMatcher(), EvilAddressOfOperatorUsed
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
EvilMatcher() || EvilMatcher() &amp;&amp; !EvilMatcher()
</Original>
<Expanded>
EvilMatcher() || EvilMatcher() &amp;&amp; !EvilMatcher()
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
(EvilMatcher() &amp;&amp; EvilMatcher()) || !EvilMatcher()
</Original>
<Expanded>
(EvilMatcher() &amp;&amp; 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>

View File

@ -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__