Skip to content

Commit

Permalink
Add AllTrue, AnyTrue, NoneTrue matchers
Browse files Browse the repository at this point in the history
  • Loading branch information
schallerr authored and horenmar committed Jul 16, 2022
1 parent f993b70 commit 1bd2338
Show file tree
Hide file tree
Showing 22 changed files with 3,142 additions and 65 deletions.
14 changes: 14 additions & 0 deletions docs/matchers.md
Expand Up @@ -258,6 +258,12 @@ definitions to handle generic range-like types. These are:
* `SizeIs(Matcher size_matcher)`
* `Contains(T&& target_element, Comparator = std::equal_to<>{})`
* `Contains(Matcher element_matcher)`
* `AllMatch(Matcher element_matcher)`
* `NoneMatch(Matcher element_matcher)`
* `AnyMatch(Matcher element_matcher)`
* `AllTrue()`
* `NoneTrue()`
* `AnyTrue()`
`IsEmpty` should be self-explanatory. It successfully matches objects
that are empty according to either `std::empty`, or ADL-found `empty`
Expand All @@ -275,6 +281,14 @@ the target element. The other variant is constructed from a matcher,
in which case a range is accepted if any of its elements is accepted
by the provided matcher.
`AllMatch`, `NoneMatch`, and `AnyMatch` match ranges for which either
all, none, or any of the contained elements matches the given matcher,
respectively.
`AllTrue`, `NoneTrue`, and `AnyTrue` match ranges for which either
all, none, or any of the contained elements are `true`, respectively.
It works for ranges of `bool`s and ranges of elements (explicitly)
convertible to `bool`.
## Writing custom matchers (old style)
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Expand Up @@ -187,6 +187,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp
${SOURCES_DIR}/internal/catch_list.cpp
${SOURCES_DIR}/matchers/catch_matchers_floating_point.cpp
${SOURCES_DIR}/matchers/catch_matchers_quantifiers.cpp
${SOURCES_DIR}/matchers/catch_matchers_string.cpp
${SOURCES_DIR}/matchers/catch_matchers_templated.cpp
${SOURCES_DIR}/catch_message.cpp
Expand Down
24 changes: 24 additions & 0 deletions src/catch2/matchers/catch_matchers_quantifiers.cpp
@@ -0,0 +1,24 @@

// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)

// SPDX-License-Identifier: BSL-1.0
#include <catch2/matchers/catch_matchers_quantifiers.hpp>

namespace Catch {
namespace Matchers {
std::string AllTrueMatcher::describe() const { return "contains only true"; }

AllTrueMatcher AllTrue() { return AllTrueMatcher{}; }

std::string NoneTrueMatcher::describe() const { return "contains no true"; }

NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; }

std::string AnyTrueMatcher::describe() const { return "contains at least one true"; }

AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; }
} // namespace Matchers
} // namespace Catch
59 changes: 58 additions & 1 deletion src/catch2/matchers/catch_matchers_quantifiers.hpp
Expand Up @@ -85,7 +85,55 @@ namespace Catch {
}
};

// Creates a matcher that checks whether a range contains element matching a matcher
// Matcher for checking that all elements in range are true.
class AllTrueMatcher final : public MatcherGenericBase {
public:
std::string describe() const override;

template <typename RangeLike>
bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (!elem) {
return false;
}
}
return true;
}
};

// Matcher for checking that no element in range is true.
class NoneTrueMatcher final : public MatcherGenericBase {
public:
std::string describe() const override;

template <typename RangeLike>
bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (elem) {
return false;
}
}
return true;
}
};

// Matcher for checking that any element in range is true.
class AnyTrueMatcher final : public MatcherGenericBase {
public:
std::string describe() const override;

template <typename RangeLike>
bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (elem) {
return true;
}
}
return false;
}
};

// Creates a matcher that checks whether all elements in a range match a matcher
template <typename Matcher>
AllMatchMatcher<Matcher> AllMatch(Matcher&& matcher) {
return { CATCH_FORWARD(matcher) };
Expand All @@ -102,6 +150,15 @@ namespace Catch {
AnyMatchMatcher<Matcher> AnyMatch(Matcher&& matcher) {
return { CATCH_FORWARD(matcher) };
}

// Creates a matcher that checks whether all elements in a range are true
AllTrueMatcher AllTrue();

// Creates a matcher that checks whether no element in a range is true
NoneTrueMatcher NoneTrue();

// Creates a matcher that checks whether any element in a range is true
AnyTrueMatcher AnyTrue();
}
}

