Add UnorderedEqualsMatcher for vectors

Closes #1093
This commit is contained in:
Martin Hořeňovský 2017-12-07 17:07:25 +01:00
parent 3035120dc7
commit 495d2458e0
7 changed files with 276 additions and 6 deletions

View File

@ -10,10 +10,33 @@
#include "catch_matchers.h" #include "catch_matchers.h"
#include <algorithm>
namespace Catch { namespace Catch {
namespace Matchers { namespace Matchers {
namespace Vector { namespace Vector {
namespace Detail {
template <typename InputIterator, typename T>
size_t count(InputIterator first, InputIterator last, T const& item) {
size_t cnt = 0;
for (; first != last; ++first) {
if (*first == item) {
++cnt;
}
}
return cnt;
}
template <typename InputIterator, typename T>
bool contains(InputIterator first, InputIterator last, T const& item) {
for (; first != last; ++first) {
if (*first == item) {
return true;
}
}
return false;
}
}
template<typename T> template<typename T>
struct ContainsElementMatcher : MatcherBase<std::vector<T>> { struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
@ -89,6 +112,46 @@ namespace Matchers {
std::vector<T> const& m_comparator; std::vector<T> const& m_comparator;
}; };
template<typename T>
struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
bool match(std::vector<T> const& vec) const override {
// Note: This is a reimplementation of std::is_permutation,
// because I don't want to include <algorithm> inside the common path
if (m_target.size() != vec.size()) {
return false;
}
auto lfirst = m_target.begin(), llast = m_target.end();
auto rfirst = vec.begin(), rlast = vec.end();
// Cut common prefix to optimize checking of permuted parts
while (lfirst != llast && *lfirst != *rfirst) {
++lfirst; ++rfirst;
}
if (lfirst == llast) {
return true;
}
for (auto mid = lfirst; mid != llast; ++mid) {
// Skip already counted items
if (Detail::contains(lfirst, mid, *mid)) {
continue;
}
size_t num_vec = Detail::count(rfirst, rlast, *mid);
if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) {
return false;
}
}
return true;
}
std::string describe() const override {
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
}
private:
std::vector<T> const& m_target;
};
} // namespace Vector } // namespace Vector
// The following functions create the actual matcher objects. // The following functions create the actual matcher objects.
@ -109,6 +172,11 @@ namespace Matchers {
return Vector::EqualsMatcher<T>( comparator ); return Vector::EqualsMatcher<T>( comparator );
} }
template<typename T>
Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
return Vector::UnorderedEqualsMatcher<T>(target);
}
} // namespace Matchers } // namespace Matchers
} // namespace Catch } // namespace Catch

View File

