Add generic Contains matcher

It matches a range iff the range contains a specific element,
or an element in the range matches the provided matcher.
This commit is contained in:
Martin Hořeňovský
2020-03-02 14:25:43 +01:00
parent 89e857349b
commit 007efc173a
13 changed files with 491 additions and 8 deletions

View File

@@ -73,6 +73,7 @@ set(INTERNAL_HEADERS
${SOURCES_DIR}/catch_leak_detector.h
${SOURCES_DIR}/catch_list.h
${SOURCES_DIR}/matchers/catch_matchers.hpp
${SOURCES_DIR}/matchers/catch_matchers_contains.hpp
${SOURCES_DIR}/matchers/catch_matchers_exception.hpp
${SOURCES_DIR}/matchers/catch_matchers_floating.hpp
${SOURCES_DIR}/matchers/catch_matchers_generic.hpp

View File

@@ -0,0 +1,102 @@
/*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
#include <catch2/matchers/catch_matchers_templates.hpp>
#include <algorithm>
#include <functional>
#include <utility>
namespace Catch {
namespace Matchers {
//! Matcher for checking that an element in range is equal to specific element
template <typename T, typename Equality>
class ContainsElementMatcher final : public MatcherGenericBase {
T m_desired;
Equality m_eq;
public:
template <typename T2, typename Equality2>
ContainsElementMatcher(T2&& target, Equality2&& predicate):
m_desired(std::forward<T2>(target)),
m_eq(std::forward<Equality2>(predicate))
{}
std::string describe() const override {
return "contains element " + Catch::Detail::stringify(m_desired);
}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
using std::begin; using std::end;
return end(rng) != std::find_if(begin(rng), end(rng),
[&](auto const& elem) {
return m_eq(elem, m_desired);
});
}
};
//! Meta-matcher for checking that an element in a range matches a specific matcher
template <typename Matcher>
class ContainsMatcherMatcher final : public MatcherGenericBase {
Matcher m_matcher;
public:
// Note that we do a copy+move to avoid having to SFINAE this
// constructor (and also avoid some perfect forwarding failure
// cases)
ContainsMatcherMatcher(Matcher matcher):
m_matcher(std::move(matcher))
{}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
using std::begin; using std::endl;
for (auto&& elem : rng) {
if (m_matcher.match(elem)) {
return true;
}
}
return false;
}
std::string describe() const override {
return "contains element matching " + m_matcher.describe();
}
};
/**
* Creates a matcher that checks whether a range contains a specific element.
*
* Uses `std::equal_to` to do the comparison
*/
template <typename T>
std::enable_if_t<!Detail::is_matcher<T>::value,
ContainsElementMatcher<T, std::equal_to<>>> Contains(T&& elem) {
return { std::forward<T>(elem), std::equal_to<>{} };
}
//! Creates a matcher that checks whether a range contains element matching a matcher
template <typename Matcher>
std::enable_if_t<Detail::is_matcher<Matcher>::value,
ContainsMatcherMatcher<Matcher>> Contains(Matcher&& matcher) {
return { std::forward<Matcher>(matcher) };
}
/**
* Creates a matcher that checks whether a range contains a specific element.
*
* Uses `eq` to do the comparisons
*/
template <typename T, typename Equality>
ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
return { std::forward<T>(elem), std::forward<Equality>(eq) };
}
}
}
#endif // TWOBLUECUBES_CATCH_MATCHERS_CONTAINS_HPP_INCLUDED