From ccfff4ac6a3fb52ad3edcf77f0c513e08df44c5d Mon Sep 17 00:00:00 2001 From: DNKpp Date: Fri, 25 Oct 2024 15:22:27 +0200 Subject: [PATCH] feat: matches::range::any_element --- include/mimic++/Matcher.hpp | 31 ++++++++++- test/unit-tests/matchers/RangeMatchers.cpp | 62 ++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/include/mimic++/Matcher.hpp b/include/mimic++/Matcher.hpp index 2e308f72..db176c8d 100644 --- a/include/mimic++/Matcher.hpp +++ b/include/mimic++/Matcher.hpp @@ -932,7 +932,7 @@ namespace mimicpp::matches::range } /** - * \brief Tests, whether the each element of the target range matches the specified matcher. + * \brief Tests, whether each element of the target range matches the specified matcher. * \param matcher The matcher. */ template @@ -960,6 +960,35 @@ namespace mimicpp::matches::range }; } + /** + * \brief Tests, whether any element of the target range matches the specified matcher. + * \param matcher The matcher. + */ + template + [[nodiscard]] + constexpr auto any_element(Matcher&& matcher) + { + using MatcherT = std::remove_cvref_t; + return PredicateMatcher{ + [](std::ranges::range auto&& target, const MatcherT& m) + { + return std::ranges::any_of( + target, + [&](const auto& element) { return m.matches(element); }); + }, + "any el in range: el {}", + "none el in range: el {}", + std::tuple{ + mimicpp::detail::arg_storage< + MatcherT, + std::identity, + decltype([](const auto& m) { return mimicpp::detail::describe_hook::describe(m); })>{ + std::forward(matcher) + } + } + }; + } + /** * \} */ diff --git a/test/unit-tests/matchers/RangeMatchers.cpp b/test/unit-tests/matchers/RangeMatchers.cpp index 7c90c4a8..f31d7167 100644 --- a/test/unit-tests/matchers/RangeMatchers.cpp +++ b/test/unit-tests/matchers/RangeMatchers.cpp @@ -484,3 +484,65 @@ TEST_CASE( } } } + +TEST_CASE( + "matches::range::any_element matches when at least one elements of the target range matches.", + "[matcher][matcher::range]" +) +{ + SECTION("Plain matcher.") + { + SECTION("When any element matches the matcher, it's a match.") + { + const auto threshold = GENERATE(42, 1337); + const auto matcher = matches::range::any_element(matches::ge(threshold)); + + const std::vector target{42, 1337}; + REQUIRE(matcher.matches(target)); + REQUIRE_THAT( + matcher.describe(), + Catch::Matchers::StartsWith("any el in range: el >= ")); + } + + SECTION("When no element matches the matcher, it's no match.") + { + const std::vector target = GENERATE( + std::vector{}, + (std::vector{42, 1337})); + const auto matcher = matches::range::any_element(matches::gt(1337)); + + REQUIRE(!matcher.matches(target)); + REQUIRE_THAT( + matcher.describe(), + Catch::Matchers::Equals("any el in range: el > 1337")); + } + } + + SECTION("Matcher can be inverted.") + { + SECTION("When any element matches the matcher, it's no match.") + { + const auto threshold = GENERATE(42, 1337); + const auto matcher = !matches::range::any_element(matches::ge(threshold)); + + const std::vector target{42, 1337}; + REQUIRE(!matcher.matches(target)); + REQUIRE_THAT( + matcher.describe(), + Catch::Matchers::StartsWith("none el in range: el >= ")); + } + + SECTION("When no element matches the matcher, it's a match.") + { + const std::vector target = GENERATE( + std::vector{}, + (std::vector{42, 1337})); + const auto matcher = !matches::range::any_element(matches::gt(1337)); + + REQUIRE(matcher.matches(target)); + REQUIRE_THAT( + matcher.describe(), + Catch::Matchers::StartsWith("none el in range: el > 1337")); + } + } +}