73namespace sequoia::testing
75 enum class comparison_flavour { equality, inequality, less_than, greater_than, leq, geq, threeway };
77 template<comparison_flavour C>
78 using comparison_constant = std::integral_constant<comparison_flavour, C>;
80 using equality_type = comparison_constant<comparison_flavour::equality>;
81 using inequality_type = comparison_constant<comparison_flavour::inequality>;
82 using less_than_type = comparison_constant<comparison_flavour::less_than>;
83 using greater_than_type = comparison_constant<comparison_flavour::greater_than>;
84 using leq_type = comparison_constant<comparison_flavour::leq>;
85 using geq_type = comparison_constant<comparison_flavour::geq>;
86 using threeway_type = comparison_constant<comparison_flavour::threeway>;
89 std::string to_string(comparison_flavour f);
92 using optional_ref = std::optional<std::reference_wrapper<T>>;
94 template<test_mode Mode, std::equality_comparable T,
class U>
95 inline constexpr bool checkable_against_for_semantics{
96 checkable_against<with_best_available_check_t<minimal_reporting_permitted::yes>, Mode, T, U, tutor<null_advisor>>
99 template<test_mode Mode, std::equality_comparable T,
class U>
100 inline constexpr bool equivalence_checkable_for_semantics{
101 (!std::is_same_v<std::remove_cvref_t<T>, std::remove_cvref_t<U>>)
102 && checkable_against<with_best_available_check_t<minimal_reporting_permitted::no>, Mode, T, U, tutor<null_advisor>>
112 constexpr static bool value{
true};
117 template<
class T>
constexpr void operator()(
const T&)
const noexcept {}
123 template<std::equality_comparable T>
130 template<std::totally_ordered T>
138 std::weak_ordering order() const noexcept
143 ~auxiliary_data_policy() =
default;
145 std::weak_ordering m_Order;
154 template<
class Actions,
class... Args>
155 inline constexpr bool has_post_comparison_action
156 =
requires (std::remove_cvref_t<Actions> actions, std::remove_cvref_t<Args>&... args) {
157 actions.post_comparison_action(args...);
160 template<
class Actions,
class... Args>
161 inline constexpr bool has_post_move_action
162 =
requires (std::remove_cvref_t<Actions> actions, std::remove_cvref_t<Args>&... args) {
163 actions.post_move_action(args...);
166 template<
class Actions,
class... Args>
167 inline constexpr bool has_post_move_assign_action
168 =
requires (std::remove_cvref_t<Actions> actions, std::remove_cvref_t<Args>&... args) {
169 actions.post_move_assign_action(args...);
172 template<
class Actions,
class... Args>
173 inline constexpr bool has_post_swap_action
174 =
requires (std::remove_cvref_t<Actions> actions, std::remove_cvref_t<Args>&... args) {
175 actions.post_swap_action(args...);
178 template<
class Actions,
class... Args>
179 inline constexpr bool has_post_serialization_action
180 =
requires (std::remove_cvref_t<Actions> actions, std::remove_cvref_t<Args>&... args) {
181 actions.post_serialization_action(args...);
184 template<test_mode Mode,
class T,
class U>
185 requires equivalence_checkable_for_semantics<Mode, T, U>
186 void check_best_equivalence(test_logger<Mode>& logger,
const T& x,
const T& y,
const U& xEquivalent,
const U& yEquivalent)
188 using availability_t = with_best_available_check_t<minimal_reporting_permitted::no>;
189 check(availability_t{},
"x not equivalent to xEquivalent", logger, x, xEquivalent);
190 check(availability_t{},
"y not equivalent to yEquivalent", logger, y, yEquivalent);
195 template<test_mode Mode,
class Actions, pseudoregular T,
class... Args>
197 static bool check_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const Args&... args)
199 return check_equality_prerequisites(logger, actions, x, y, args...);
202 template<test_mode Mode,
class Actions, pseudoregular T,
class... Args>
203 requires std::totally_ordered<T>
205 static bool check_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const Args&... args)
207 return check_orderable_prerequisites(logger, actions, x, y, args...);
210 template<test_mode Mode,
class Actions, moveonly T,
class U,
class... Args>
212 static bool check_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const U& xEquivalent,
const U& yEquivalent,
const Args&... args)
214 return check_equality_prerequisites(logger, actions, x, y, xEquivalent, yEquivalent, args...);
217 template<test_mode Mode,
class Actions, moveonly T,
class U,
class... Args>
218 requires std::totally_ordered<T>
220 static bool check_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const U& xEquivalent,
const U& yEquivalent,
const Args&... args)
222 return check_orderable_prerequisites(logger, actions, x, y, xEquivalent, yEquivalent, args...);
225 template<test_mode Mode,
class Actions, moveonly T,
class... Args>
227 static bool check_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const Args&... args)
229 return check_equality_prerequisites(logger, actions, x, y, args...);
232 template<test_mode Mode,
class Actions, moveonly T,
class... Args>
233 requires std::totally_ordered<T>
235 static bool check_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const Args&... args)
237 return check_orderable_prerequisites(logger, actions, x, y, args...);
242 template<test_mode Mode, comparison_flavour C,
class Actions, movable_comparable T, invocable_r<
bool, T> Fn,
class... Args>
243 bool do_check_comparison_consistency(test_logger<Mode>& logger, comparison_constant<C> comparison, [[maybe_unused]]
const Actions& actions,
const T& x, std::string_view tag, Fn fn, [[maybe_unused]]
const Args&... args)
245 if(!
check(std::string{
"operator"}.append(to_string(comparison.value)).append(
" is inconsistent ").append(tag), logger, fn(x)))
248 if constexpr (has_post_comparison_action<Actions, test_logger<Mode>, comparison_constant<C>, T, std::string_view, Args...>)
250 if(!actions.post_comparison_action(logger, comparison, x, tag, args...))
257 template<test_mode Mode, comparison_flavour C,
class Actions, movable_comparable T, invocable_r<
bool, T> Fn>
258 bool check_comparison_consistency(test_logger<Mode>& logger, comparison_constant<C> comparison,
const Actions& actions,
const T& x,
const T& y, Fn fn)
260 sentinel sentry{logger,
""};
262 do_check_comparison_consistency(logger, comparison, actions, x,
"(x)", fn);
263 do_check_comparison_consistency(logger, comparison, actions, y,
"(y)", fn);
265 return !sentry.failure_detected();
268 template<test_mode Mode,
class Actions, std::totally_ordered T,
class... Args>
270 bool check_ordering_operators(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const Args&... args)
272 sentinel sentry{logger,
""};
274 check_comparison_consistency(logger, less_than_type{}, actions, x, y, [](
const T& x) {
return !(x < x); }, args...);
275 check_comparison_consistency(logger, leq_type{}, actions, x, y, [](
const T& x) {
return x <= x; }, args...);
276 check_comparison_consistency(logger, greater_than_type{}, actions, x, y, [](
const T& x) {
return !(x > x); }, args...);
277 check_comparison_consistency(logger, geq_type{}, actions, x, y, [](
const T& x) {
return x >= x; }, args...);
279 if constexpr (std::three_way_comparable<T>)
281 check_comparison_consistency(logger, threeway_type{}, actions, x, y, [](
const T& x) {
return (x <=> x) == 0; }, args...);
284 return !sentry.failure_detected();
287 template<test_mode Mode,
class Actions, std::totally_ordered T,
class... Args>
289 bool check_ordering_consistency(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const Args&... args)
291 if(!check_ordering_operators(logger, actions, x, y, args...))
return false;
294 [&logger](
const T& x,
const T& y){
295 sentinel sentry{logger,
""};
297 check(
"operator> and operator< are inconsistent", logger, y > x);
298 check(
"operator< and operator<= are inconsistent", logger, x <= y);
299 check(
"operator< and operator>= are inconsistent", logger, y >= x);
301 if constexpr (std::three_way_comparable<T>)
303 check(
"operator< and operator<=> are inconsistent", logger, (x <=> y) < 0);
306 return !sentry.failure_detected();
310 return x < y ? comp(x,y) : comp(y,x);
313 template<test_mode Mode,
class Actions, std::totally_ordered T,
class U,
class... Args>
315 bool check_ordering_consistency(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const U&,
const U&,
const Args&... args)
317 return check_ordering_consistency(logger, actions, x, y, args...);
320 template<test_mode Mode,
class Actions, std::equality_comparable T,
class... Args>
322 bool check_equality_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const Args&... args)
324 const bool eq{check_comparison_consistency(logger, equality_type{}, actions, x, y, [](
const T& x) {
return x == x; }, args...)};
325 const bool neq{check_comparison_consistency(logger, inequality_type{}, actions, x, y, [](
const T& x) {
return !(x != x); }, args...)};
327 return eq && neq &&
check(
"Prerequisite - for checking semantics, x and y are assumed to be different", logger, x != y);
330 template<test_mode Mode, std::equality_comparable T,
class U>
331 requires checkable_against_for_semantics<Mode, T, U>
332 bool check_against(std::string message, test_logger<Mode>& logger,
const T& x,
const U& xEquivalent)
334 return check(with_best_available_check_t<minimal_reporting_permitted::yes>{},
341 template<test_mode Mode,
class Actions, std::equality_comparable T,
class U,
class... Args>
343 bool check_equality_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const U& xEquivalent,
const U& yEquivalent,
const Args&... args)
345 if(!check_equality_prerequisites(logger, actions, x, y, args...))
349 [](std::string_view var) {
350 return std::format(
"Prerequisite: {0} and {0}Equivalent should be equivalent", var);
354 const bool xPassed{check_against(makeMessage(
"x"), logger, x, xEquivalent)},
355 yPassed{check_against(makeMessage(
"y"), logger, y, yEquivalent)};
357 return xPassed && yPassed;
360 template<test_mode Mode,
class Actions, std::totally_ordered T,
class... Args>
362 bool check_orderable_prerequisites(test_logger<Mode>& logger,
const Actions& actions,
const T& x,
const T& y,
const Args&... args)
364 if(check_equality_prerequisites(logger, actions, x, y, args...))
366 const auto order{actions.order()};
367 if(
check(
"Prerequisite - for checking semantics, order must be weak_ordering::less or weak_ordering::greater",
370 if(check_ordering_consistency(logger, actions, x, y, args...))
372 const bool cond{order < 0 ? x < y : x > y};
375 std::string mess{
"Prerequisite - for ordered semantics, it is assumed that "};
376 return order == 0 ? mess.append(
"x < y") : mess.append(
"y > x");
380 if constexpr(serializable<T>)
382 return check(mess(), logger, cond,
383 tutor{[](
const T& x,
const T& y) {
384 return prediction_message(to_string(x), to_string(y)); } });
388 return check(mess(), logger, cond);
399 template<test_mode Mode,
class Actions, movable_comparable T,
class U,
class V,
class... Args>
400 requires checkable_against_for_semantics<Mode, T, U> && checkable_against_for_semantics<Mode, T, V>
401 std::optional<T> do_check_move_construction(test_logger<Mode>& logger,
402 [[maybe_unused]]
const Actions& actions,
405 optional_ref<const V> movedFrom,
409 if(!check_against(
"Inconsistent move construction", logger, w, y))
412 if(movedFrom.has_value())
414 check_against(
"Incorrect moved-from value after move construction", logger, z, movedFrom.value().get());
417 if constexpr(has_post_move_action<Actions, test_logger<Mode>, T, Args...>)
419 actions.post_move_action(logger, w, args...);
425 template<test_mode Mode,
class Actions, movable_comparable T,
class U,
class V>
426 requires checkable_against_for_semantics<Mode, T, U>
427 std::optional<T> check_move_construction(test_logger<Mode>& logger,
428 const Actions& actions,
431 optional_ref<const V> movedFrom)
433 return do_check_move_construction(logger, actions, std::forward<T>(z), y, movedFrom);
438 template<test_mode Mode,
class Actions, movable_comparable T,
class U,
class V, std::invocable<T&> Mutator,
class... Args>
439 requires checkable_against_for_semantics<Mode, T, U> && checkable_against_for_semantics<Mode, T, V>
440 void do_check_move_assign(test_logger<Mode>& logger,
441 [[maybe_unused]]
const Actions& actions,
444 const U& yEquivalent,
445 optional_ref<const V> movedFrom,
446 [[maybe_unused]] Mutator&& yMutator,
450 if(!check_against(
"Inconsistent move assignment (from y)", logger, z, yEquivalent))
453 if(movedFrom.has_value())
455 check_against(
"Incorrect moved-from value after move assignment", logger, y, movedFrom.value().get());
458 if constexpr(has_post_move_assign_action<Actions, test_logger<Mode>, T, Mutator, Args...>)
460 actions.post_move_assign_action(logger, z, std::move(yMutator), args...);
464 template<test_mode Mode,
class Actions, movable_comparable T,
class U,
class V, std::invocable<T&> Mutator>
465 requires checkable_against_for_semantics<Mode, T, U> && checkable_against_for_semantics<Mode, T, V>
466 void check_move_assign(test_logger<Mode>& logger,
467 const Actions& actions,
470 const U& yEquivalent,
471 optional_ref<const V> movedFrom,
474 do_check_move_assign(logger, actions, z, std::forward<T>(y), yEquivalent, movedFrom, std::move(m));
479 template<test_mode Mode,
class Actions, movable_comparable T,
class U,
class... Args>
480 requires checkable_against_for_semantics<Mode, T, U>
481 bool do_check_swap(test_logger<Mode>& logger,
482 [[maybe_unused]]
const Actions& actions,
485 const U& xEquivalent,
486 const U& yEquivalent,
487 [[maybe_unused]]
const Args&... args)
489 std::ranges::swap(x, y);
492 check_against(
"Inconsistent Swap (y)", logger, y, xEquivalent)
496 check_against(
"Inconsistent Swap (x)", logger, x, yEquivalent)
501 if constexpr(has_post_swap_action<Actions, test_logger<Mode>, T, T, T, Args...>)
503 actions.post_swap_action(logger, x, y, yEquivalent, args...);
506 std::ranges::swap(y,y);
507 return check_against(
"Inconsistent Self Swap", logger, y, xEquivalent);
513 template<test_mode Mode,
class Actions, movable_comparable T,
class U>
514 requires checkable_against_for_semantics<Mode, T, U>
515 bool check_swap(test_logger<Mode>& logger,
const Actions& actions, T&& x, T&& y,
const U& xEquivalent,
const U& yEquivalent)
517 return do_check_swap(logger, actions, std::move(x), std::move(y), xEquivalent, yEquivalent);
520 template<test_mode Mode,
class Actions, pseudoregular T,
class U, std::invocable<T&> Mutator>
521 requires checkable_against_for_semantics<Mode, T, U>
522 bool check_swap(test_logger<Mode>& logger,
const Actions& actions, T&& x, T&& y,
const U& xEquivalent,
const U& yEquivalent, Mutator yMutator)
524 return do_check_swap(logger, actions, std::move(x), std::move(y), xEquivalent, yEquivalent, std::move(yMutator));
529 template<test_mode Mode,
class Actions, movable_comparable T,
class... Args>
530 requires (serializable_to<T, std::stringstream> && deserializable_from<T, std::stringstream>)
531 bool do_check_serialization(test_logger<Mode>& logger,
532 [[maybe_unused]]
const Actions& actions,
535 [[maybe_unused]]
const Args&... args)
537 std::stringstream s{};
540 if constexpr(has_post_serialization_action<Actions, test_logger<Mode>, T, Args...>)
542 actions.post_serialization_action(logger, y, args...);
547 return check_against(
"Inconsistent (de)serialization", logger, u, y);
550 template<test_mode Mode,
class Actions, movable_comparable T>
551 requires (serializable_to<T, std::stringstream> && deserializable_from<T, std::stringstream>)
552 bool check_serialization(test_logger<Mode>& logger,
const Actions& actions, T&& u,
const T& y)
554 return do_check_serialization(logger, actions, std::forward<T>(u), y);
Utilities for the advice framework, which provides hints for certain failures.
Concepts which are sufficiently general to appear in the sequoia namespace.
Free functions for performing checks, together with the 'checker' class template which wraps them.
bool check(CheckType flavour, std::string description, test_logger< Mode > &logger, Iter first, Sentinel last, PredictionIter predictionFirst, PredictionSentinel predictionLast, tutor< Advisor > advisor={})
The workhorse for comparing the contents of ranges.
Definition: FreeCheckers.hpp:377
Utilities for recording the outcome of tests.
Condition for applying a container check.
Definition: AllocationCheckersDetails.cpp:15
Definition: SemanticsCheckersDetails.hpp:121
Definition: SemanticsCheckersDetails.hpp:150
Definition: SemanticsCheckersDetails.hpp:111
Definition: SemanticsCheckersDetails.hpp:108
Definition: SemanticsCheckersDetails.hpp:116