mirror of
https://github.com/catchorg/Catch2.git
synced 2025-08-01 21:05:39 +02:00
Add more comprehensive tests for the quantifier matchers
This includes * Testing both positive and negative path through the matchers * Testing them with types whose `begin` and `end` member functions require ADL * Testing them with types that return different types from `begin` and `end`
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <catch2/matchers/catch_matchers_contains.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 <array>
|
||||
@@ -33,8 +34,143 @@ namespace unrelated {
|
||||
}
|
||||
};
|
||||
} // end unrelated namespace
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
class has_different_begin_end_types {
|
||||
std::array<int, 5> elements{ {1, 2, 3, 4, 5} };
|
||||
|
||||
// Different type for the "end" iterator
|
||||
struct iterator_end {};
|
||||
// Just a fake forward iterator, that only compares to a different
|
||||
// type (so we can test two-type ranges).
|
||||
struct iterator {
|
||||
int const* start;
|
||||
int const* end;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = int;
|
||||
using reference = int const&;
|
||||
using pointer = int const*;
|
||||
|
||||
|
||||
friend bool operator==( iterator iter, iterator_end ) {
|
||||
return iter.start == iter.end;
|
||||
}
|
||||
friend bool operator!=( iterator iter, iterator_end ) {
|
||||
return iter.start != iter.end;
|
||||
}
|
||||
iterator& operator++() {
|
||||
++start;
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int) {
|
||||
auto tmp(*this);
|
||||
++start;
|
||||
return tmp;
|
||||
}
|
||||
reference operator*() const {
|
||||
return *start;
|
||||
}
|
||||
pointer operator->() const {
|
||||
return start;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
iterator begin() const {
|
||||
return { elements.data(), elements.data() + elements.size() };
|
||||
}
|
||||
|
||||
iterator_end end() const {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
struct with_mocked_iterator_access {
|
||||
static constexpr size_t data_size = 5;
|
||||
std::array<int, data_size> elements{ {1, 2, 3, 4, 5} };
|
||||
std::array<bool, data_size> touched{};
|
||||
std::array<bool, data_size> derefed{};
|
||||
|
||||
// We want to check which elements were touched when iterating, so
|
||||
// we can check whether iterator-using code traverses range correctly
|
||||
struct iterator {
|
||||
with_mocked_iterator_access* m_origin;
|
||||
size_t m_origin_idx;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = int;
|
||||
using reference = int const&;
|
||||
using pointer = int const*;
|
||||
|
||||
friend bool operator==(iterator lhs, iterator rhs) {
|
||||
return lhs.m_origin == rhs.m_origin
|
||||
&& lhs.m_origin_idx == rhs.m_origin_idx;
|
||||
}
|
||||
friend bool operator!=(iterator lhs, iterator rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
iterator& operator++() {
|
||||
++m_origin_idx;
|
||||
assert(m_origin_idx < data_size + 1 && "Outside of valid alloc");
|
||||
if (m_origin_idx < data_size) {
|
||||
m_origin->touched[m_origin_idx] = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int) {
|
||||
auto tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
reference operator*() const {
|
||||
assert(m_origin_idx < data_size && "Attempted to deref invalid position");
|
||||
m_origin->derefed[m_origin_idx] = true;
|
||||
return m_origin->elements[m_origin_idx];
|
||||
}
|
||||
pointer operator->() const {
|
||||
assert(m_origin_idx < data_size && "Attempted to deref invalid position");
|
||||
return &m_origin->elements[m_origin_idx];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
iterator begin() const {
|
||||
// Const-cast away to avoid overcomplicating the iterators
|
||||
// We should actually fix this over time
|
||||
return { const_cast<with_mocked_iterator_access*>(this), 0 };
|
||||
}
|
||||
iterator end() const {
|
||||
return { const_cast<with_mocked_iterator_access*>(this), data_size };
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // end anon namespace
|
||||
|
||||
namespace Catch {
|
||||
template <>
|
||||
struct StringMaker<with_mocked_iterator_access> {
|
||||
static std::string convert(with_mocked_iterator_access const& access) {
|
||||
// We have to avoid the type's iterators, because we check
|
||||
// their use in tests
|
||||
return ::Catch::Detail::stringify(access.elements);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct MoveOnlyTestElement {
|
||||
int num = 0;
|
||||
MoveOnlyTestElement(int n) :num(n) {}
|
||||
@@ -220,55 +356,190 @@ TEST_CASE("Usage of the SizeIs range matcher", "[matchers][templated][size]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Usage of the quantifiers matchers", "[matchers][templated][quantifiers]") {
|
||||
|
||||
TEST_CASE("Usage of AllMatch range matcher", "[matchers][templated][quantifiers]") {
|
||||
using Catch::Matchers::AllMatch;
|
||||
using Catch::Matchers::AnyMatch;
|
||||
using Catch::Matchers::Contains;
|
||||
using Catch::Matchers::EndsWith;
|
||||
using Catch::Matchers::IsEmpty;
|
||||
using Catch::Matchers::NoneMatch;
|
||||
using Catch::Matchers::SizeIs;
|
||||
using Catch::Matchers::StartsWith;
|
||||
std::array<std::array<int, 5>, 5> int_arr_arr{{
|
||||
{{ 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 }}
|
||||
}};
|
||||
std::vector<std::string> string_vector{ "Command+", "Catch2+", "CMake+", "C++", "Console+" };
|
||||
std::list<std::vector<int>> int_vectors_list{ { 1, 2 }, { 3, 4 }, { 5, 6 } };
|
||||
using Catch::Matchers::Predicate;
|
||||
|
||||
SECTION("Usage of the AllMatch range matcher") {
|
||||
REQUIRE_THAT(int_arr_arr, AllMatch(Contains(0)));
|
||||
REQUIRE_THAT(int_arr_arr, AllMatch(SizeIs(5)));
|
||||
SECTION("Basic usage") {
|
||||
using Catch::Matchers::Contains;
|
||||
using Catch::Matchers::SizeIs;
|
||||
|
||||
REQUIRE_THAT(string_vector, AllMatch(StartsWith("C")));
|
||||
REQUIRE_THAT(string_vector, AllMatch(EndsWith("+")));
|
||||
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(int_vectors_list, AllMatch(!IsEmpty()));
|
||||
REQUIRE_THAT(int_vectors_list, AllMatch(SizeIs(2)));
|
||||
REQUIRE_THAT(data, AllMatch(SizeIs(5)));
|
||||
REQUIRE_THAT(data, !AllMatch(Contains(0) && Contains(1)));
|
||||
}
|
||||
|
||||
SECTION("Usage of the AnyMatch range matcher") {
|
||||
REQUIRE_THAT(int_arr_arr, AnyMatch(Contains(-2)));
|
||||
REQUIRE_THAT(int_arr_arr, AnyMatch(SizeIs(5)));
|
||||
|
||||
REQUIRE_THAT(string_vector, AnyMatch(StartsWith("CMak")));
|
||||
REQUIRE_THAT(string_vector, AnyMatch(EndsWith("++")));
|
||||
|
||||
REQUIRE_THAT(int_vectors_list, AnyMatch(Contains(4)));
|
||||
REQUIRE_THAT(int_vectors_list, AnyMatch(SizeIs(2)));
|
||||
SECTION("Type requires ADL found begin and end") {
|
||||
unrelated::needs_ADL_begin needs_adl;
|
||||
REQUIRE_THAT( needs_adl, AllMatch( Predicate<int>( []( int elem ) {
|
||||
return elem < 6;
|
||||
} ) ) );
|
||||
}
|
||||
|
||||
SECTION("Usage of the NoneMatch range matcher") {
|
||||
REQUIRE_THAT(int_arr_arr, NoneMatch(Contains(-6)));
|
||||
REQUIRE_THAT(int_arr_arr, NoneMatch(SizeIs(42)));
|
||||
|
||||
REQUIRE_THAT(string_vector, NoneMatch(StartsWith("abd")));
|
||||
REQUIRE_THAT(string_vector, NoneMatch(EndsWith("#!--")));
|
||||
|
||||
REQUIRE_THAT(int_vectors_list, NoneMatch(IsEmpty()));
|
||||
REQUIRE_THAT(int_vectors_list, NoneMatch(SizeIs(3)));
|
||||
SECTION("Shortcircuiting") {
|
||||
with_mocked_iterator_access mocked;
|
||||
SECTION("All are read") {
|
||||
auto allMatch = AllMatch(Predicate<int>([](int elem) {
|
||||
return elem < 10;
|
||||
}));
|
||||
REQUIRE_THAT(mocked, allMatch);
|
||||
REQUIRE(mocked.derefed[0]);
|
||||
REQUIRE(mocked.derefed[1]);
|
||||
REQUIRE(mocked.derefed[2]);
|
||||
REQUIRE(mocked.derefed[3]);
|
||||
REQUIRE(mocked.derefed[4]);
|
||||
}
|
||||
SECTION("Short-circuited") {
|
||||
auto allMatch = AllMatch(Predicate<int>([](int elem) {
|
||||
return elem < 3;
|
||||
}));
|
||||
REQUIRE_THAT(mocked, !allMatch);
|
||||
REQUIRE(mocked.derefed[0]);
|
||||
REQUIRE(mocked.derefed[1]);
|
||||
REQUIRE(mocked.derefed[2]);
|
||||
REQUIRE_FALSE(mocked.derefed[3]);
|
||||
REQUIRE_FALSE(mocked.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 needs_adl;
|
||||
REQUIRE_THAT( needs_adl, AnyMatch( Predicate<int>( []( int elem ) {
|
||||
return elem < 3;
|
||||
} ) ) );
|
||||
}
|
||||
|
||||
SECTION("Shortcircuiting") {
|
||||
with_mocked_iterator_access mocked;
|
||||
SECTION("All are read") {
|
||||
auto anyMatch = AnyMatch(
|
||||
Predicate<int>( []( int elem ) { return elem > 10; } ) );
|
||||
REQUIRE_THAT( mocked, !anyMatch );
|
||||
REQUIRE( mocked.derefed[0] );
|
||||
REQUIRE( mocked.derefed[1] );
|
||||
REQUIRE( mocked.derefed[2] );
|
||||
REQUIRE( mocked.derefed[3] );
|
||||
REQUIRE( mocked.derefed[4] );
|
||||
}
|
||||
SECTION("Short-circuited") {
|
||||
auto anyMatch = AnyMatch(
|
||||
Predicate<int>( []( int elem ) { return elem < 3; } ) );
|
||||
REQUIRE_THAT( mocked, anyMatch );
|
||||
REQUIRE( mocked.derefed[0] );
|
||||
REQUIRE_FALSE( mocked.derefed[1] );
|
||||
REQUIRE_FALSE( mocked.derefed[2] );
|
||||
REQUIRE_FALSE( mocked.derefed[3] );
|
||||
REQUIRE_FALSE( mocked.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 needs_adl;
|
||||
REQUIRE_THAT( needs_adl, NoneMatch( Predicate<int>( []( int elem ) {
|
||||
return elem > 6;
|
||||
} ) ) );
|
||||
}
|
||||
|
||||
SECTION("Shortcircuiting") {
|
||||
with_mocked_iterator_access mocked;
|
||||
SECTION("All are read") {
|
||||
auto noneMatch = NoneMatch(
|
||||
Predicate<int>([](int elem) { return elem > 10; }));
|
||||
REQUIRE_THAT(mocked, noneMatch);
|
||||
REQUIRE(mocked.derefed[0]);
|
||||
REQUIRE(mocked.derefed[1]);
|
||||
REQUIRE(mocked.derefed[2]);
|
||||
REQUIRE(mocked.derefed[3]);
|
||||
REQUIRE(mocked.derefed[4]);
|
||||
}
|
||||
SECTION("Short-circuited") {
|
||||
auto noneMatch = NoneMatch(
|
||||
Predicate<int>([](int elem) { return elem < 3; }));
|
||||
REQUIRE_THAT(mocked, !noneMatch);
|
||||
REQUIRE(mocked.derefed[0]);
|
||||
REQUIRE_FALSE(mocked.derefed[1]);
|
||||
REQUIRE_FALSE(mocked.derefed[2]);
|
||||
REQUIRE_FALSE(mocked.derefed[3]);
|
||||
REQUIRE_FALSE(mocked.derefed[4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is a C++17 extension, and GCC refuses to compile such code
|
||||
// unless it 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::AnyMatch;
|
||||
using Catch::Matchers::NoneMatch;
|
||||
|
||||
using Catch::Matchers::Predicate;
|
||||
|
||||
has_different_begin_end_types diff_types;
|
||||
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;
|
||||
} ) ) );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user