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
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
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_leak_detector.h
${SOURCES_DIR}/catch_list.h ${SOURCES_DIR}/catch_list.h
${SOURCES_DIR}/matchers/catch_matchers.hpp ${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_exception.hpp
${SOURCES_DIR}/matchers/catch_matchers_floating.hpp ${SOURCES_DIR}/matchers/catch_matchers_floating.hpp
${SOURCES_DIR}/matchers/catch_matchers_generic.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

View File

@ -50,6 +50,7 @@ set(TEST_SOURCES
${SELF_TEST_DIR}/UsageTests/ToStringWhich.tests.cpp ${SELF_TEST_DIR}/UsageTests/ToStringWhich.tests.cpp
${SELF_TEST_DIR}/UsageTests/Tricky.tests.cpp ${SELF_TEST_DIR}/UsageTests/Tricky.tests.cpp
${SELF_TEST_DIR}/UsageTests/VariadicMacros.tests.cpp ${SELF_TEST_DIR}/UsageTests/VariadicMacros.tests.cpp
${SELF_TEST_DIR}/UsageTests/MatchersRanges.tests.cpp
${SELF_TEST_DIR}/UsageTests/Matchers.tests.cpp ${SELF_TEST_DIR}/UsageTests/Matchers.tests.cpp
) )
CheckFileList(TEST_SOURCES ${SELF_TEST_DIR}) CheckFileList(TEST_SOURCES ${SELF_TEST_DIR})

View File

@ -80,6 +80,7 @@ Nor would this
:test-result: PASS Approximate comparisons with mixed numeric types :test-result: PASS Approximate comparisons with mixed numeric types
:test-result: PASS Arbitrary predicate matcher :test-result: PASS Arbitrary predicate matcher
:test-result: PASS Assertions then sections :test-result: PASS Assertions then sections
:test-result: PASS Basic use of the Contains range matcher
:test-result: PASS CAPTURE can deal with complex expressions :test-result: PASS CAPTURE can deal with complex expressions
:test-result: PASS CAPTURE can deal with complex expressions involving commas :test-result: PASS CAPTURE can deal with complex expressions involving commas
:test-result: PASS CAPTURE parses string and character constants :test-result: PASS CAPTURE parses string and character constants

View File

@ -244,6 +244,18 @@ Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: true Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: true Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: true Tricky.tests.cpp:<line number>: passed: true
MatchersRanges.tests.cpp:<line number>: passed: a, Contains(1) for: { 1, 2, 3 } contains element 1
MatchersRanges.tests.cpp:<line number>: passed: b, Contains(1) for: { 0, 1, 2 } contains element 1
MatchersRanges.tests.cpp:<line number>: passed: c, !Contains(1) for: { 4, 5, 6 } not contains element 1
MatchersRanges.tests.cpp:<line number>: passed: a, Contains(0, close_enough) for: { 1, 2, 3 } contains element 0
MatchersRanges.tests.cpp:<line number>: passed: b, Contains(0, close_enough) for: { 0, 1, 2 } contains element 0
MatchersRanges.tests.cpp:<line number>: passed: c, !Contains(0, close_enough) for: { 4, 5, 6 } not contains element 0
MatchersRanges.tests.cpp:<line number>: passed: a, Contains(4, [](auto&& lhs, size_t sz) { return lhs.size() == sz; }) for: { "abc", "abcd", "abcde" } contains element 4
MatchersRanges.tests.cpp:<line number>: passed: in, Contains(1) for: { 1, 2, 3, 4, 5 } contains element 1
MatchersRanges.tests.cpp:<line number>: passed: in, !Contains(8) for: { 1, 2, 3, 4, 5 } not contains element 8
MatchersRanges.tests.cpp:<line number>: passed: in, Contains(MoveOnlyTestElement{ 2 }) for: { 1, 2, 3 } contains element 2
MatchersRanges.tests.cpp:<line number>: passed: in, !Contains(MoveOnlyTestElement{ 9 }) for: { 1, 2, 3 } not contains element 9
MatchersRanges.tests.cpp:<line number>: passed: in, Contains(Catch::Matchers::WithinAbs(0.5, 0.5)) for: { 1.0, 2.0, 3.0, 0.0 } contains element matching is within 0.5 of 0.5
Message.tests.cpp:<line number>: passed: with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' Message.tests.cpp:<line number>: passed: with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true'
Message.tests.cpp:<line number>: passed: with 7 messages: 'std::vector<int>{1, 2, 3}[0, 1, 2] := 3' and 'std::vector<int>{1, 2, 3}[(0, 1)] := 2' and 'std::vector<int>{1, 2, 3}[0] := 1' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' Message.tests.cpp:<line number>: passed: with 7 messages: 'std::vector<int>{1, 2, 3}[0, 1, 2] := 3' and 'std::vector<int>{1, 2, 3}[(0, 1)] := 2' and 'std::vector<int>{1, 2, 3}[0] := 1' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3'
Message.tests.cpp:<line number>: passed: with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' Message.tests.cpp:<line number>: passed: with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{''