@ -828,6 +828,10 @@ Matchers.tests.cpp:<line number>: passed: v, VectorContains(1) && VectorContains
Matchers.tests.cpp:<line number>: passed: v, Equals(v) for: { 1, 2, 3 } Equals: { 1, 2, 3 } Matchers.tests.cpp:<line number>: passed: v, Equals(v) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: passed: empty, Equals(empty) for: { } Equals: { } Matchers.tests.cpp:<line number>: passed: empty, Equals(empty) for: { } Equals: { }
Matchers.tests.cpp:<line number>: passed: v, Equals(v2) for: { 1, 2, 3 } Equals: { 1, 2, 3 } Matchers.tests.cpp:<line number>: passed: v, Equals(v2) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: passed: v, UnorderedEquals(v) for: { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: passed: empty, UnorderedEquals(empty) for: { } UnorderedEquals: { }
Matchers.tests.cpp:<line number>: passed: permuted, UnorderedEquals(v) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: passed: permuted, UnorderedEquals(v) for: { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: failed: v, VectorContains(-1) for: { 1, 2, 3 } Contains: -1 Matchers.tests.cpp:<line number>: failed: v, VectorContains(-1) for: { 1, 2, 3 } Contains: -1
Matchers.tests.cpp:<line number>: failed: empty, VectorContains(1) for: { } Contains: 1 Matchers.tests.cpp:<line number>: failed: empty, VectorContains(1) for: { } Contains: 1
Matchers.tests.cpp:<line number>: failed: empty, Contains(v) for: { } Contains: { 1, 2, 3 } Matchers.tests.cpp:<line number>: failed: empty, Contains(v) for: { } Contains: { 1, 2, 3 }
@ -836,6 +840,10 @@ Matchers.tests.cpp:<line number>: failed: v, Equals(v2) for: { 1, 2, 3 } Equals:
Matchers.tests.cpp:<line number>: failed: v2, Equals(v) for: { 1, 2 } Equals: { 1, 2, 3 } Matchers.tests.cpp:<line number>: failed: v2, Equals(v) for: { 1, 2 } Equals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: failed: empty, Equals(v) for: { } Equals: { 1, 2, 3 } Matchers.tests.cpp:<line number>: failed: empty, Equals(v) for: { } Equals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: failed: v, Equals(empty) for: { 1, 2, 3 } Equals: { } Matchers.tests.cpp:<line number>: failed: v, Equals(empty) for: { 1, 2, 3 } Equals: { }
Matchers.tests.cpp:<line number>: failed: v, UnorderedEquals(empty) for: { 1, 2, 3 } UnorderedEquals: { }
Matchers.tests.cpp:<line number>: failed: empty, UnorderedEquals(v) for: { } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: failed: permuted, UnorderedEquals(v) for: { 1, 3 } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: failed: permuted, UnorderedEquals(v) for: { 3, 1 } UnorderedEquals: { 1, 2, 3 }
Exception.tests.cpp:<line number>: passed: thisThrows(), std::domain_error Exception.tests.cpp:<line number>: passed: thisThrows(), std::domain_error
Exception.tests.cpp:<line number>: passed: thisDoesntThrow() Exception.tests.cpp:<line number>: passed: thisDoesntThrow()
Exception.tests.cpp:<line number>: passed: thisThrows() Exception.tests.cpp:<line number>: passed: thisThrows()
@ -1023,5 +1031,5 @@ Misc.tests.cpp:<line number>: passed: v.size() == 5 for: 5 == 5
Misc.tests.cpp:<line number>: passed: v.capacity() >= 5 for: 5 >= 5 Misc.tests.cpp:<line number>: passed: v.capacity() >= 5 for: 5 >= 5
Misc.tests.cpp:<line number>: passed: Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed: Misc.tests.cpp:<line number>: passed:
Failed 50 test cases, failed 106 assertions. Failed 50 test cases, failed 110 assertions.

View File

@ -809,6 +809,33 @@ Matchers.tests.cpp:<line number>: FAILED:
with expansion: with expansion:
{ 1, 2, 3 } Equals: { } { 1, 2, 3 } Equals: { }
-------------------------------------------------------------------------------
Vector matchers that fail
UnorderedEquals
-------------------------------------------------------------------------------
Matchers.tests.cpp:<line number>
...............................................................................
Matchers.tests.cpp:<line number>: FAILED:
CHECK_THAT( v, UnorderedEquals(empty) )
with expansion:
{ 1, 2, 3 } UnorderedEquals: { }
Matchers.tests.cpp:<line number>: FAILED:
CHECK_THAT( empty, UnorderedEquals(v) )
with expansion:
{ } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: FAILED:
CHECK_THAT( permuted, UnorderedEquals(v) )
with expansion:
{ 1, 3 } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: FAILED:
CHECK_THAT( permuted, UnorderedEquals(v) )
with expansion:
{ 3, 1 } UnorderedEquals: { 1, 2, 3 }
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
When unchecked exceptions are thrown directly they are always failures When unchecked exceptions are thrown directly they are always failures
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -1054,5 +1081,5 @@ with expansion:
=============================================================================== ===============================================================================
test cases: 191 | 139 passed | 48 failed | 4 failed as expected test cases: 191 | 139 passed | 48 failed | 4 failed as expected
assertions: 963 | 839 passed | 103 failed | 21 failed as expected assertions: 971 | 843 passed | 107 failed | 21 failed as expected

View File

@ -6480,6 +6480,37 @@ PASSED:
with expansion: with expansion:
{ 1, 2, 3 } Equals: { 1, 2, 3 } { 1, 2, 3 } Equals: { 1, 2, 3 }
-------------------------------------------------------------------------------
Vector matchers
UnorderedEquals
-------------------------------------------------------------------------------
Matchers.tests.cpp:<line number>
...............................................................................
Matchers.tests.cpp:<line number>:
PASSED:
CHECK_THAT( v, UnorderedEquals(v) )
with expansion:
{ 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>:
PASSED:
CHECK_THAT( empty, UnorderedEquals(empty) )
with expansion:
{ } UnorderedEquals: { }
Matchers.tests.cpp:<line number>:
PASSED:
REQUIRE_THAT( permuted, UnorderedEquals(v) )
with expansion:
{ 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>:
PASSED:
REQUIRE_THAT( permuted, UnorderedEquals(v) )
with expansion:
{ 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Vector matchers that fail Vector matchers that fail
Contains (element) Contains (element)
@ -6541,6 +6572,33 @@ Matchers.tests.cpp:<line number>: FAILED:
with expansion: with expansion:
{ 1, 2, 3 } Equals: { } { 1, 2, 3 } Equals: { }
-------------------------------------------------------------------------------
Vector matchers that fail
UnorderedEquals
-------------------------------------------------------------------------------
Matchers.tests.cpp:<line number>
...............................................................................
Matchers.tests.cpp:<line number>: FAILED:
CHECK_THAT( v, UnorderedEquals(empty) )
with expansion:
{ 1, 2, 3 } UnorderedEquals: { }
Matchers.tests.cpp:<line number>: FAILED:
CHECK_THAT( empty, UnorderedEquals(v) )
with expansion:
{ } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: FAILED:
CHECK_THAT( permuted, UnorderedEquals(v) )
with expansion:
{ 1, 3 } UnorderedEquals: { 1, 2, 3 }
Matchers.tests.cpp:<line number>: FAILED:
CHECK_THAT( permuted, UnorderedEquals(v) )
with expansion:
{ 3, 1 } UnorderedEquals: { 1, 2, 3 }
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
When checked exceptions are thrown they can be expected or unexpected When checked exceptions are thrown they can be expected or unexpected
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -8057,5 +8115,5 @@ PASSED:
=============================================================================== ===============================================================================
test cases: 191 | 137 passed | 50 failed | 4 failed as expected test cases: 191 | 137 passed | 50 failed | 4 failed as expected
assertions: 962 | 835 passed | 106 failed | 21 failed as expected assertions: 970 | 839 passed | 110 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="15" failures="92" tests="963" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <testsuite name="<exe-name>" errors="15" failures="96" tests="971" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/> <testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/>
<testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/> <testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/>
<testcase classname="<exe-name>.global" name="#1027" time="{duration}"/> <testcase classname="<exe-name>.global" name="#1027" time="{duration}"/>
@ -608,6 +608,7 @@ Exception.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Vector matchers/Contains (vector)" time="{duration}"/> <testcase classname="<exe-name>.global" name="Vector matchers/Contains (vector)" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Vector matchers/Contains (element), composed" time="{duration}"/> <testcase classname="<exe-name>.global" name="Vector matchers/Contains (element), composed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Vector matchers/Equals" time="{duration}"/> <testcase classname="<exe-name>.global" name="Vector matchers/Equals" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Vector matchers/UnorderedEquals" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Vector matchers that fail/Contains (element)" time="{duration}"> <testcase classname="<exe-name>.global" name="Vector matchers that fail/Contains (element)" time="{duration}">
<failure message="{ 1, 2, 3 } Contains: -1" type="CHECK_THAT"> <failure message="{ 1, 2, 3 } Contains: -1" type="CHECK_THAT">
Matchers.tests.cpp:<line number> Matchers.tests.cpp:<line number>
@ -635,6 +636,20 @@ Matchers.tests.cpp:<line number>
Matchers.tests.cpp:<line number> Matchers.tests.cpp:<line number>
</failure> </failure>
<failure message="{ 1, 2, 3 } Equals: { }" type="CHECK_THAT"> <failure message="{ 1, 2, 3 } Equals: { }" type="CHECK_THAT">
Matchers.tests.cpp:<line number>
</failure>
</testcase>
<testcase classname="<exe-name>.global" name="Vector matchers that fail/UnorderedEquals" time="{duration}">
<failure message="{ 1, 2, 3 } UnorderedEquals: { }" type="CHECK_THAT">
Matchers.tests.cpp:<line number>
</failure>
<failure message="{ } UnorderedEquals: { 1, 2, 3 }" type="CHECK_THAT">
Matchers.tests.cpp:<line number>
</failure>
<failure message="{ 1, 3 } UnorderedEquals: { 1, 2, 3 }" type="CHECK_THAT">
Matchers.tests.cpp:<line number>
</failure>
<failure message="{ 3, 1 } UnorderedEquals: { 1, 2, 3 }" type="CHECK_THAT">
Matchers.tests.cpp:<line number> Matchers.tests.cpp:<line number>
</failure> </failure>
</testcase> </testcase>

View File

@ -7377,6 +7377,41 @@ Message from section two
</Expression> </Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/> <OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section> </Section>
<Section name="UnorderedEquals" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Expression success="true" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
v, UnorderedEquals(v)
</Original>
<Expanded>
{ 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
</Expanded>
</Expression>
<Expression success="true" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
empty, UnorderedEquals(empty)
</Original>
<Expanded>
{ } UnorderedEquals: { }
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
permuted, UnorderedEquals(v)
</Original>
<Expanded>
{ 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
permuted, UnorderedEquals(v)
</Original>
<Expanded>
{ 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
</Expanded>
</Expression>
<OverallResults successes="4" failures="0" expectedFailures="0"/>
</Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Vector matchers that fail" tags="[.][failing][matchers][vector]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > <TestCase name="Vector matchers that fail" tags="[.][failing][matchers][vector]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
@ -7453,6 +7488,41 @@ Message from section two
</Expression> </Expression>
<OverallResults successes="0" failures="4" expectedFailures="0"/> <OverallResults successes="0" failures="4" expectedFailures="0"/>
</Section> </Section>
<Section name="UnorderedEquals" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
v, UnorderedEquals(empty)
</Original>
<Expanded>
{ 1, 2, 3 } UnorderedEquals: { }
</Expanded>
</Expression>
<Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
empty, UnorderedEquals(v)
</Original>
<Expanded>
{ } UnorderedEquals: { 1, 2, 3 }
</Expanded>
</Expression>
<Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
permuted, UnorderedEquals(v)
</Original>
<Expanded>
{ 1, 3 } UnorderedEquals: { 1, 2, 3 }
</Expanded>
</Expression>
<Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
permuted, UnorderedEquals(v)
</Original>
<Expanded>
{ 3, 1 } UnorderedEquals: { 1, 2, 3 }
</Expanded>
</Expression>
<OverallResults successes="0" failures="4" expectedFailures="0"/>
</Section>
<OverallResult success="false"/> <OverallResult success="false"/>
</TestCase> </TestCase>
<TestCase name="When checked exceptions are thrown they can be expected or unexpected" tags="[!throws]" filename="projects/<exe-name>/UsageTests/Exception.tests.cpp" > <TestCase name="When checked exceptions are thrown they can be expected or unexpected" tags="[!throws]" filename="projects/<exe-name>/UsageTests/Exception.tests.cpp" >
@ -8968,7 +9038,7 @@ loose text artifact
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<OverallResults successes="835" failures="107" expectedFailures="21"/> <OverallResults successes="839" failures="111" expectedFailures="21"/>
</Group> </Group>
<OverallResults successes="835" failures="106" expectedFailures="21"/> <OverallResults successes="839" failures="110" expectedFailures="21"/>
</Catch> </Catch>

View File

@ -9,6 +9,7 @@
#include "catch.hpp" #include "catch.hpp"
#include <sstream> #include <sstream>
#include <algorithm>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma clang diagnostic push
@ -216,6 +217,17 @@ namespace { namespace MatchersTests {
v2.push_back(3); v2.push_back(3);
CHECK_THAT(v, Equals(v2)); CHECK_THAT(v, Equals(v2));
} }
SECTION("UnorderedEquals") {
CHECK_THAT(v, UnorderedEquals(v));
CHECK_THAT(empty, UnorderedEquals(empty));
auto permuted = v;
std::next_permutation(begin(permuted), end(permuted));
REQUIRE_THAT(permuted, UnorderedEquals(v));
std::reverse(begin(permuted), end(permuted));
REQUIRE_THAT(permuted, UnorderedEquals(v));
}
} }
TEST_CASE("Vector matchers that fail", "[matchers][vector][.][failing]") { TEST_CASE("Vector matchers that fail", "[matchers][vector][.][failing]") {
@ -247,6 +259,18 @@ namespace { namespace MatchersTests {
CHECK_THAT(empty, Equals(v)); CHECK_THAT(empty, Equals(v));
CHECK_THAT(v, Equals(empty)); CHECK_THAT(v, Equals(empty));
} }
SECTION("UnorderedEquals") {
CHECK_THAT(v, UnorderedEquals(empty));
CHECK_THAT(empty, UnorderedEquals(v));
auto permuted = v;
std::next_permutation(begin(permuted), end(permuted));
permuted.pop_back();
CHECK_THAT(permuted, UnorderedEquals(v));
std::reverse(begin(permuted), end(permuted));
CHECK_THAT(permuted, UnorderedEquals(v));
}
} }
TEST_CASE("Exception matchers that succeed", "[matchers][exceptions][!throws]") { TEST_CASE("Exception matchers that succeed", "[matchers][exceptions][!throws]") {