mimic++ v1
Loading...
Searching...
No Matches
Matcher.hpp
Go to the documentation of this file.
1// // Copyright Dominic (DNKpp) Koepke 2024 - 2024.
2// // Distributed under the Boost Software License, Version 1.0.
3// // (See accompanying file LICENSE_1_0.txt or copy at
4// // https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_MATCHER_HPP
7#define MIMICPP_MATCHER_HPP
8
9#pragma once
10
11#include "mimic++/Printer.hpp"
12#include "mimic++/Utility.hpp"
13
14#include <algorithm>
15#include <concepts>
16#include <functional>
17#include <optional>
18#include <ranges>
19#include <tuple>
20#include <type_traits>
21
22namespace mimicpp
23{
24 template <typename T, typename Target>
25 concept matcher_for = std::same_as<T, std::remove_cvref_t<T>>
26 && std::is_move_constructible_v<T>
27 && std::destructible<T>
28 && requires(const T& matcher, Target& target)
29 {
30 { matcher.matches(target) } -> std::convertible_to<bool>;
31 { matcher.describe() } -> std::convertible_to<std::optional<StringT>>;
32 };
33
41 template <typename Predicate, typename... AdditionalArgs>
42 requires std::is_move_constructible_v<Predicate>
43 && (... && std::is_move_constructible_v<AdditionalArgs>)
45 {
46 public:
47 [[nodiscard]]
48 explicit constexpr PredicateMatcher(
49 Predicate predicate,
50 StringT fmt,
51 StringT invertedFmt,
52 std::tuple<AdditionalArgs...> additionalArgs = std::tuple{}
53 ) noexcept(std::is_nothrow_move_constructible_v<Predicate>
54 && (... && std::is_nothrow_move_constructible_v<AdditionalArgs>))
55 : m_Predicate{std::move(predicate)},
56 m_FormatString{std::move(fmt)},
57 m_InvertedFormatString{std::move(invertedFmt)},
58 m_AdditionalArgs{std::move(additionalArgs)}
59 {
60 }
61
62 template <typename T>
63 requires std::predicate<const Predicate&, T&, const AdditionalArgs&...>
64 [[nodiscard]]
65 constexpr bool matches(
66 T& target
67 ) const noexcept(std::is_nothrow_invocable_v<const Predicate&, T&, const AdditionalArgs&...>)
68 {
69 return std::apply(
70 [&, this](auto&... additionalArgs)
71 {
72 return std::invoke(
73 m_Predicate,
74 target,
75 additionalArgs...);
76 },
77 m_AdditionalArgs);
78 }
79
80 [[nodiscard]]
81 constexpr StringT describe() const
82 {
83 return std::apply(
84 [&, this](auto&... additionalArgs)
85 {
86 // std::make_format_args requires lvalue-refs, so let's transform rvalue-refs to const lvalue-refs
87 constexpr auto makeLvalue = [](auto&& val) noexcept -> const auto& { return val; };
88 return format::vformat(
89 m_FormatString,
90 format::make_format_args(
91 makeLvalue(mimicpp::print(additionalArgs))...));
92 },
93 m_AdditionalArgs);
94 }
95
96 [[nodiscard]]
97 constexpr auto operator !() const &
98 requires std::is_copy_constructible_v<Predicate>
99 && (... && std::is_copy_constructible_v<AdditionalArgs>)
100 {
101 return make_inverted(
102 m_Predicate,
103 m_InvertedFormatString,
104 m_FormatString,
105 std::move(m_AdditionalArgs));
106 }
107
108 [[nodiscard]]
109 constexpr auto operator !() &&
110 {
111 return make_inverted(
112 std::move(m_Predicate),
113 std::move(m_InvertedFormatString),
114 std::move(m_FormatString),
115 std::move(m_AdditionalArgs));
116 }
117
118 private:
119 [[no_unique_address]] Predicate m_Predicate;
120 StringT m_FormatString;
121 StringT m_InvertedFormatString;
122 mutable std::tuple<AdditionalArgs...> m_AdditionalArgs{};
123
124 template <typename Fn>
125 [[nodiscard]]
126 static constexpr auto make_inverted(
127 Fn&& fn,
128 StringT fmt,
129 StringT invertedFmt,
130 std::tuple<AdditionalArgs...> tuple
131 )
132 {
133 using NotFnT = decltype(std::not_fn(std::forward<Fn>(fn)));
134 return PredicateMatcher<NotFnT, AdditionalArgs...>{
135 std::not_fn(std::forward<Fn>(fn)),
136 std::move(fmt),
137 std::move(invertedFmt),
138 std::move(tuple)
139 };
140 }
141 };
142
150 {
151 public:
152 static constexpr bool matches(auto&& target) noexcept
153 {
154 return true;
155 }
156
157 static constexpr std::nullopt_t describe() noexcept
158 {
159 return std::nullopt;
160 }
161 };
162}
163
165{
203 inline constexpr WildcardMatcher _{};
204
210 template <typename T>
211 [[nodiscard]]
212 constexpr auto eq(T&& value)
213 {
214 return PredicateMatcher{
215 std::ranges::equal_to{},
216 "== {}",
217 "!= {}",
218 std::tuple{std::forward<T>(value)}
219 };
220 }
221
227 template <typename T>
228 [[nodiscard]]
229 constexpr auto ne(T&& value)
230 {
231 return PredicateMatcher{
232 std::ranges::not_equal_to{},
233 "!= {}",
234 "== {}",
235 std::tuple{std::forward<T>(value)}
236 };
237 }
238
244 template <typename T>
245 [[nodiscard]]
246 constexpr auto lt(T&& value)
247 {
248 return PredicateMatcher{
249 std::ranges::less{},
250 "< {}",
251 ">= {}",
252 std::tuple{std::forward<T>(value)}
253 };
254 }
255
261 template <typename T>
262 [[nodiscard]]
263 constexpr auto le(T&& value)
264 {
265 return PredicateMatcher{
266 std::ranges::less_equal{},
267 "<= {}",
268 "> {}",
269 std::tuple{std::forward<T>(value)}
270 };
271 }
272
278 template <typename T>
279 [[nodiscard]]
280 constexpr auto gt(T&& value)
281 {
282 return PredicateMatcher{
283 std::ranges::greater{},
284 "> {}",
285 "<= {}",
286 std::tuple{std::forward<T>(value)}
287 };
288 }
289
295 template <typename T>
296 [[nodiscard]]
297 constexpr auto ge(T&& value)
298 {
299 return PredicateMatcher{
300 std::ranges::greater_equal{},
301 ">= {}",
302 "< {}",
303 std::tuple{std::forward<T>(value)}
304 };
305 }
306
315 template <typename UnaryPredicate>
316 [[nodiscard]]
317 constexpr auto predicate(
318 UnaryPredicate&& predicate,
319 StringT description = "passes predicate",
320 StringT invertedDescription = "fails predicate"
321 )
322 {
323 return PredicateMatcher{
324 std::forward<UnaryPredicate>(predicate),
325 std::move(description),
326 std::move(invertedDescription),
327 };
328 }
329
333}
334
336{
353 template <typename Char, typename Traits, typename Allocator>
354 [[nodiscard]]
355 constexpr auto eq(std::basic_string<Char, Traits, Allocator> expected)
356 {
357 using ViewT = std::basic_string_view<Char>;
358 return PredicateMatcher{
359 [](const ViewT target, const ViewT exp)
360 {
361 return target == exp;
362 },
363 "is equal to {}",
364 "is not equal to {}",
365 std::tuple{std::move(expected)}
366 };
367 }
368
374 template <typename Char>
375 [[nodiscard]]
376 constexpr auto eq(const Char* expected)
377 {
378 return eq(
379 std::basic_string<Char>{expected});
380 }
381
385}
386
388{
405 template <std::ranges::forward_range Range, typename Comparator = std::equal_to<>>
406 [[nodiscard]]
407 constexpr auto eq(Range&& expected, Comparator comparator = Comparator{})
408 {
409 return PredicateMatcher{
410 [comp = std::move(comparator)]
411 <typename Target>(Target&& target, auto& range) // NOLINT(cppcoreguidelines-missing-std-forward)
412 requires std::predicate<
413 const Comparator&,
414 std::ranges::range_reference_t<Target>,
415 std::ranges::range_reference_t<Range>>
416 {
417 return std::ranges::equal(
418 target,
419 range,
420 std::ref(comp));
421 },
422 "elements are {}",
423 "elements are not {}",
424 std::tuple{std::views::all(std::forward<Range>(expected))}
425 };
426 }
427
435 template <std::ranges::forward_range Range, typename Comparator = std::equal_to<>>
436 [[nodiscard]]
437 constexpr auto unordered_eq(Range&& expected, Comparator comparator = Comparator{})
438 {
439 return PredicateMatcher{
440 [comp = std::move(comparator)]<typename Target
441 >(Target&& target, auto& range) // NOLINT(cppcoreguidelines-missing-std-forward)
442 requires std::predicate<
443 const Comparator&,
444 std::ranges::range_reference_t<Target>,
445 std::ranges::range_reference_t<Range>>
446 {
447 return std::ranges::is_permutation(
448 target,
449 range,
450 std::ref(comp));
451 },
452 "is a permutation of {}",
453 "is not a permutation of {}",
454 std::tuple{std::views::all(std::forward<Range>(expected))}
455 };
456 }
457
463 template <typename Relation = std::ranges::less>
464 [[nodiscard]]
465 constexpr auto is_sorted(Relation relation = Relation{})
466 {
467 return PredicateMatcher{
468 [rel = std::move(relation)]<typename Target>(Target&& target) // NOLINT(cppcoreguidelines-missing-std-forward)
469 requires std::equivalence_relation<
470 const Relation&,
471 std::ranges::range_reference_t<Target>,
472 std::ranges::range_reference_t<Target>>
473 {
474 return std::ranges::is_sorted(
475 target,
476 std::ref(rel));
477 },
478 "is a sorted range",
479 "is an unsorted range"
480 };
481 }
482
486 [[nodiscard]]
487 constexpr auto is_empty()
488 {
489 return PredicateMatcher{
490 [](std::ranges::range auto&& target)
491 {
492 return std::ranges::empty(target);
493 },
494 "is an empty range",
495 "is not an empty range"
496 };
497 }
498
503 [[nodiscard]]
504 constexpr auto has_size(const std::integral auto expected)
505 {
506 return PredicateMatcher{
507 [](std::ranges::range auto&& target, const std::integral auto size)
508 {
509 return std::cmp_equal(
510 size,
511 std::ranges::size(target));
512 },
513 "has size of {}",
514 "has different size than {}",
515 std::tuple{expected}
516 };
517 }
518
522}
523
524#endif
Generic matcher and the basic building block of most of the built-in matchers.
Definition Matcher.hpp:45
constexpr bool matches(T &target) const noexcept(std::is_nothrow_invocable_v< const Predicate &, T &, const AdditionalArgs &... >)
Definition Matcher.hpp:65
constexpr StringT describe() const
Definition Matcher.hpp:81
constexpr PredicateMatcher(Predicate predicate, StringT fmt, StringT invertedFmt, std::tuple< AdditionalArgs... > additionalArgs=std::tuple{}) noexcept(std::is_nothrow_move_constructible_v< Predicate > &&(... &&std::is_nothrow_move_constructible_v< AdditionalArgs >))
Definition Matcher.hpp:48
Matcher, which never fails.
Definition Matcher.hpp:150
static constexpr bool matches(auto &&target) noexcept
Definition Matcher.hpp:152
static constexpr std::nullopt_t describe() noexcept
Definition Matcher.hpp:157
Definition Matcher.hpp:25
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:407
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:437
constexpr auto has_size(const std::integral auto expected)
Tests, whether the target range has the expected size.
Definition Matcher.hpp:504
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:465
constexpr auto is_empty()
Tests, whether the target range is empty.
Definition Matcher.hpp:487
constexpr auto lt(T &&value)
Tests, whether the target is less than the expected value.
Definition Matcher.hpp:246
constexpr auto le(T &&value)
Tests, whether the target is less than or equal to the expected value.
Definition Matcher.hpp:263
constexpr auto predicate(UnaryPredicate &&predicate, StringT description="passes predicate", StringT invertedDescription="fails predicate")
Tests, whether the target fulfills the given predicate.
Definition Matcher.hpp:317
constexpr WildcardMatcher _
The wildcard matcher, always matching.
Definition Matcher.hpp:203
constexpr auto eq(T &&value)
Tests, whether the target compares equal to the expected value.
Definition Matcher.hpp:212
constexpr auto gt(T &&value)
Tests, whether the target is greater than the expected value.
Definition Matcher.hpp:280
constexpr auto ge(T &&value)
Tests, whether the target is greater than or equal to the expected value.
Definition Matcher.hpp:297
constexpr auto ne(T &&value)
Tests, whether the target compares not equal to the expected value.
Definition Matcher.hpp:229
constexpr auto eq(std::basic_string< Char, Traits, Allocator > expected)
Tests, whether the target string compares equal to the expected string.
Definition Matcher.hpp:355
Definition Matcher.hpp:388
Definition Matcher.hpp:336
Definition Matcher.hpp:165
Definition Catch2.hpp:22
constexpr detail::PrintFn print
Definition Printer.hpp:266
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:37