6#ifndef MIMICPP_MATCHER_HPP
7#define MIMICPP_MATCHER_HPP
27 template <
typename Matcher>
31namespace mimicpp::detail::matches_hook
33 template <
typename Matcher,
typename T>
35 constexpr bool matches_impl(
36 const Matcher& matcher,
44 } -> std::convertible_to<bool>;
47 return custom::matcher_traits<Matcher>{}.matches(matcher, target);
50 template <
typename Matcher,
typename T>
52 constexpr bool matches_impl(
53 const Matcher& matcher,
55 [[maybe_unused]]
const priority_tag<0>
57 requires requires { { matcher.matches(target) } -> std::convertible_to<bool>; }
59 return matcher.matches(target);
62 constexpr priority_tag<1> maxTag;
64 template <
typename Matcher,
typename T>
66 constexpr bool matches(
const Matcher& matcher, T& target)
67 requires requires { { matches_impl(matcher, target, maxTag) } -> std::convertible_to<bool>; }
69 return matches_impl(matcher, target, maxTag);
73namespace mimicpp::detail::describe_hook
75 template <
typename Matcher>
77 constexpr decltype(
auto) describe_impl(
78 const Matcher& matcher,
79 [[maybe_unused]]
const priority_tag<1>
84 custom::matcher_traits<Matcher>{}.describe(matcher)
85 } -> std::convertible_to<StringViewT>;
88 return custom::matcher_traits<Matcher>{}.describe(matcher);
91 template <
typename Matcher>
93 constexpr decltype(
auto) describe_impl(
94 const Matcher& matcher,
95 [[maybe_unused]]
const priority_tag<0>
97 requires requires { { matcher.describe() } -> std::convertible_to<StringViewT>; }
99 return matcher.describe();
102 constexpr priority_tag<1> maxTag;
104 template <
typename Matcher>
106 constexpr decltype(
auto) describe(
const Matcher& matcher)
107 requires requires { { describe_impl(matcher, maxTag) } -> std::convertible_to<StringViewT>; }
109 return describe_impl(matcher, maxTag);
115 template <
typename T,
typename Target>
117 && std::is_move_constructible_v<T>
118 && std::destructible<T>
119 &&
requires(
const T& matcher, Target& target)
121 { detail::matches_hook::matches(matcher, target) } -> std::convertible_to<bool>;
122 { detail::describe_hook::describe(matcher) } -> std::convertible_to<StringViewT>;
127 template <
typename Arg,
typename MatchesProjection = std::
identity,
typename DescribeProjection = Pr
intFn>
130 using matches_reference = std::invoke_result_t<MatchesProjection, const Arg&>;
131 using describe_reference = std::invoke_result_t<DescribeProjection, const Arg&>;
135 decltype(
auto) as_matches_arg()
const noexcept(std::is_nothrow_invocable_v<MatchesProjection, const Arg&>)
137 return std::invoke(MatchesProjection{}, arg);
140 decltype(
auto) as_describe_arg()
const noexcept(std::is_nothrow_invocable_v<DescribeProjection, const Arg&>)
142 return std::invoke(DescribeProjection{},
arg);
146 template <
typename T>
147 struct to_arg_storage
149 using type = arg_storage<T>;
152 template <
typename Arg,
typename MatchesProjection,
typename DescribeProjection>
153 struct to_arg_storage<arg_storage<Arg, MatchesProjection, DescribeProjection>>
155 using type = arg_storage<Arg, MatchesProjection, DescribeProjection>;
158 template <
typename T>
159 using to_arg_storage_t =
typename to_arg_storage<T>::type;
169 template <
typename Predicate,
typename... AdditionalArgs>
170 requires std::is_move_constructible_v<Predicate>
171 && (... && std::is_move_constructible_v<AdditionalArgs>)
175 using storage_t = std::tuple<detail::to_arg_storage_t<AdditionalArgs>...>;
176 template <
typename T>
177 using matches_reference_t =
typename detail::to_arg_storage_t<T>::matches_reference;
185 std::tuple<AdditionalArgs...> additionalArgs = {}
186 )
noexcept(std::is_nothrow_move_constructible_v<Predicate>
187 && (... && std::is_nothrow_move_constructible_v<AdditionalArgs>))
188 : m_Predicate{std::move(predicate)},
189 m_FormatString{std::move(fmt)},
190 m_InvertedFormatString{std::move(invertedFmt)},
191 m_AdditionalArgs{std::move(additionalArgs)}
195 template <
typename T>
196 requires std::predicate<
199 matches_reference_t<AdditionalArgs>...>
203 )
const noexcept(std::is_nothrow_invocable_v<const Predicate&, T&, matches_reference_t<AdditionalArgs>...>)
206 [&,
this](
auto&... additionalArgs)
211 additionalArgs.as_matches_arg()...);
220 [&,
this](
auto&... additionalArgs)
222 return format::vformat(
224 format::make_format_args(
227 [](
auto&& val)
noexcept ->
const auto& {
return val; },
228 additionalArgs.as_describe_arg())
235 constexpr auto operator !() const &
236 requires std::is_copy_constructible_v<Predicate>
237 && std::is_copy_constructible_v<storage_t>
239 return make_inverted(
241 m_InvertedFormatString,
247 constexpr auto operator !() &&
249 return make_inverted(
250 std::move(m_Predicate),
251 std::move(m_InvertedFormatString),
252 std::move(m_FormatString),
253 std::move(m_AdditionalArgs));
257 [[no_unique_address]] Predicate m_Predicate;
259 StringT m_InvertedFormatString;
260 storage_t m_AdditionalArgs;
262 template <
typename Fn>
264 static constexpr auto make_inverted(
271 using NotFnT =
decltype(std::not_fn(std::forward<Fn>(fn)));
273 std::not_fn(std::forward<Fn>(fn)),
275 std::move(invertedFmt),
290 static constexpr bool matches([[maybe_unused]]
auto&& target)
noexcept
297 return "has no constraints";
348 template <
typename T>
350 constexpr auto eq(T&& value)
356 std::tuple{std::forward<T>(value)}
365 template <
typename T>
367 constexpr auto ne(T&& value)
373 std::tuple{std::forward<T>(value)}
382 template <
typename T>
384 constexpr auto lt(T&& value)
390 std::tuple{std::forward<T>(value)}
399 template <
typename T>
401 constexpr auto le(T&& value)
407 std::tuple{std::forward<T>(value)}
416 template <
typename T>
418 constexpr auto gt(T&& value)
424 std::tuple{std::forward<T>(value)}
433 template <
typename T>
435 constexpr auto ge(T&& value)
438 std::greater_equal{},
441 std::tuple{std::forward<T>(value)}
453 template <
typename UnaryPredicate>
457 StringT description =
"passes predicate",
458 StringT invertedDescription =
"fails predicate"
463 std::move(description),
464 std::move(invertedDescription),
474 template <satisfies<std::is_lvalue_reference> T>
479 [](
const std::remove_cvref_t<T>& target,
const auto* instancePtr)
noexcept
481 return std::addressof(target) == instancePtr;
484 "is not instance at {}",
485 std::tuple{std::addressof(
instance)}
505namespace mimicpp::matches::detail
507 template <
string String>
509 constexpr auto make_view(String&& str)
511 using traits_t = string_traits<std::remove_cvref_t<String>>;
512 return traits_t::view(std::forward<String>(str));
515 template <case_foldable_
string String>
517 constexpr auto make_case_folded_string(String&& str)
521 detail::make_view(std::forward<String>(str)));
524 template <
string Target,
string Pattern>
525 constexpr void check_string_compatibility() noexcept
531 "Pattern and target string must have the same character-type.");
592 template <
string Pattern>
594 constexpr auto eq(Pattern&& pattern)
597 []<
string T,
typename Stored>(T&& target, Stored&& stored)
599 detail::check_string_compatibility<T, Pattern>();
601 return std::ranges::equal(
602 detail::make_view(std::forward<T>(target)),
603 detail::make_view(std::forward<Stored>(stored)));
606 "is not equal to {}",
607 std::tuple{std::forward<Pattern>(pattern)}
616 template <case_foldable_
string Pattern>
623 detail::check_string_compatibility<T, Pattern>();
625 return std::ranges::equal(
626 detail::make_case_folded_string(std::forward<T>(target)),
627 detail::make_case_folded_string(std::forward<Stored>(stored)));
629 "is case-insensitively equal to {}",
630 "is case-insensitively not equal to {}",
631 std::tuple{std::forward<Pattern>(pattern)}
640 template <
string Pattern>
645 []<
string T,
typename Stored>(T&& target, Stored&& stored)
647 detail::check_string_compatibility<T, Pattern>();
649 auto patternView = detail::make_view(std::forward<Stored>(stored));
650 const auto [ignore, patternIter] = std::ranges::mismatch(
651 detail::make_view(std::forward<T>(target)),
654 return patternIter == std::ranges::end(patternView);
657 "starts not with {}",
658 std::tuple{std::forward<Pattern>(pattern)}
667 template <
string Pattern>
672 []<
string T,
typename Stored>(T&& target, Stored&& stored)
674 detail::check_string_compatibility<T, Pattern>();
676 auto caseFoldedPattern = detail::make_case_folded_string(std::forward<Stored>(stored));
677 const auto [ignore, patternIter] = std::ranges::mismatch(
678 detail::make_case_folded_string(std::forward<T>(target)),
681 return patternIter == std::ranges::end(caseFoldedPattern);
683 "case-insensitively starts with {}",
684 "case-insensitively starts not with {}",
685 std::tuple{std::forward<Pattern>(pattern)}
694 template <
string Pattern>
699 []<
string T,
typename Stored>(T&& target, Stored&& stored)
701 detail::check_string_compatibility<T, Pattern>();
703 auto patternView = detail::make_view(std::forward<Stored>(stored))
704 | std::views::reverse;
705 const auto [ignore, patternIter] = std::ranges::mismatch(
706 detail::make_view(std::forward<T>(target)) | std::views::reverse,
709 return patternIter == std::ranges::end(patternView);
713 std::tuple{std::forward<Pattern>(pattern)}
722 template <
string Pattern>
727 []<
string T,
typename Stored>(T&& target, Stored&& stored)
729 detail::check_string_compatibility<T, Pattern>();
731 auto caseFoldedPattern = detail::make_case_folded_string(std::forward<Stored>(stored))
732 | std::views::reverse;
733 const auto [ignore, patternIter] = std::ranges::mismatch(
734 detail::make_case_folded_string(std::forward<T>(target)) | std::views::reverse,
737 return patternIter == std::ranges::end(caseFoldedPattern);
739 "case-insensitively ends with {}",
740 "case-insensitively ends not with {}",
741 std::tuple{std::forward<Pattern>(pattern)}
750 template <
string Pattern>
755 []<
string T,
typename Stored>(T&& target, Stored&& stored)
757 detail::check_string_compatibility<T, Pattern>();
759 auto patternView = detail::make_view(std::forward<Stored>(stored));
760 return std::ranges::empty(patternView)
761 || !std::ranges::empty(
763 detail::make_view(std::forward<T>(target)),
764 std::move(patternView)));
768 std::tuple{std::forward<Pattern>(pattern)}
777 template <
string Pattern>
782 []<
string T,
typename Stored>(T&& target, Stored&& stored)
784 detail::check_string_compatibility<T, Pattern>();
786 auto patternView = detail::make_case_folded_string(std::forward<Stored>(stored));
787 auto targetView = detail::make_case_folded_string(std::forward<T>(target));
788 return std::ranges::empty(patternView)
789 || !std::ranges::empty(std::ranges::search(targetView, patternView));
791 "case-insensitively contains {}",
792 "case-insensitively contains not {}",
793 std::tuple{std::forward<Pattern>(pattern)}
820 template <std::ranges::forward_range Range,
typename Comparator = std::equal_to<>>
822 constexpr auto eq(Range&& expected, Comparator comparator = Comparator{})
825 [comp = std::move(comparator)]
826 <
typename Target>(Target&& target,
auto& range)
827 requires std::predicate<
829 std::ranges::range_reference_t<Target>,
830 std::ranges::range_reference_t<Range>>
832 return std::ranges::equal(
838 "elements are not {}",
839 std::tuple{std::views::all(std::forward<Range>(expected))}
850 template <std::ranges::forward_range Range,
typename Comparator = std::equal_to<>>
852 constexpr auto unordered_eq(Range&& expected, Comparator comparator = Comparator{})
855 [comp = std::move(comparator)]<
typename Target
856 >(Target&& target,
auto& range)
857 requires std::predicate<
859 std::ranges::range_reference_t<Target>,
860 std::ranges::range_reference_t<Range>>
862 return std::ranges::is_permutation(
867 "is a permutation of {}",
868 "is not a permutation of {}",
869 std::tuple{std::views::all(std::forward<Range>(expected))}
878 template <
typename Relation = std::ranges::less>
880 constexpr auto is_sorted(Relation relation = Relation{})
883 [rel = std::move(relation)]<
typename Target>(Target&& target)
884 requires std::equivalence_relation<
886 std::ranges::range_reference_t<Target>,
887 std::ranges::range_reference_t<Target>>
889 return std::ranges::is_sorted(
894 "is an unsorted range"
905 [](std::ranges::range
auto&& target)
907 return std::ranges::empty(target);
910 "is not an empty range"
919 constexpr auto has_size(
const std::integral
auto expected)
922 [](std::ranges::range
auto&& target,
const std::integral
auto size)
924 return std::cmp_equal(
926 std::ranges::size(target));
929 "has different size than {}",
938 template <
typename Matcher>
942 using MatcherT = std::remove_cvref_t<Matcher>;
944 [](std::ranges::range
auto&& target,
const MatcherT& m)
946 return std::ranges::all_of(
948 [&](
const auto& element) {
return m.matches(element); });
950 "each el in range: el {}",
951 "not each el in range: el {}",
953 mimicpp::detail::arg_storage<
956 decltype([](
const auto& m) {
return mimicpp::detail::describe_hook::describe(m); })>{
957 std::forward<MatcherT>(matcher)
967 template <
typename Matcher>
971 using MatcherT = std::remove_cvref_t<Matcher>;
973 [](std::ranges::range
auto&& target,
const MatcherT& m)
975 return std::ranges::any_of(
977 [&](
const auto& element) {
return m.matches(element); });
979 "any el in range: el {}",
980 "none el in range: el {}",
982 mimicpp::detail::arg_storage<
985 decltype([](
const auto& m) {
return mimicpp::detail::describe_hook::describe(m); })>{
986 std::forward<MatcherT>(matcher)
Generic matcher and the basic building block of most of the built-in matchers.
Definition Matcher.hpp:173
constexpr PredicateMatcher(Predicate predicate, StringT fmt, StringT invertedFmt, std::tuple< AdditionalArgs... > additionalArgs={}) noexcept(std::is_nothrow_move_constructible_v< Predicate > &&(... &&std::is_nothrow_move_constructible_v< AdditionalArgs >))
Definition Matcher.hpp:181
constexpr StringT describe() const
Definition Matcher.hpp:217
constexpr bool matches(T &target) const noexcept(std::is_nothrow_invocable_v< const Predicate &, T &, matches_reference_t< AdditionalArgs >... >)
Definition Matcher.hpp:201
Matcher, which never fails.
Definition Matcher.hpp:288
static constexpr bool matches(auto &&target) noexcept
Definition Matcher.hpp:290
static constexpr StringViewT describe() noexcept
Definition Matcher.hpp:295
Determines, whether the given type supports string normalization.
Definition String.hpp:377
Definition Matcher.hpp:116
constexpr auto eq(Range &&expected, Comparator comparator=Comparator{})
Tests, whether the target range compares equal to the expected range, by comparing them element-wise.
Definition Matcher.hpp:822
constexpr auto each_element(Matcher &&matcher)
Tests, whether each element of the target range matches the specified matcher.
Definition Matcher.hpp:940
constexpr auto unordered_eq(Range &&expected, Comparator comparator=Comparator{})
Tests, whether the target range is a permutation of the expected range, by comparing them element-wis...
Definition Matcher.hpp:852
constexpr auto any_element(Matcher &&matcher)
Tests, whether any element of the target range matches the specified matcher.
Definition Matcher.hpp:969
constexpr auto has_size(const std::integral auto expected)
Tests, whether the target range has the expected size.
Definition Matcher.hpp:919
constexpr auto is_sorted(Relation relation=Relation{})
Tests, whether the target range is sorted, by applying the relation on each adjacent elements.
Definition Matcher.hpp:880
constexpr auto is_empty()
Tests, whether the target range is empty.
Definition Matcher.hpp:902
constexpr auto lt(T &&value)
Tests, whether the target is less than the expected value.
Definition Matcher.hpp:384
constexpr auto instance(T &&instance)
Tests, whether the target is the expected instance.
Definition Matcher.hpp:476
constexpr auto le(T &&value)
Tests, whether the target is less than or equal to the expected value.
Definition Matcher.hpp:401
constexpr auto predicate(UnaryPredicate &&predicate, StringT description="passes predicate", StringT invertedDescription="fails predicate")
Tests, whether the target fulfills the given predicate.
Definition Matcher.hpp:455
constexpr WildcardMatcher _
The wildcard matcher, always matching.
Definition Matcher.hpp:341
constexpr auto eq(T &&value)
Tests, whether the target compares equal to the expected value.
Definition Matcher.hpp:350
constexpr auto gt(T &&value)
Tests, whether the target is greater than the expected value.
Definition Matcher.hpp:418
constexpr auto ge(T &&value)
Tests, whether the target is greater than or equal to the expected value.
Definition Matcher.hpp:435
constexpr auto ne(T &&value)
Tests, whether the target compares not equal to the expected value.
Definition Matcher.hpp:367
struct mimicpp::case_insensitive_t case_insensitive
constexpr auto eq(Pattern &&pattern)
Tests, whether the target string compares equal to the expected string.
Definition Matcher.hpp:594
constexpr auto ends_with(Pattern &&pattern)
Tests, whether the target string ends with the pattern string.
Definition Matcher.hpp:696
constexpr auto contains(Pattern &&pattern)
Tests, whether the pattern string is part of the target string.
Definition Matcher.hpp:752
constexpr auto starts_with(Pattern &&pattern)
Tests, whether the target string starts with the pattern string.
Definition Matcher.hpp:642
constexpr auto arg(Matcher &&matcher, Projection projection={}) noexcept(std::is_nothrow_constructible_v< std::remove_cvref_t< Matcher >, Matcher > &&std::is_nothrow_move_constructible_v< Projection >)
Checks, whether the selected argument matches the given matcher.
Definition ExpectationPolicies.hpp:405
typename string_traits< std::remove_cvref_t< T > >::char_t string_char_t
Computes the character type for the given string.
Definition String.hpp:260
Definition Matcher.hpp:26
Definition Matcher.hpp:803
Definition Matcher.hpp:536
Definition Matcher.hpp:303
Definition BoostTest.hpp:20
std::basic_string_view< CharT, CharTraitsT > StringViewT
Definition Fwd.hpp:208
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:207
Tag type, used in string matchers.
Definition Matcher.hpp:501
Definition Matcher.hpp:28
Definition Utility.hpp:32