aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp')
-rw-r--r--contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp936
1 files changed, 936 insertions, 0 deletions
diff --git a/contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp
new file mode 100644
index 0000000..4f906b9
--- /dev/null
+++ b/contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp
@@ -0,0 +1,936 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_container_properties.hpp>
+#include <catch2/matchers/catch_matchers_contains.hpp>
+#include <catch2/matchers/catch_matchers_range_equals.hpp>
+#include <catch2/matchers/catch_matchers_floating_point.hpp>
+#include <catch2/matchers/catch_matchers_quantifiers.hpp>
+#include <catch2/matchers/catch_matchers_predicate.hpp>
+#include <catch2/matchers/catch_matchers_string.hpp>
+
+#include <helpers/range_test_helpers.hpp>
+
+#include <cmath>
+#include <list>
+#include <map>
+#include <type_traits>
+#include <vector>
+#include <memory>
+
+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<int> in{1, 2, 3, 4, 5};
+
+ 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)));
+ }
+}
+
+namespace {
+
+ struct has_empty {
+ bool empty() const { return false; }
+ };
+
+} // end unnamed namespace
+
+TEST_CASE("Basic use of the Empty range matcher", "[matchers][templated][empty]") {
+ using Catch::Matchers::IsEmpty;
+ SECTION("Simple, std-provided containers") {
+ std::array<int, 0> empty_array{};
+ std::array<double, 1> non_empty_array{};
+ REQUIRE_THAT(empty_array, IsEmpty());
+ REQUIRE_THAT(non_empty_array, !IsEmpty());
+
+ std::vector<std::string> empty_vec;
+ std::vector<char> non_empty_vec{ 'a', 'b', 'c' };
+ REQUIRE_THAT(empty_vec, IsEmpty());
+ REQUIRE_THAT(non_empty_vec, !IsEmpty());
+
+ std::list<std::list<std::list<int>>> inner_lists_are_empty;
+ inner_lists_are_empty.push_back({});
+ REQUIRE_THAT(inner_lists_are_empty, !IsEmpty());
+ REQUIRE_THAT(inner_lists_are_empty.front(), IsEmpty());
+ }
+ SECTION("Type with empty") {
+ REQUIRE_THAT(has_empty{}, !IsEmpty());
+ }
+ SECTION("Type requires ADL found empty free function") {
+ REQUIRE_THAT(unrelated::ADL_empty{}, IsEmpty());
+ }
+}
+
+namespace {
+ class LessThanMatcher final : public Catch::Matchers::MatcherBase<size_t> {
+ size_t m_target;
+ public:
+ explicit LessThanMatcher(size_t target):
+ m_target(target)
+ {}
+
+ bool match(size_t const& size) const override {
+ return size < m_target;
+ }
+
+ std::string describe() const override {
+ return "is less than " + std::to_string(m_target);
+ }
+ };
+
+ LessThanMatcher Lt(size_t sz) {
+ return LessThanMatcher{ sz };
+ }
+
+ struct has_size {
+ size_t size() const {
+ return 13;
+ }
+ };
+
+} // end unnamed namespace
+
+TEST_CASE("Usage of the SizeIs range matcher", "[matchers][templated][size]") {
+ using Catch::Matchers::SizeIs;
+ SECTION("Some with stdlib containers") {
+ std::vector<int> empty_vec;
+ REQUIRE_THAT(empty_vec, SizeIs(0));
+ REQUIRE_THAT(empty_vec, !SizeIs(2));
+ REQUIRE_THAT(empty_vec, SizeIs(Lt(2)));
+
+ std::array<int, 2> arr{};
+ REQUIRE_THAT(arr, SizeIs(2));
+ REQUIRE_THAT(arr, SizeIs( Lt(3)));
+ REQUIRE_THAT(arr, !SizeIs(!Lt(3)));
+
+ std::map<int, int> map{ {1, 1}, {2, 2}, {3, 3} };
+ REQUIRE_THAT(map, SizeIs(3));
+ }
+ SECTION("Type requires ADL found size free function") {
+ REQUIRE_THAT(unrelated::ADL_size{}, SizeIs(12));
+ }
+ SECTION("Type has size member") {
+ REQUIRE_THAT(has_size{}, SizeIs(13));
+ }
+}
+
+
+TEST_CASE("Usage of AllMatch range matcher", "[matchers][templated][quantifiers]") {
+ using Catch::Matchers::AllMatch;
+ using Catch::Matchers::Predicate;
+
+ SECTION("Basic usage") {
+ using Catch::Matchers::Contains;
+ using Catch::Matchers::SizeIs;
+
+ std::array<std::array<int, 5>, 5> data{{
+ {{ 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 }}
+ }};
+
+ REQUIRE_THAT(data, AllMatch(SizeIs(5)));
+ REQUIRE_THAT(data, !AllMatch(Contains(0) && Contains(1)));
+ }
+
+ SECTION("Type requires ADL found begin and end") {
+ unrelated::needs_ADL_begin<int> needs_adl{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( needs_adl, AllMatch( Predicate<int>( []( int elem ) {
+ return elem < 6;
+ } ) ) );
+ }
+
+ SECTION("Shortcircuiting") {
+ with_mocked_iterator_access<int> mocked{ 1, 2, 3, 4, 5 };
+ SECTION("All are read") {
+ auto allMatch = AllMatch(Predicate<int>([](int elem) {
+ return elem < 10;
+ }));
+ REQUIRE_THAT(mocked, allMatch);
+ REQUIRE(mocked.m_derefed[0]);
+ REQUIRE(mocked.m_derefed[1]);
+ REQUIRE(mocked.m_derefed[2]);
+ REQUIRE(mocked.m_derefed[3]);
+ REQUIRE(mocked.m_derefed[4]);
+ }
+ SECTION("Short-circuited") {
+ auto allMatch = AllMatch(Predicate<int>([](int elem) {
+ return elem < 3;
+ }));
+ REQUIRE_THAT(mocked, !allMatch);
+ REQUIRE(mocked.m_derefed[0]);
+ REQUIRE(mocked.m_derefed[1]);
+ REQUIRE(mocked.m_derefed[2]);
+ REQUIRE_FALSE(mocked.m_derefed[3]);
+ REQUIRE_FALSE(mocked.m_derefed[4]);
+ }
+ }
+}
+
+TEST_CASE("Usage of AnyMatch range matcher", "[matchers][templated][quantifiers]") {
+ using Catch::Matchers::AnyMatch;
+ using Catch::Matchers::Predicate;
+
+ SECTION("Basic usage") {
+ using Catch::Matchers::Contains;
+ using Catch::Matchers::SizeIs;
+
+ std::array<std::array<int, 5>, 5> data{ {
+ {{ 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 }}
+ } };
+
+ REQUIRE_THAT(data, AnyMatch(SizeIs(5)));
+ REQUIRE_THAT(data, !AnyMatch(Contains(0) && Contains(10)));
+ }
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<int> needs_adl{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( needs_adl, AnyMatch( Predicate<int>( []( int elem ) {
+ return elem < 3;
+ } ) ) );
+ }
+
+ SECTION("Shortcircuiting") {
+ with_mocked_iterator_access<int> mocked{ 1, 2, 3, 4, 5 };
+ SECTION("All are read") {
+ auto anyMatch = AnyMatch(
+ Predicate<int>( []( int elem ) { return elem > 10; } ) );
+ REQUIRE_THAT( mocked, !anyMatch );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE( mocked.m_derefed[3] );
+ REQUIRE( mocked.m_derefed[4] );
+ }
+ SECTION("Short-circuited") {
+ auto anyMatch = AnyMatch(
+ Predicate<int>( []( int elem ) { return elem < 3; } ) );
+ REQUIRE_THAT( mocked, anyMatch );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE_FALSE( mocked.m_derefed[1] );
+ REQUIRE_FALSE( mocked.m_derefed[2] );
+ REQUIRE_FALSE( mocked.m_derefed[3] );
+ REQUIRE_FALSE( mocked.m_derefed[4] );
+ }
+ }
+}
+
+TEST_CASE("Usage of NoneMatch range matcher", "[matchers][templated][quantifiers]") {
+ using Catch::Matchers::NoneMatch;
+ using Catch::Matchers::Predicate;
+
+ SECTION("Basic usage") {
+ using Catch::Matchers::Contains;
+ using Catch::Matchers::SizeIs;
+
+ std::array<std::array<int, 5>, 5> data{ {
+ {{ 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 }}
+ } };
+
+ REQUIRE_THAT(data, NoneMatch(SizeIs(6)));
+ REQUIRE_THAT(data, !NoneMatch(Contains(0) && Contains(1)));
+ }
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<int> needs_adl{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( needs_adl, NoneMatch( Predicate<int>( []( int elem ) {
+ return elem > 6;
+ } ) ) );
+ }
+
+ SECTION("Shortcircuiting") {
+ with_mocked_iterator_access<int> mocked{ 1, 2, 3, 4, 5 };
+ SECTION("All are read") {
+ auto noneMatch = NoneMatch(
+ Predicate<int>([](int elem) { return elem > 10; }));
+ REQUIRE_THAT(mocked, noneMatch);
+ REQUIRE(mocked.m_derefed[0]);
+ REQUIRE(mocked.m_derefed[1]);
+ REQUIRE(mocked.m_derefed[2]);
+ REQUIRE(mocked.m_derefed[3]);
+ REQUIRE(mocked.m_derefed[4]);
+ }
+ SECTION("Short-circuited") {
+ auto noneMatch = NoneMatch(
+ Predicate<int>([](int elem) { return elem < 3; }));
+ REQUIRE_THAT(mocked, !noneMatch);
+ REQUIRE(mocked.m_derefed[0]);
+ REQUIRE_FALSE(mocked.m_derefed[1]);
+ REQUIRE_FALSE(mocked.m_derefed[2]);
+ REQUIRE_FALSE(mocked.m_derefed[3]);
+ REQUIRE_FALSE(mocked.m_derefed[4]);
+ }
+ }
+}
+
+namespace {
+ struct ConvertibleToBool
+ {
+ bool v;
+
+ explicit operator bool() const
+ {
+ return v;
+ }
+ };
+}
+
+namespace Catch {
+ template <>
+ struct StringMaker<ConvertibleToBool> {
+ static std::string
+ convert( ConvertibleToBool const& convertible_to_bool ) {
+ return ::Catch::Detail::stringify( convertible_to_bool.v );
+ }
+ };
+} // namespace Catch
+
+TEST_CASE("Usage of AllTrue range matcher", "[matchers][templated][quantifiers]") {
+ using Catch::Matchers::AllTrue;
+
+ SECTION( "Basic usage" ) {
+ SECTION( "All true evaluates to true" ) {
+ std::array<bool, 5> const data{ { true, true, true, true, true } };
+ REQUIRE_THAT( data, AllTrue() );
+ }
+ SECTION( "Empty evaluates to true" ) {
+ std::array<bool, 0> const data{};
+ REQUIRE_THAT( data, AllTrue() );
+ }
+ SECTION( "One false evaluates to false" ) {
+ std::array<bool, 5> const data{ { true, true, false, true, true } };
+ REQUIRE_THAT( data, !AllTrue() );
+ }
+ SECTION( "All false evaluates to false" ) {
+ std::array<bool, 5> const data{
+ { false, false, false, false, false } };
+ REQUIRE_THAT( data, !AllTrue() );
+ }
+ }
+
+ SECTION( "Contained type is convertible to bool" ) {
+ SECTION( "All true evaluates to true" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { true }, { true }, { true }, { true }, { true } } };
+ REQUIRE_THAT( data, AllTrue() );
+ }
+ SECTION( "One false evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { true }, { true }, { false }, { true }, { true } } };
+ REQUIRE_THAT( data, !AllTrue() );
+ }
+ SECTION( "All false evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { false }, { false }, { false } } };
+ REQUIRE_THAT( data, !AllTrue() );
+ }
+ }
+
+ SECTION( "Shortcircuiting" ) {
+ SECTION( "All are read" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ true, true, true, true, true };
+ REQUIRE_THAT( mocked, AllTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE( mocked.m_derefed[3] );
+ REQUIRE( mocked.m_derefed[4] );
+ }
+ SECTION( "Short-circuited" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ true, true, false, true, true };
+ REQUIRE_THAT( mocked, !AllTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE_FALSE( mocked.m_derefed[3] );
+ REQUIRE_FALSE( mocked.m_derefed[4] );
+ }
+ }
+}
+
+TEST_CASE( "Usage of NoneTrue range matcher", "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::NoneTrue;
+
+ SECTION( "Basic usage" ) {
+ SECTION( "All true evaluates to false" ) {
+ std::array<bool, 5> const data{ { true, true, true, true, true } };
+ REQUIRE_THAT( data, !NoneTrue() );
+ }
+ SECTION( "Empty evaluates to true" ) {
+ std::array<bool, 0> const data{};
+ REQUIRE_THAT( data, NoneTrue() );
+ }
+ SECTION( "One true evaluates to false" ) {
+ std::array<bool, 5> const data{
+ { false, false, true, false, false } };
+ REQUIRE_THAT( data, !NoneTrue() );
+ }
+ SECTION( "All false evaluates to true" ) {
+ std::array<bool, 5> const data{
+ { false, false, false, false, false } };
+ REQUIRE_THAT( data, NoneTrue() );
+ }
+ }
+
+ SECTION( "Contained type is convertible to bool" ) {
+ SECTION( "All true evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { true }, { true }, { true }, { true }, { true } } };
+ REQUIRE_THAT( data, !NoneTrue() );
+ }
+ SECTION( "One true evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { true }, { false }, { false } } };
+ REQUIRE_THAT( data, !NoneTrue() );
+ }
+ SECTION( "All false evaluates to true" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { false }, { false }, { false } } };
+ REQUIRE_THAT( data, NoneTrue() );
+ }
+ }
+
+ SECTION( "Shortcircuiting" ) {
+ SECTION( "All are read" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ false, false, false, false, false };
+ REQUIRE_THAT( mocked, NoneTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE( mocked.m_derefed[3] );
+ REQUIRE( mocked.m_derefed[4] );
+ }
+ SECTION( "Short-circuited" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ false, false, true, true, true };
+ REQUIRE_THAT( mocked, !NoneTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE_FALSE( mocked.m_derefed[3] );
+ REQUIRE_FALSE( mocked.m_derefed[4] );
+ }
+ }
+}
+
+TEST_CASE( "Usage of AnyTrue range matcher", "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::AnyTrue;
+
+ SECTION( "Basic usage" ) {
+ SECTION( "All true evaluates to true" ) {
+ std::array<bool, 5> const data{ { true, true, true, true, true } };
+ REQUIRE_THAT( data, AnyTrue() );
+ }
+ SECTION( "Empty evaluates to false" ) {
+ std::array<bool, 0> const data{};
+ REQUIRE_THAT( data, !AnyTrue() );
+ }
+ SECTION( "One true evaluates to true" ) {
+ std::array<bool, 5> const data{
+ { false, false, true, false, false } };
+ REQUIRE_THAT( data, AnyTrue() );
+ }
+ SECTION( "All false evaluates to false" ) {
+ std::array<bool, 5> const data{
+ { false, false, false, false, false } };
+ REQUIRE_THAT( data, !AnyTrue() );
+ }
+ }
+
+ SECTION( "Contained type is convertible to bool" ) {
+ SECTION( "All true evaluates to true" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { true }, { true }, { true }, { true }, { true } } };
+ REQUIRE_THAT( data, AnyTrue() );
+ }
+ SECTION( "One true evaluates to true" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { true }, { false }, { false } } };
+ REQUIRE_THAT( data, AnyTrue() );
+ }
+ SECTION( "All false evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { false }, { false }, { false } } };
+ REQUIRE_THAT( data, !AnyTrue() );
+ }
+ }
+
+ SECTION( "Shortcircuiting" ) {
+ SECTION( "All are read" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ false, false, false, false, true };
+ REQUIRE_THAT( mocked, AnyTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE( mocked.m_derefed[3] );
+ REQUIRE( mocked.m_derefed[4] );
+ }
+ SECTION( "Short-circuited" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ false, false, true, true, true };
+ REQUIRE_THAT( mocked, AnyTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE_FALSE( mocked.m_derefed[3] );
+ REQUIRE_FALSE( mocked.m_derefed[4] );
+ }
+ }
+}
+
+TEST_CASE("All/Any/None True matchers support types with ADL begin",
+ "[approvals][matchers][quantifiers][templated]") {
+ using Catch::Matchers::AllTrue;
+ using Catch::Matchers::NoneTrue;
+ using Catch::Matchers::AnyTrue;
+
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<bool> const needs_adl{
+ true, true, true, true, true };
+ REQUIRE_THAT( needs_adl, AllTrue() );
+ }
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<bool> const needs_adl{
+ false, false, false, false, false };
+ REQUIRE_THAT( needs_adl, NoneTrue() );
+ }
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<bool> const needs_adl{
+ false, false, true, false, false };
+ REQUIRE_THAT( needs_adl, AnyTrue() );
+ }
+}
+
+// Range loop iterating over range with different types for begin and end is a
+// C++17 feature, and GCC refuses to compile such code unless the lang mode is
+// set to C++17 or later.
+#if defined(CATCH_CPP17_OR_GREATER)
+
+TEST_CASE( "The quantifier range matchers support types with different types returned from begin and end",
+ "[matchers][templated][quantifiers][approvals]" ) {
+ using Catch::Matchers::AllMatch;
+ using Catch::Matchers::AllTrue;
+ using Catch::Matchers::AnyMatch;
+ using Catch::Matchers::AnyTrue;
+ using Catch::Matchers::NoneMatch;
+ using Catch::Matchers::NoneTrue;
+
+ using Catch::Matchers::Predicate;
+
+ SECTION( "AllAnyNoneMatch" ) {
+ has_different_begin_end_types<int> diff_types{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( diff_types, !AllMatch( Predicate<int>( []( int elem ) {
+ return elem < 3;
+ } ) ) );
+
+ REQUIRE_THAT( diff_types, AnyMatch( Predicate<int>( []( int elem ) {
+ return elem < 2;
+ } ) ) );
+
+ REQUIRE_THAT( diff_types, !NoneMatch( Predicate<int>( []( int elem ) {
+ return elem < 3;
+ } ) ) );
+ }
+ SECTION( "AllAnyNoneTrue" ) {
+ has_different_begin_end_types<bool> diff_types{ false, false, true, false, false };
+
+ REQUIRE_THAT( diff_types, !AllTrue() );
+ REQUIRE_THAT( diff_types, AnyTrue() );
+ REQUIRE_THAT( diff_types, !NoneTrue() );
+ }
+}
+
+TEST_CASE( "RangeEquals supports ranges with different types returned from begin and end",
+ "[matchers][templated][range][approvals] ") {
+ using Catch::Matchers::RangeEquals;
+ using Catch::Matchers::UnorderedRangeEquals;
+
+ has_different_begin_end_types<int> diff_types{ 1, 2, 3, 4, 5 };
+ std::array<int, 5> arr1{ { 1, 2, 3, 4, 5 } }, arr2{ { 2, 3, 4, 5, 6 } };
+
+ REQUIRE_THAT( diff_types, RangeEquals( arr1 ) );
+ REQUIRE_THAT( diff_types, RangeEquals( arr2, []( int l, int r ) {
+ return l + 1 == r;
+ } ) );
+ REQUIRE_THAT( diff_types, UnorderedRangeEquals( diff_types ) );
+}
+
+TEST_CASE( "RangeContains supports ranges with different types returned from "
+ "begin and end",
+ "[matchers][templated][range][approvals]" ) {
+ using Catch::Matchers::Contains;
+
+ has_different_begin_end_types<size_t> diff_types{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( diff_types, Contains( size_t( 3 ) ) );
+ REQUIRE_THAT( diff_types, Contains( LessThanMatcher( size_t( 4 ) ) ) );
+}
+
+#endif
+
+TEST_CASE( "Usage of RangeEquals range matcher", "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::RangeEquals;
+
+ // In these tests, the types are always the same - type conversion is in the next section
+ SECTION( "Basic usage" ) {
+ SECTION( "Empty container matches empty container" ) {
+ const std::vector<int> empty_vector;
+ CHECK_THAT( empty_vector, RangeEquals( empty_vector ) );
+ }
+ SECTION( "Empty container does not match non-empty container" ) {
+ const std::vector<int> empty_vector;
+ const std::vector<int> non_empty_vector{ 1 };
+ CHECK_THAT( empty_vector, !RangeEquals( non_empty_vector ) );
+ // ...and in reverse
+ CHECK_THAT( non_empty_vector, !RangeEquals( empty_vector ) );
+ }
+ SECTION( "Two equal 1-length non-empty containers" ) {
+ const std::array<int, 1> non_empty_array{ { 1 } };
+ CHECK_THAT( non_empty_array, RangeEquals( non_empty_array ) );
+ }
+ SECTION( "Two equal-sized, equal, non-empty containers" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+ CHECK_THAT( array_a, RangeEquals( array_a ) );
+ }
+ SECTION( "Two equal-sized, non-equal, non-empty containers" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+ const std::array<int, 3> array_b{ { 2, 2, 3 } };
+ const std::array<int, 3> array_c{ { 1, 2, 2 } };
+ CHECK_THAT( array_a, !RangeEquals( array_b ) );
+ CHECK_THAT( array_a, !RangeEquals( array_c ) );
+ }
+ SECTION( "Two non-equal-sized, non-empty containers (with same first "
+ "elements)" ) {
+ const std::vector<int> vector_a{ 1, 2, 3 };
+ const std::vector<int> vector_b{ 1, 2, 3, 4 };
+ CHECK_THAT( vector_a, !RangeEquals( vector_b ) );
+ }
+ }
+
+ SECTION( "Custom predicate" ) {
+
+ auto close_enough = []( int lhs, int rhs ) {
+ return std::abs( lhs - rhs ) <= 1;
+ };
+
+ SECTION( "Two equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 2, 3 } };
+ const std::vector<int> vector_a_plus_1{ { 2, 3, 4 } };
+ CHECK_THAT( vector_a, RangeEquals( vector_a_plus_1, close_enough ) );
+ }
+ SECTION( "Two non-equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 2, 3 } };
+ const std::vector<int> vector_b{ { 3, 3, 4 } };
+ CHECK_THAT( vector_a, !RangeEquals( vector_b, close_enough ) );
+ }
+ }
+
+ SECTION( "Ranges that need ADL begin/end" ) {
+ unrelated::needs_ADL_begin<int> const
+ needs_adl1{ 1, 2, 3, 4, 5 },
+ needs_adl2{ 1, 2, 3, 4, 5 },
+ needs_adl3{ 2, 3, 4, 5, 6 };
+
+ REQUIRE_THAT( needs_adl1, RangeEquals( needs_adl2 ) );
+ REQUIRE_THAT( needs_adl1, RangeEquals( needs_adl3, []( int l, int r ) {
+ return l + 1 == r;
+ } ) );
+ }
+
+ SECTION( "Compare against std::initializer_list" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+
+ REQUIRE_THAT( array_a, RangeEquals( { 1, 2, 3 } ) );
+ REQUIRE_THAT( array_a, RangeEquals( { 2, 4, 6 }, []( int l, int r ) {
+ return l * 2 == r;
+ } ) );
+ }
+
+ SECTION("Check short-circuiting behaviour") {
+ with_mocked_iterator_access<int> const mocked1{ 1, 2, 3, 4 };
+
+ SECTION( "Check short-circuits on failure" ) {
+ std::array<int, 4> arr{ { 1, 2, 4, 4 } };
+
+ REQUIRE_THAT( mocked1, !RangeEquals( arr ) );
+ REQUIRE( mocked1.m_derefed[0] );
+ REQUIRE( mocked1.m_derefed[1] );
+ REQUIRE( mocked1.m_derefed[2] );
+ REQUIRE_FALSE( mocked1.m_derefed[3] );
+ }
+ SECTION("All elements are checked on success") {
+ std::array<int, 4> arr{ { 1, 2, 3, 4 } };
+
+ REQUIRE_THAT( mocked1, RangeEquals( arr ) );
+ REQUIRE( mocked1.m_derefed[0] );
+ REQUIRE( mocked1.m_derefed[1] );
+ REQUIRE( mocked1.m_derefed[2] );
+ REQUIRE( mocked1.m_derefed[3] );
+ }
+ }
+}
+
+TEST_CASE( "Usage of UnorderedRangeEquals range matcher",
+ "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::UnorderedRangeEquals;
+
+ // In these tests, the types are always the same - type conversion is in the
+ // next section
+ SECTION( "Basic usage" ) {
+ SECTION( "Empty container matches empty container" ) {
+ const std::vector<int> empty_vector;
+ CHECK_THAT( empty_vector, UnorderedRangeEquals( empty_vector ) );
+ }
+ SECTION( "Empty container does not match non-empty container" ) {
+ const std::vector<int> empty_vector;
+ const std::vector<int> non_empty_vector{ 1 };
+ CHECK_THAT( empty_vector,
+ !UnorderedRangeEquals( non_empty_vector ) );
+ // ...and in reverse
+ CHECK_THAT( non_empty_vector,
+ !UnorderedRangeEquals( empty_vector ) );
+ }
+ SECTION( "Two equal 1-length non-empty containers" ) {
+ const std::array<int, 1> non_empty_array{ { 1 } };
+ CHECK_THAT( non_empty_array,
+ UnorderedRangeEquals( non_empty_array ) );
+ }
+ SECTION( "Two equal-sized, equal, non-empty containers" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+ CHECK_THAT( array_a, UnorderedRangeEquals( array_a ) );
+ }
+ SECTION( "Two equal-sized, non-equal, non-empty containers" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+ const std::array<int, 3> array_b{ { 2, 2, 3 } };
+ CHECK_THAT( array_a, !UnorderedRangeEquals( array_b ) );
+ }
+ SECTION( "Two non-equal-sized, non-empty containers" ) {
+ const std::vector<int> vector_a{ 1, 2, 3 };
+ const std::vector<int> vector_b{ 1, 2, 3, 4 };
+ CHECK_THAT( vector_a, !UnorderedRangeEquals( vector_b ) );
+ }
+ }
+
+ SECTION( "Custom predicate" ) {
+
+ auto close_enough = []( int lhs, int rhs ) {
+ return std::abs( lhs - rhs ) <= 1;
+ };
+
+ SECTION( "Two equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 10, 20 } };
+ const std::vector<int> vector_a_plus_1{ { 11, 21, 2 } };
+ CHECK_THAT( vector_a,
+ UnorderedRangeEquals( vector_a_plus_1, close_enough ) );
+ }
+ SECTION( "Two non-equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 10, 21 } };
+ const std::vector<int> vector_b{ { 11, 21, 3 } };
+ CHECK_THAT( vector_a,
+ !UnorderedRangeEquals( vector_b, close_enough ) );
+ }
+ }
+
+
+ SECTION( "Ranges that need ADL begin/end" ) {
+ unrelated::needs_ADL_begin<int> const
+ needs_adl1{ 1, 2, 3, 4, 5 },
+ needs_adl2{ 1, 2, 3, 4, 5 };
+
+ REQUIRE_THAT( needs_adl1, UnorderedRangeEquals( needs_adl2 ) );
+ }
+
+ SECTION( "Compare against std::initializer_list" ) {
+ const std::array<int, 3> array_a{ { 1, 10, 20 } };
+
+ REQUIRE_THAT( array_a, UnorderedRangeEquals( { 10, 20, 1 } ) );
+ REQUIRE_THAT( array_a,
+ UnorderedRangeEquals( { 11, 21, 2 }, []( int l, int r ) {
+ return std::abs( l - r ) <= 1;
+ } ) );
+ }
+}
+
+/**
+ * Return true if the type given has a random access iterator type.
+ */
+template <typename Container>
+static constexpr bool ContainerIsRandomAccess( const Container& ) {
+ using array_iter_category = typename std::iterator_traits<
+ typename Container::iterator>::iterator_category;
+
+ return std::is_base_of<std::random_access_iterator_tag,
+ array_iter_category>::value;
+}
+
+TEST_CASE( "Type conversions of RangeEquals and similar",
+ "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::RangeEquals;
+ using Catch::Matchers::UnorderedRangeEquals;
+
+ // In these test, we can always test RangeEquals and
+ // UnorderedRangeEquals in the same way, since we're mostly
+ // testing the template type deductions (and RangeEquals
+ // implies UnorderedRangeEquals)
+
+ SECTION( "Container conversions" ) {
+ SECTION( "Two equal containers of different container types" ) {
+ const std::array<int, 3> array_int_a{ { 1, 2, 3 } };
+ const int c_array[3] = { 1, 2, 3 };
+ CHECK_THAT( array_int_a, RangeEquals( c_array ) );
+ CHECK_THAT( array_int_a, UnorderedRangeEquals( c_array ) );
+ }
+ SECTION( "Two equal containers of different container types "
+ "(differ in array N)" ) {
+ const std::array<int, 3> array_int_3{ { 1, 2, 3 } };
+ const std::array<int, 4> array_int_4{ { 1, 2, 3, 4 } };
+ CHECK_THAT( array_int_3, !RangeEquals( array_int_4 ) );
+ CHECK_THAT( array_int_3, !UnorderedRangeEquals( array_int_4 ) );
+ }
+ SECTION( "Two equal containers of different container types and value "
+ "types" ) {
+ const std::array<int, 3> array_int_a{ { 1, 2, 3 } };
+ const std::vector<int> vector_char_a{ 1, 2, 3 };
+ CHECK_THAT( array_int_a, RangeEquals( vector_char_a ) );
+ CHECK_THAT( array_int_a, UnorderedRangeEquals( vector_char_a ) );
+ }
+ SECTION( "Two equal containers, one random access, one not" ) {
+ const std::array<int, 3> array_int_a{ { 1, 2, 3 } };
+ const std::list<int> list_char_a{ 1, 2, 3 };
+
+ // Verify these types really are different in random access nature
+ STATIC_REQUIRE( ContainerIsRandomAccess( array_int_a ) !=
+ ContainerIsRandomAccess( list_char_a ) );
+
+ CHECK_THAT( array_int_a, RangeEquals( list_char_a ) );
+ CHECK_THAT( array_int_a, UnorderedRangeEquals( list_char_a ) );
+ }
+ }
+
+ SECTION( "Value type" ) {
+ SECTION( "Two equal containers of different value types" ) {
+ const std::vector<int> vector_int_a{ 1, 2, 3 };
+ const std::vector<char> vector_char_a{ 1, 2, 3 };
+ CHECK_THAT( vector_int_a, RangeEquals( vector_char_a ) );
+ CHECK_THAT( vector_int_a, UnorderedRangeEquals( vector_char_a ) );
+ }
+ SECTION( "Two non-equal containers of different value types" ) {
+ const std::vector<int> vector_int_a{ 1, 2, 3 };
+ const std::vector<char> vector_char_b{ 1, 2, 2 };
+ CHECK_THAT( vector_int_a, !RangeEquals( vector_char_b ) );
+ CHECK_THAT( vector_int_a, !UnorderedRangeEquals( vector_char_b ) );
+ }
+ }
+
+ SECTION( "Ranges with begin that needs ADL" ) {
+ unrelated::needs_ADL_begin<int> a{ 1, 2, 3 }, b{ 3, 2, 1 };
+ REQUIRE_THAT( a, !RangeEquals( b ) );
+ REQUIRE_THAT( a, UnorderedRangeEquals( b ) );
+ }
+
+ SECTION( "Custom predicate" ) {
+
+ auto close_enough = []( int lhs, int rhs ) {
+ return std::abs( lhs - rhs ) <= 1;
+ };
+
+ SECTION( "Two equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 2, 3 } };
+ const std::array<char, 3> array_a_plus_1{ { 2, 3, 4 } };
+ CHECK_THAT( vector_a,
+ RangeEquals( array_a_plus_1, close_enough ) );
+ CHECK_THAT( vector_a,
+ UnorderedRangeEquals( array_a_plus_1, close_enough ) );
+ }
+ }
+} \ No newline at end of file