View File

@ -1380,6 +1380,6 @@ due to unexpected exception with message:
Why would you throw a std::string? Why would you throw a std::string?
=============================================================================== ===============================================================================
test cases: 328 | 254 passed | 70 failed | 4 failed as expected test cases: 329 | 255 passed | 70 failed | 4 failed as expected
assertions: 1839 | 1687 passed | 131 failed | 21 failed as expected assertions: 1851 | 1699 passed | 131 failed | 21 failed as expected

View File

@ -1948,6 +1948,108 @@ Tricky.tests.cpp:<line number>
Tricky.tests.cpp:<line number>: PASSED: Tricky.tests.cpp:<line number>: PASSED:
REQUIRE( true ) REQUIRE( true )
-------------------------------------------------------------------------------
Basic use of the Contains range matcher
Different argument ranges, same element type, default comparison
-------------------------------------------------------------------------------
MatchersRanges.tests.cpp:<line number>
...............................................................................
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( a, Contains(1) )
with expansion:
{ 1, 2, 3 } contains element 1
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( b, Contains(1) )
with expansion:
{ 0, 1, 2 } contains element 1
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( c, !Contains(1) )
with expansion:
{ 4, 5, 6 } not contains element 1
-------------------------------------------------------------------------------
Basic use of the Contains range matcher
Different argument ranges, same element type, custom comparison
-------------------------------------------------------------------------------
MatchersRanges.tests.cpp:<line number>
...............................................................................
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( a, Contains(0, close_enough) )
with expansion:
{ 1, 2, 3 } contains element 0
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( b, Contains(0, close_enough) )
with expansion:
{ 0, 1, 2 } contains element 0
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( c, !Contains(0, close_enough) )
with expansion:
{ 4, 5, 6 } not contains element 0
-------------------------------------------------------------------------------
Basic use of the Contains range matcher
Different element type, custom comparisons
-------------------------------------------------------------------------------
MatchersRanges.tests.cpp:<line number>
...............................................................................
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( a, Contains(4, [](auto&& lhs, size_t sz) { return lhs.size() == sz; }) )
with expansion:
{ "abc", "abcd", "abcde" } contains element 4
-------------------------------------------------------------------------------
Basic use of the Contains range matcher
Can handle type that requires ADL-found free function begin and end
-------------------------------------------------------------------------------
MatchersRanges.tests.cpp:<line number>
...............................................................................
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( in, Contains(1) )
with expansion:
{ 1, 2, 3, 4, 5 } contains element 1
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( in, !Contains(8) )
with expansion:
{ 1, 2, 3, 4, 5 } not contains element 8
-------------------------------------------------------------------------------
Basic use of the Contains range matcher
Initialization with move only types
-------------------------------------------------------------------------------
MatchersRanges.tests.cpp:<line number>
...............................................................................
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( in, Contains(MoveOnlyTestElement{ 2 }) )
with expansion:
{ 1, 2, 3 } contains element 2
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( in, !Contains(MoveOnlyTestElement{ 9 }) )
with expansion:
{ 1, 2, 3 } not contains element 9
-------------------------------------------------------------------------------
Basic use of the Contains range matcher
Matching using matcher
-------------------------------------------------------------------------------
MatchersRanges.tests.cpp:<line number>
...............................................................................
MatchersRanges.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( in, Contains(Catch::Matchers::WithinAbs(0.5, 0.5)) )
with expansion:
{ 1.0, 2.0, 3.0, 0.0 } contains element matching is within 0.5 of 0.5
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
CAPTURE can deal with complex expressions CAPTURE can deal with complex expressions
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -14396,6 +14498,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED: Misc.tests.cpp:<line number>: PASSED:
=============================================================================== ===============================================================================
test cases: 328 | 238 passed | 86 failed | 4 failed as expected test cases: 329 | 239 passed | 86 failed | 4 failed as expected
assertions: 1856 | 1687 passed | 148 failed | 21 failed as expected assertions: 1868 | 1699 passed | 148 failed | 21 failed as expected

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact <testsuitesloose text artifact
> >
<testsuite name="<exe-name>" errors="17" failures="132" tests="1857" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <testsuite name="<exe-name>" errors="17" failures="132" tests="1869" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties> <properties>
<property name="filters" value="~[!nonportable]~[!benchmark]~[approvals] *"/> <property name="filters" value="~[!nonportable]~[!benchmark]~[approvals] *"/>
<property name="random-seed" value="1"/> <property name="random-seed" value="1"/>
@ -342,6 +342,12 @@ Exception.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Assertions then sections/A section" time="{duration}"/> <testcase classname="<exe-name>.global" name="Assertions then sections/A section" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Assertions then sections/A section/Another section" time="{duration}"/> <testcase classname="<exe-name>.global" name="Assertions then sections/A section/Another section" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Assertions then sections/A section/Another other section" time="{duration}"/> <testcase classname="<exe-name>.global" name="Assertions then sections/A section/Another other section" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Basic use of the Contains range matcher/Different argument ranges, same element type, default comparison" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Basic use of the Contains range matcher/Different argument ranges, same element type, custom comparison" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Basic use of the Contains range matcher/Different element type, custom comparisons" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Basic use of the Contains range matcher/Can handle type that requires ADL-found free function begin and end" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Basic use of the Contains range matcher/Initialization with move only types" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Basic use of the Contains range matcher/Matching using matcher" time="{duration}"/>
<testcase classname="<exe-name>.global" name="CAPTURE can deal with complex expressions" time="{duration}"/> <testcase classname="<exe-name>.global" name="CAPTURE can deal with complex expressions" time="{duration}"/>
<testcase classname="<exe-name>.global" name="CAPTURE can deal with complex expressions involving commas" time="{duration}"/> <testcase classname="<exe-name>.global" name="CAPTURE can deal with complex expressions involving commas" time="{duration}"/>
<testcase classname="<exe-name>.global" name="CAPTURE parses string and character constants" time="{duration}"/> <testcase classname="<exe-name>.global" name="CAPTURE parses string and character constants" time="{duration}"/>