Expand Down
3 changes: 3 additions & 0 deletions tests/SelfTest/Baselines/automake.sw.approved.txt
Expand Up @@ -271,8 +271,11 @@ Message from section two
:test-result: FAIL Unexpected exceptions can be translated
:test-result: PASS Upcasting special member functions
:test-result: PASS Usage of AllMatch range matcher
:test-result: PASS Usage of AllTrue range matcher
:test-result: PASS Usage of AnyMatch range matcher
:test-result: PASS Usage of AnyTrue range matcher
:test-result: PASS Usage of NoneMatch range matcher
:test-result: PASS Usage of NoneTrue range matcher
:test-result: PASS Usage of the SizeIs range matcher
:test-result: PASS Use a custom approx
:test-result: PASS Variadic macros
Expand Down
3 changes: 3 additions & 0 deletions tests/SelfTest/Baselines/automake.sw.multi.approved.txt
Expand Up @@ -264,8 +264,11 @@
:test-result: FAIL Unexpected exceptions can be translated
:test-result: PASS Upcasting special member functions
:test-result: PASS Usage of AllMatch range matcher
:test-result: PASS Usage of AllTrue range matcher
:test-result: PASS Usage of AnyMatch range matcher
:test-result: PASS Usage of AnyTrue range matcher
:test-result: PASS Usage of NoneMatch range matcher
:test-result: PASS Usage of NoneTrue range matcher
:test-result: PASS Usage of the SizeIs range matcher
:test-result: PASS Use a custom approx
:test-result: PASS Variadic macros
Expand Down
57 changes: 57 additions & 0 deletions tests/SelfTest/Baselines/compact.sw.approved.txt
Expand Up @@ -2013,6 +2013,25 @@ MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[1] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[2] for: true
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[3]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[4]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: data, AllTrue() for: { true, true, true, true, true } contains only true
MatchersRanges.tests.cpp:<line number>: passed: data, AllTrue() for: { } contains only true
MatchersRanges.tests.cpp:<line number>: passed: data, !AllTrue() for: { true, true, false, true, true } not contains only true
MatchersRanges.tests.cpp:<line number>: passed: data, !AllTrue() for: { false, false, false, false, false } not contains only true
MatchersRanges.tests.cpp:<line number>: passed: data, AllTrue() for: { true, true, true, true, true } contains only true
MatchersRanges.tests.cpp:<line number>: passed: data, !AllTrue() for: { true, true, false, true, true } not contains only true
MatchersRanges.tests.cpp:<line number>: passed: data, !AllTrue() for: { false, false, false, false, false } not contains only true
MatchersRanges.tests.cpp:<line number>: passed: mocked, AllTrue() for: { true, true, true, true, true } contains only true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[0] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[1] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[2] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[3] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[4] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked, !AllTrue() for: { true, true, false, true, true } not contains only true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[0] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[1] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[2] for: true
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[3]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[4]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: data, AnyMatch(SizeIs(5)) for: { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } any match has size == 5
MatchersRanges.tests.cpp:<line number>: passed: data, !AnyMatch(Contains(0) && Contains(10)) for: { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } not any match ( contains element 0 and contains element 10 )
MatchersRanges.tests.cpp:<line number>: passed: needs_adl, AnyMatch( Predicate<int>( []( int elem ) { return elem < 3; } ) ) for: { 1, 2, 3, 4, 5 } any match matches undescribed predicate
Expand All @@ -2028,6 +2047,25 @@ MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[1]) for: !fal
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[2]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[3]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[4]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: data, AnyTrue() for: { true, true, true, true, true } contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: data, !AnyTrue() for: { } not contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: data, AnyTrue() for: { false, false, true, false, false } contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: data, !AnyTrue() for: { false, false, false, false, false } not contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: data, AnyTrue() for: { true, true, true, true, true } contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: data, AnyTrue() for: { false, false, true, false, false } contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: data, !AnyTrue() for: { false, false, false, false, false } not contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: mocked, AnyTrue() for: { false, false, false, false, true } contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[0] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[1] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[2] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[3] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[4] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked, AnyTrue() for: { false, false, true, true, true } contains at least one true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[0] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[1] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[2] for: true
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[3]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[4]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: data, NoneMatch(SizeIs(6)) for: { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } none match has size == 6
MatchersRanges.tests.cpp:<line number>: passed: data, !NoneMatch(Contains(0) && Contains(1)) for: { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } not none match ( contains element 0 and contains element 1 )
MatchersRanges.tests.cpp:<line number>: passed: needs_adl, NoneMatch( Predicate<int>( []( int elem ) { return elem > 6; } ) ) for: { 1, 2, 3, 4, 5 } none match matches undescribed predicate
Expand All @@ -2043,6 +2081,25 @@ MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[1]) for: !fal
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[2]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[3]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[4]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: data, !NoneTrue() for: { true, true, true, true, true } not contains no true
MatchersRanges.tests.cpp:<line number>: passed: data, NoneTrue() for: { } contains no true
MatchersRanges.tests.cpp:<line number>: passed: data, !NoneTrue() for: { false, false, true, false, false } not contains no true
MatchersRanges.tests.cpp:<line number>: passed: data, NoneTrue() for: { false, false, false, false, false } contains no true
MatchersRanges.tests.cpp:<line number>: passed: data, !NoneTrue() for: { true, true, true, true, true } not contains no true
MatchersRanges.tests.cpp:<line number>: passed: data, !NoneTrue() for: { false, false, true, false, false } not contains no true
MatchersRanges.tests.cpp:<line number>: passed: data, NoneTrue() for: { false, false, false, false, false } contains no true
MatchersRanges.tests.cpp:<line number>: passed: mocked, NoneTrue() for: { false, false, false, false, false } contains no true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[0] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[1] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[2] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[3] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[4] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked, !NoneTrue() for: { false, false, true, true, true } not contains no true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[0] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[1] for: true
MatchersRanges.tests.cpp:<line number>: passed: mocked.m_derefed[2] for: true
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[3]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: !(mocked.m_derefed[4]) for: !false
MatchersRanges.tests.cpp:<line number>: passed: empty_vec, SizeIs(0) for: { } has size == 0
MatchersRanges.tests.cpp:<line number>: passed: empty_vec, !SizeIs(2) for: { } not has size == 2
MatchersRanges.tests.cpp:<line number>: passed: empty_vec, SizeIs(Lt(2)) for: { } size matches is less than 2
Expand Down

0 comments on commit 1bd2338

Please sign in to comment.