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
15 changed files with 1192 additions and 16 deletions

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