View File

@ -1215,6 +1215,14 @@ Matchers.tests.cpp:<line number>
</failure> </failure>
</testCase> </testCase>
</file> </file>
<file path="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp">
<testCase name="Basic use of the Contains range matcher/Different argument ranges, same element type, default comparison" duration="{duration}"/>
<testCase name="Basic use of the Contains range matcher/Different argument ranges, same element type, custom comparison" duration="{duration}"/>
<testCase name="Basic use of the Contains range matcher/Different element type, custom comparisons" duration="{duration}"/>
<testCase name="Basic use of the Contains range matcher/Can handle type that requires ADL-found free function begin and end" duration="{duration}"/>
<testCase name="Basic use of the Contains range matcher/Initialization with move only types" duration="{duration}"/>
<testCase name="Basic use of the Contains range matcher/Matching using matcher" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/UsageTests/Message.tests.cpp"> <file path="tests/<exe-name>/UsageTests/Message.tests.cpp">
<testCase name="#1455 - INFO and WARN can start with a linebreak" duration="{duration}"/> <testCase name="#1455 - INFO and WARN can start with a linebreak" duration="{duration}"/>
<testCase name="CAPTURE can deal with complex expressions" duration="{duration}"/> <testCase name="CAPTURE can deal with complex expressions" duration="{duration}"/>

View File

@ -486,6 +486,30 @@ ok {test-number} - true
ok {test-number} - true ok {test-number} - true
# Assertions then sections # Assertions then sections
ok {test-number} - true ok {test-number} - true
# Basic use of the Contains range matcher
ok {test-number} - a, Contains(1) for: { 1, 2, 3 } contains element 1
# Basic use of the Contains range matcher
ok {test-number} - b, Contains(1) for: { 0, 1, 2 } contains element 1
# Basic use of the Contains range matcher
ok {test-number} - c, !Contains(1) for: { 4, 5, 6 } not contains element 1
# Basic use of the Contains range matcher
ok {test-number} - a, Contains(0, close_enough) for: { 1, 2, 3 } contains element 0
# Basic use of the Contains range matcher
ok {test-number} - b, Contains(0, close_enough) for: { 0, 1, 2 } contains element 0
# Basic use of the Contains range matcher
ok {test-number} - c, !Contains(0, close_enough) for: { 4, 5, 6 } not contains element 0
# Basic use of the Contains range matcher
ok {test-number} - a, Contains(4, [](auto&& lhs, size_t sz) { return lhs.size() == sz; }) for: { "abc", "abcd", "abcde" } contains element 4
# Basic use of the Contains range matcher
ok {test-number} - in, Contains(1) for: { 1, 2, 3, 4, 5 } contains element 1
# Basic use of the Contains range matcher
ok {test-number} - in, !Contains(8) for: { 1, 2, 3, 4, 5 } not contains element 8
# Basic use of the Contains range matcher
ok {test-number} - in, Contains(MoveOnlyTestElement{ 2 }) for: { 1, 2, 3 } contains element 2
# Basic use of the Contains range matcher
ok {test-number} - in, !Contains(MoveOnlyTestElement{ 9 }) for: { 1, 2, 3 } not contains element 9
# Basic use of the Contains range matcher
ok {test-number} - in, Contains(Catch::Matchers::WithinAbs(0.5, 0.5)) for: { 1.0, 2.0, 3.0, 0.0 } contains element matching is within 0.5 of 0.5
# CAPTURE can deal with complex expressions # CAPTURE can deal with complex expressions
ok {test-number} - with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' ok {test-number} - with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true'
# CAPTURE can deal with complex expressions involving commas # CAPTURE can deal with complex expressions involving commas
@ -3704,5 +3728,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
ok {test-number} - ok {test-number} -
# xmlentitycheck # xmlentitycheck
ok {test-number} - ok {test-number} -
1..1848 1..1860

