diff --git a/include/internal/catch_matchers_vector.h b/include/internal/catch_matchers_vector.h index 347c5d50..2dc8a715 100644 --- a/include/internal/catch_matchers_vector.h +++ b/include/internal/catch_matchers_vector.h @@ -9,6 +9,7 @@ #define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED #include "catch_matchers.h" +#include "catch_approx.h" #include <algorithm> @@ -90,6 +91,42 @@ namespace Matchers { std::vector<T> const& m_comparator; }; + template<typename T> + struct ApproxMatcher : MatcherBase<std::vector<T>> { + + ApproxMatcher(std::vector<T> const& comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T> const &v) const override { + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != approx(v[i])) + return false; + return true; + } + std::string describe() const override { + return "is approx: " + ::Catch::Detail::stringify( m_comparator ); + } + template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& epsilon( T const& newEpsilon ) { + approx.epsilon(newEpsilon); + return *this; + } + template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& margin( T const& newMargin ) { + approx.margin(newMargin); + return *this; + } + template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& scale( T const& newScale ) { + approx.scale(newScale); + return *this; + } + + std::vector<T> const& m_comparator; + mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); + }; + template<typename T> struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> { UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {} @@ -129,6 +166,11 @@ namespace Matchers { return Vector::EqualsMatcher<T>( comparator ); } + template<typename T> + Vector::ApproxMatcher<T> Approx( std::vector<T> const& comparator ) { + return Vector::ApproxMatcher<T>( comparator ); + } + template<typename T> Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) { return Vector::UnorderedEqualsMatcher<T>(target); diff --git a/projects/SelfTest/Baselines/compact.sw.approved.txt b/projects/SelfTest/Baselines/compact.sw.approved.txt index 99eca94d..6fedbb92 100644 --- a/projects/SelfTest/Baselines/compact.sw.approved.txt +++ b/projects/SelfTest/Baselines/compact.sw.approved.txt @@ -1189,6 +1189,15 @@ Approx.tests.cpp:<line number>: passed: approx( d ) == 1.22 for: Approx( 1.23 ) Approx.tests.cpp:<line number>: passed: approx( d ) == 1.24 for: Approx( 1.23 ) == 1.24 Approx.tests.cpp:<line number>: passed: approx( d ) != 1.25 for: Approx( 1.23 ) != 1.25 VariadicMacros.tests.cpp:<line number>: passed: with 1 message: 'no assertions' +Matchers.tests.cpp:<line number>: passed: empty, Approx(empty) for: { } is approx: { } +Matchers.tests.cpp:<line number>: passed: v1, Approx(v1) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 } +Matchers.tests.cpp:<line number>: passed: v1, !Approx(temp) for: { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 } +Matchers.tests.cpp:<line number>: passed: v1, !Approx(v2) for: { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 } +Matchers.tests.cpp:<line number>: passed: v1, Approx(v2).margin(0.5) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } +Matchers.tests.cpp:<line number>: passed: v1, Approx(v2).epsilon(0.5) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } +Matchers.tests.cpp:<line number>: passed: v1, Approx(v2).epsilon(0.1).scale(500) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } +Matchers.tests.cpp:<line number>: failed: empty, Approx(t1) for: { } is approx: { 1.0, 2.0 } +Matchers.tests.cpp:<line number>: failed: v1, Approx(v2) for: { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 } Matchers.tests.cpp:<line number>: passed: v, VectorContains(1) for: { 1, 2, 3 } Contains: 1 Matchers.tests.cpp:<line number>: passed: v, VectorContains(2) for: { 1, 2, 3 } Contains: 2 Matchers.tests.cpp:<line number>: passed: v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2 } @@ -1505,5 +1514,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: Misc.tests.cpp:<line number>: passed: -Failed 78 test cases, failed 139 assertions. +Failed 79 test cases, failed 141 assertions. diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index 3b6f2068..e5a1d5da 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -861,6 +861,30 @@ Exception.tests.cpp:<line number>: FAILED: due to unexpected exception with message: 3.14 +------------------------------------------------------------------------------- +Vector Approx matcher -- failing + Empty and non empty vectors are not approx equal +------------------------------------------------------------------------------- +Matchers.tests.cpp:<line number> +............................................................................... + +Matchers.tests.cpp:<line number>: FAILED: + CHECK_THAT( empty, Approx(t1) ) +with expansion: + { } is approx: { 1.0, 2.0 } + +------------------------------------------------------------------------------- +Vector Approx matcher -- failing + Just different vectors +------------------------------------------------------------------------------- +Matchers.tests.cpp:<line number> +............................................................................... + +Matchers.tests.cpp:<line number>: FAILED: + CHECK_THAT( v1, Approx(v2) ) +with expansion: + { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 } + ------------------------------------------------------------------------------- Vector matchers that fail Contains (element) @@ -1275,6 +1299,6 @@ due to unexpected exception with message: Why would you throw a std::string? =============================================================================== -test cases: 258 | 192 passed | 62 failed | 4 failed as expected -assertions: 1419 | 1276 passed | 122 failed | 21 failed as expected +test cases: 260 | 193 passed | 63 failed | 4 failed as expected +assertions: 1428 | 1283 passed | 124 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index c63de78a..9def37d9 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -8720,6 +8720,96 @@ VariadicMacros.tests.cpp:<line number>: PASSED: with message: no assertions +------------------------------------------------------------------------------- +Vector Approx matcher + Empty vector is roughly equal to an empty vector +------------------------------------------------------------------------------- +Matchers.tests.cpp:<line number> +............................................................................... + +Matchers.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( empty, Approx(empty) ) +with expansion: + { } is approx: { } + +------------------------------------------------------------------------------- +Vector Approx matcher + Vectors with elements + A vector is approx equal to itself +------------------------------------------------------------------------------- +Matchers.tests.cpp:<line number> +............................................................................... + +Matchers.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( v1, Approx(v1) ) +with expansion: + { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 } + +------------------------------------------------------------------------------- +Vector Approx matcher + Vectors with elements + Different length +------------------------------------------------------------------------------- +Matchers.tests.cpp:<line number> +............................................................................... + +Matchers.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( v1, !Approx(temp) ) +with expansion: + { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 } + +------------------------------------------------------------------------------- +Vector Approx matcher + Vectors with elements + Same length, different elements +------------------------------------------------------------------------------- +Matchers.tests.cpp:<line number> +............................................................................... + +Matchers.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( v1, !Approx(v2) ) +with expansion: + { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 } + +Matchers.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( v1, Approx(v2).margin(0.5) ) +with expansion: + { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } + +Matchers.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( v1, Approx(v2).epsilon(0.5) ) +with expansion: + { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } + +Matchers.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( v1, Approx(v2).epsilon(0.1).scale(500) ) +with expansion: + { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } + +------------------------------------------------------------------------------- +Vector Approx matcher -- failing + Empty and non empty vectors are not approx equal +------------------------------------------------------------------------------- +Matchers.tests.cpp:<line number> +............................................................................... + +Matchers.tests.cpp:<line number>: FAILED: + CHECK_THAT( empty, Approx(t1) ) +with expansion: + { } is approx: { 1.0, 2.0 } + +------------------------------------------------------------------------------- +Vector Approx matcher -- failing + Just different vectors +------------------------------------------------------------------------------- +Matchers.tests.cpp:<line number> +............................................................................... + +Matchers.tests.cpp:<line number>: FAILED: + CHECK_THAT( v1, Approx(v2) ) +with expansion: + { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 } + ------------------------------------------------------------------------------- Vector matchers Contains (element) @@ -11128,6 +11218,6 @@ Misc.tests.cpp:<line number> Misc.tests.cpp:<line number>: PASSED: =============================================================================== -test cases: 258 | 176 passed | 78 failed | 4 failed as expected -assertions: 1436 | 1276 passed | 139 failed | 21 failed as expected +test cases: 260 | 177 passed | 79 failed | 4 failed as expected +assertions: 1445 | 1283 passed | 141 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index 43c78d7e..af4ebe07 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <testsuitesloose text artifact > - <testsuite name="<exe-name>" errors="17" failures="123" tests="1437" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> + <testsuite name="<exe-name>" errors="17" failures="125" tests="1446" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <properties> <property name="filters" value="~[!nonportable]~[!benchmark]~[approvals]"/> <property name="random-seed" value="1"/> @@ -741,6 +741,20 @@ Exception.tests.cpp:<line number> </testcase> <testcase classname="<exe-name>.global" name="Use a custom approx" time="{duration}"/> <testcase classname="<exe-name>.global" name="Variadic macros/Section with one argument" time="{duration}"/> + <testcase classname="<exe-name>.global" name="Vector Approx matcher/Empty vector is roughly equal to an empty vector" time="{duration}"/> + <testcase classname="<exe-name>.global" name="Vector Approx matcher/Vectors with elements/A vector is approx equal to itself" time="{duration}"/> + <testcase classname="<exe-name>.global" name="Vector Approx matcher/Vectors with elements/Different length" time="{duration}"/> + <testcase classname="<exe-name>.global" name="Vector Approx matcher/Vectors with elements/Same length, different elements" time="{duration}"/> + <testcase classname="<exe-name>.global" name="Vector Approx matcher -- failing/Empty and non empty vectors are not approx equal" time="{duration}"> + <failure message="{ } is approx: { 1.0, 2.0 }" type="CHECK_THAT"> +Matchers.tests.cpp:<line number> + </failure> + </testcase> + <testcase classname="<exe-name>.global" name="Vector Approx matcher -- failing/Just different vectors" time="{duration}"> + <failure message="{ 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }" type="CHECK_THAT"> +Matchers.tests.cpp:<line number> + </failure> + </testcase> <testcase classname="<exe-name>.global" name="Vector matchers/Contains (element)" 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}"/> diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index b526a5f7..d053b408 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -10713,6 +10713,111 @@ Message from section two </Section> <OverallResult success="true"/> </TestCase> + <TestCase name="Vector Approx matcher" tags="[approx][matchers][vector]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Section name="Empty vector is roughly equal to an empty vector" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + empty, Approx(empty) + </Original> + <Expanded> + { } is approx: { } + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0"/> + </Section> + <Section name="Vectors with elements" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Section name="A vector is approx equal to itself" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + v1, Approx(v1) + </Original> + <Expanded> + { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 } + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0"/> + </Section> + <OverallResults successes="1" failures="0" expectedFailures="0"/> + </Section> + <Section name="Vectors with elements" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Section name="Different length" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + v1, !Approx(temp) + </Original> + <Expanded> + { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 } + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0"/> + </Section> + <OverallResults successes="1" failures="0" expectedFailures="0"/> + </Section> + <Section name="Vectors with elements" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Section name="Same length, different elements" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + v1, !Approx(v2) + </Original> + <Expanded> + { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 } + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + v1, Approx(v2).margin(0.5) + </Original> + <Expanded> + { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + v1, Approx(v2).epsilon(0.5) + </Original> + <Expanded> + { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + v1, Approx(v2).epsilon(0.1).scale(500) + </Original> + <Expanded> + { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 } + </Expanded> + </Expression> + <OverallResults successes="4" failures="0" expectedFailures="0"/> + </Section> + <OverallResults successes="4" failures="0" expectedFailures="0"/> + </Section> + <OverallResult success="true"/> + </TestCase> + <TestCase name="Vector Approx matcher -- failing" tags="[.][approx][failing][matchers][vector]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Section name="Empty and non empty vectors are not approx equal" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + empty, Approx(t1) + </Original> + <Expanded> + { } is approx: { 1.0, 2.0 } + </Expanded> + </Expression> + <OverallResults successes="0" failures="1" expectedFailures="0"/> + </Section> + <Section name="Just different vectors" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Expression success="false" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > + <Original> + v1, Approx(v2) + </Original> + <Expanded> + { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 } + </Expanded> + </Expression> + <OverallResults successes="0" failures="1" expectedFailures="0"/> + </Section> + <OverallResult success="false"/> + </TestCase> <TestCase name="Vector matchers" tags="[matchers][vector]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > <Section name="Contains (element)" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > <Expression success="true" type="CHECK_THAT" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" > @@ -13399,7 +13504,7 @@ loose text artifact </Section> <OverallResult success="true"/> </TestCase> - <OverallResults successes="1276" failures="140" expectedFailures="21"/> + <OverallResults successes="1283" failures="142" expectedFailures="21"/> </Group> - <OverallResults successes="1276" failures="139" expectedFailures="21"/> + <OverallResults successes="1283" failures="141" expectedFailures="21"/> </Catch> diff --git a/projects/SelfTest/UsageTests/Matchers.tests.cpp b/projects/SelfTest/UsageTests/Matchers.tests.cpp index f07481d4..d741e57c 100644 --- a/projects/SelfTest/UsageTests/Matchers.tests.cpp +++ b/projects/SelfTest/UsageTests/Matchers.tests.cpp @@ -213,6 +213,16 @@ namespace { namespace MatchersTests { v2.push_back(1); v2.push_back(2); + std::vector<double> v3; + v3.push_back(1); + v3.push_back(2); + v3.push_back(3); + + std::vector<double> v4; + v4.push_back(1 + 1e-8); + v4.push_back(2 + 1e-8); + v4.push_back(3 + 1e-8); + std::vector<int> empty; SECTION("Contains (element)") { @@ -265,6 +275,16 @@ namespace { namespace MatchersTests { v2.push_back(1); v2.push_back(2); + std::vector<double> v3; + v3.push_back(1); + v3.push_back(2); + v3.push_back(3); + + std::vector<double> v4; + v4.push_back(1.1); + v4.push_back(2.1); + v4.push_back(3.1); + std::vector<int> empty; SECTION("Contains (element)") { @@ -436,6 +456,44 @@ namespace { namespace MatchersTests { REQUIRE_THAT("foo", Predicate<const char*>([] (const char* const&) { return true; })); } + TEST_CASE("Vector Approx matcher", "[matchers][approx][vector]") { + using Catch::Matchers::Approx; + SECTION("Empty vector is roughly equal to an empty vector") { + std::vector<double> empty; + REQUIRE_THAT(empty, Approx(empty)); + } + SECTION("Vectors with elements") { + std::vector<double> v1({1., 2., 3.}); + SECTION("A vector is approx equal to itself") { + REQUIRE_THAT(v1, Approx(v1)); + } + std::vector<double> v2({1.5, 2.5, 3.5}); + SECTION("Different length") { + auto temp(v1); + temp.push_back(4); + REQUIRE_THAT(v1, !Approx(temp)); + } + SECTION("Same length, different elements") { + REQUIRE_THAT(v1, !Approx(v2)); + REQUIRE_THAT(v1, Approx(v2).margin(0.5)); + REQUIRE_THAT(v1, Approx(v2).epsilon(0.5)); + REQUIRE_THAT(v1, Approx(v2).epsilon(0.1).scale(500)); + } + } + } + + TEST_CASE("Vector Approx matcher -- failing", "[matchers][approx][vector][.failing]") { + using Catch::Matchers::Approx; + SECTION("Empty and non empty vectors are not approx equal") { + std::vector<double> empty, t1({1, 2}); + CHECK_THAT(empty, Approx(t1)); + } + SECTION("Just different vectors") { + std::vector<double> v1({2., 4., 6.}), v2({1., 3., 5.}); + CHECK_THAT(v1, Approx(v2)); + } + } + } } // namespace MatchersTests #endif // CATCH_CONFIG_DISABLE_MATCHERS