View File

@ -193,6 +193,8 @@ Exception.tests.cpp:<line number>|nunexpected exception with message:|n "unexpe
##teamcity[testFinished name='Arbitrary predicate matcher' duration="{duration}"] ##teamcity[testFinished name='Arbitrary predicate matcher' duration="{duration}"]
##teamcity[testStarted name='Assertions then sections'] ##teamcity[testStarted name='Assertions then sections']
##teamcity[testFinished name='Assertions then sections' duration="{duration}"] ##teamcity[testFinished name='Assertions then sections' duration="{duration}"]
##teamcity[testStarted name='Basic use of the Contains range matcher']
##teamcity[testFinished name='Basic use of the Contains range matcher' duration="{duration}"]
##teamcity[testStarted name='CAPTURE can deal with complex expressions'] ##teamcity[testStarted name='CAPTURE can deal with complex expressions']
##teamcity[testFinished name='CAPTURE can deal with complex expressions' duration="{duration}"] ##teamcity[testFinished name='CAPTURE can deal with complex expressions' duration="{duration}"]
##teamcity[testStarted name='CAPTURE can deal with complex expressions involving commas'] ##teamcity[testStarted name='CAPTURE can deal with complex expressions involving commas']

View File

@ -2178,6 +2178,123 @@ Nor would this
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Basic use of the Contains range matcher" tags="[contains][matchers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Section name="Different argument ranges, same element type, default comparison" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
a, Contains(1)
</Original>
<Expanded>
{ 1, 2, 3 } contains element 1
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
b, Contains(1)
</Original>
<Expanded>
{ 0, 1, 2 } contains element 1
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
c, !Contains(1)
</Original>
<Expanded>
{ 4, 5, 6 } not contains element 1
</Expanded>
</Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section>
<Section name="Different argument ranges, same element type, custom comparison" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
a, Contains(0, close_enough)
</Original>
<Expanded>
{ 1, 2, 3 } contains element 0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
b, Contains(0, close_enough)
</Original>
<Expanded>
{ 0, 1, 2 } contains element 0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
c, !Contains(0, close_enough)
</Original>
<Expanded>
{ 4, 5, 6 } not contains element 0
</Expanded>
</Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section>
<Section name="Different element type, custom comparisons" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
a, Contains(4, [](auto&amp;&amp; lhs, size_t sz) { return lhs.size() == sz; })
</Original>
<Expanded>
{ "abc", "abcd", "abcde" } contains element 4
</Expanded>
</Expression>
<OverallResults successes="1" failures="0" expectedFailures="0"/>
</Section>
<Section name="Can handle type that requires ADL-found free function begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
in, Contains(1)
</Original>
<Expanded>
{ 1, 2, 3, 4, 5 } contains element 1
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
in, !Contains(8)
</Original>
<Expanded>
{ 1, 2, 3, 4, 5 } not contains element 8
</Expanded>
</Expression>
<OverallResults successes="2" failures="0" expectedFailures="0"/>
</Section>
<Section name="Initialization with move only types" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
in, Contains(MoveOnlyTestElement{ 2 })
</Original>
<Expanded>
{ 1, 2, 3 } contains element 2
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
in, !Contains(MoveOnlyTestElement{ 9 })
</Original>
<Expanded>
{ 1, 2, 3 } not contains element 9
</Expanded>
</Expression>
<OverallResults successes="2" failures="0" expectedFailures="0"/>
</Section>
<Section name="Matching using matcher" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
<Original>
in, Contains(Catch::Matchers::WithinAbs(0.5, 0.5))
</Original>
<Expanded>
{ 1.0, 2.0, 3.0, 0.0 } contains element matching is within 0.5 of 0.5
</Expanded>
</Expression>
<OverallResults successes="1" failures="0" expectedFailures="0"/>
</Section>
<OverallResult success="true"/>
</TestCase>
<TestCase name="CAPTURE can deal with complex expressions" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <TestCase name="CAPTURE can deal with complex expressions" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
<Info> <Info>
a := 1 a := 1
@ -17264,7 +17381,7 @@ loose text artifact
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<OverallResults successes="1687" failures="149" expectedFailures="21"/> <OverallResults successes="1699" failures="149" expectedFailures="21"/>
</Group> </Group>
<OverallResults successes="1687" failures="148" expectedFailures="21"/> <OverallResults successes="1699" failures="148" expectedFailures="21"/>
</Catch> </Catch>

View File

@ -0,0 +1,107 @@
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_contains.hpp>
#include <catch2/matchers/catch_matchers_floating.hpp>
#include <array>
#include <cmath>
#include <list>
#include <vector>
namespace unrelated {
class needs_ADL_begin {
std::array<int, 5> elements{ {1, 2, 3, 4, 5} };
public:
using iterator = std::array<int, 5>::iterator;
using const_iterator = std::array<int, 5>::const_iterator;
const_iterator Begin() const { return elements.begin(); }
const_iterator End() const { return elements.end(); }
friend const_iterator begin(needs_ADL_begin const& lhs) {
return lhs.Begin();
}
friend const_iterator end(needs_ADL_begin const& rhs) {
return rhs.End();
}
};
} // end anon namespace
struct MoveOnlyTestElement {
int num = 0;
MoveOnlyTestElement(int n) :num(n) {}
MoveOnlyTestElement(MoveOnlyTestElement&& rhs) = default;
MoveOnlyTestElement& operator=(MoveOnlyTestElement&& rhs) = default;
friend bool operator==(MoveOnlyTestElement const& lhs, MoveOnlyTestElement const& rhs) {
return lhs.num == rhs.num;
}
friend std::ostream& operator<<(std::ostream& out, MoveOnlyTestElement const& elem) {
out << elem.num;
return out;
}
};
TEST_CASE("Basic use of the Contains range matcher", "[matchers][templated][contains]") {
using Catch::Matchers::Contains;
SECTION("Different argument ranges, same element type, default comparison") {
std::array<int, 3> a{ { 1,2,3 } };
std::vector<int> b{ 0,1,2 };
std::list<int> c{ 4,5,6 };
// A contains 1
REQUIRE_THAT(a, Contains(1));
// B contains 1
REQUIRE_THAT(b, Contains(1));
// C does not contain 1
REQUIRE_THAT(c, !Contains(1));
}
SECTION("Different argument ranges, same element type, custom comparison") {
std::array<int, 3> a{ { 1,2,3 } };
std::vector<int> b{ 0,1,2 };
std::list<int> c{ 4,5,6 };
auto close_enough = [](int lhs, int rhs) { return std::abs(lhs - rhs) <= 1; };
// A contains 1, which is "close enough" to 0
REQUIRE_THAT(a, Contains(0, close_enough));
// B contains 0 directly
REQUIRE_THAT(b, Contains(0, close_enough));
// C does not contain anything "close enough" to 0
REQUIRE_THAT(c, !Contains(0, close_enough));
}
SECTION("Different element type, custom comparisons") {
std::array<std::string, 3> a{ { "abc", "abcd" , "abcde" } };
REQUIRE_THAT(a, Contains(4, [](auto&& lhs, size_t sz) {
return lhs.size() == sz;
}));
}
SECTION("Can handle type that requires ADL-found free function begin and end") {
unrelated::needs_ADL_begin in;
REQUIRE_THAT(in, Contains(1));
REQUIRE_THAT(in, !Contains(8));
}
SECTION("Initialization with move only types") {
std::array<MoveOnlyTestElement, 3> in{ { MoveOnlyTestElement{ 1 }, MoveOnlyTestElement{ 2 }, MoveOnlyTestElement{ 3 } } };
REQUIRE_THAT(in, Contains(MoveOnlyTestElement{ 2 }));
REQUIRE_THAT(in, !Contains(MoveOnlyTestElement{ 9 }));
}
SECTION("Matching using matcher") {
std::array<double, 4> in{ {1, 2, 3} };
REQUIRE_THAT(in, Contains(Catch::Matchers::WithinAbs(0.5, 0.5)));
}
}