72namespace sequoia::testing
85 template<
class ValueBasedCustomizer>
88 using customizer_type = ValueBasedCustomizer;
90 template<
class Customizer>
93 customizer_type customizer;
96 template<
class ValueBasedCustomizer>
97 requires std::is_void_v<ValueBasedCustomizer>
105 template<
class ValueBasedCustomizer>
108 using customizer_type = ValueBasedCustomizer;
110 template<
class Customizer>
113 customizer_type customizer;
116 template<
class ValueBasedCustomizer>
117 requires std::is_void_v<ValueBasedCustomizer>
125 enum class minimal_reporting_permitted :
bool { no, yes };
127 template<minimal_reporting_permitted MinimalReporting>
130 template<
class ValueBasedCustomizer>
134 return "equivalence";
137 template<
class ValueBasedCustomizer>
139 std::string to_string(general_weak_equivalence_check_t<ValueBasedCustomizer>)
141 return "weak equivalence";
146 inline constexpr equality_check_t equality{};
147 inline constexpr simple_equality_check_t simple_equality{};
148 inline constexpr equivalence_check_t equivalence{};
149 inline constexpr weak_equivalence_check_t weak_equivalence{};
150 inline constexpr with_best_available_check_t<minimal_reporting_permitted::no> with_best_available{};
153 inline constexpr bool is_elementary_check{
154 std::is_same_v<T, equality_check_t>
155 || std::is_same_v<T, simple_equality_check_t>
156 || std::is_same_v<T, equivalence_check_t>
157 || std::is_same_v<T, weak_equivalence_check_t>
161 inline constexpr bool is_best_available_check{
162 std::is_same_v<T, with_best_available_check_t<minimal_reporting_permitted::yes>>
163 || std::is_same_v<T, with_best_available_check_t<minimal_reporting_permitted::no>>
167 inline constexpr bool is_customized_check{
169 typename T::customizer_type;
170 typename T::template rebind_check_type<typename T::customizer_type>;
171 requires std::is_same_v<typename T::template rebind_check_type<typename T::customizer_type>, T>;
176 inline constexpr bool is_general_equivalence_check{is_customized_check<T> || std::is_same_v<T, equivalence_check_t> || std::is_same_v<T, weak_equivalence_check_t>};
178 template<
class Compare,
class T>
179 inline constexpr bool potential_comparator_for{
180 std::is_invocable_r_v<bool, Compare, T, T>
181 || (faithful_range<T> && !(is_elementary_check<Compare> || is_best_available_check<Compare> || is_customized_check<Compare>))
184 template<
class CheckType,
test_mode Mode,
class T,
class U,
class... Args>
185 concept tests_against =
requires(CheckType c, test_logger<Mode>& logger, T&& obtained,
const U& predicted, Args&&... args) {
186 value_tester<std::remove_cvref_t<T>>::test(c, logger, std::forward<T>(obtained), predicted, std::forward<Args>(args)...);
189 template<
class CheckType, test_mode Mode,
class T,
class U,
class Tutor>
192 template<
class CheckType, test_mode Mode,
class T,
class U,
class Tutor>
193 concept checkable_against =
requires(CheckType c, test_logger<Mode>& logger, T&& obtained,
const U& predicted, Tutor tutor) {
194 check(c,
"", logger, std::forward<T>(obtained), predicted, tutor);
197 template<
class CheckType, test_mode Mode,
class T,
class U,
class Tutor>
198 inline constexpr bool checkable_against_fallback{
200 typename CheckType::fallback;
205 template<test_mode Mode,
class T,
class U,
class Tutor>
206 inline constexpr bool has_elementary_check_against{
207 checkable_against<equality_check_t, Mode, T, U, Tutor>
208 || checkable_against<simple_equality_check_t, Mode, T, U, Tutor>
209 || checkable_against<equivalence_check_t, Mode, T, U, Tutor>
210 || checkable_against<weak_equivalence_check_t, Mode, T, U, Tutor>
220 requires std::derived_from<E, std::exception>
224 static std::string get(
const E& e) {
return e.what(); }
232 static std::string get(
const E& e) {
return to_string(e); }
235 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor>
236 requires tests_against_with_or_without_tutor<CheckType, Mode, T, U, tutor<Advisor>>
237 void select_test(CheckType flavour, test_logger<Mode>& logger,
const T& obtained,
const U& prediction, [[maybe_unused]] tutor<Advisor> advisor)
239 if constexpr(tests_against<CheckType, Mode, T, U, tutor<Advisor>>)
241 value_tester<T>::test(flavour, logger, obtained, prediction, advisor);
243 else if constexpr(tests_against<CheckType, Mode, T, U>)
245 value_tester<T>::test(flavour, logger, obtained, prediction);
249 static_assert(dependent_false<CheckType>::value,
"Should never be triggered; indicates mismatch between requirement and logic");
258 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor>
259 requires is_general_equivalence_check<CheckType> && tests_against_with_or_without_tutor<CheckType, Mode, T, U, tutor<Advisor>>
261 std::string description,
268 [flavour] (std::string desc) -> std::string {
271 "Comparison performed using:",
273 std::format(
"Checking for {} with:", to_string(flavour)), make_type_info<U>()
280 select_test(flavour, logger, obtained, predicted, advisor);
282 return !sentry.failure_detected();
285 template<
bool IsFinalMessage, test_mode Mode,
class Compare,
class T,
class Advisor>
286 requires std::is_invocable_r_v<bool, Compare, T, T> && (!IsFinalMessage || reportable<T>)
287 void binary_comparison(final_message_constant<IsFinalMessage>, sentinel<Mode>& sentry, Compare compare,
const T& obtained,
const T& prediction, tutor<Advisor> advisor)
290 if(!compare(obtained, prediction))
292 std::string message{failure_reporter<Compare>::reporter(final_message_constant<IsFinalMessage>{}, compare, obtained, prediction)};
293 append_advice(message, {advisor, obtained, prediction});
295 sentry.log_failure(message);
302 std::string operator()(
const project_paths& projPaths, std::string message)
const
304 constexpr auto npos{std::string::npos};
305 if(
const auto pos{message.find(projPaths.project_root().generic_string())}; pos < npos)
307 const auto len{projPaths.project_root().generic_string().size()};
308 message.erase(pos, len + 1);
324 bool check_exception_thrown(std::string description,
test_logger<Mode>& logger, Fn&& function,
const project_paths& projPaths, Postprocessor postprocessor={})
327 [&description]() -> std::string&& {
328 return std::move(append_lines(description,
"Expected Exception Type:", make_type_info<E>()));
332 sentinel<Mode> sentry{logger, message};
336 std::forward<Fn>(function)();
337 sentry.log_failure(
"No exception thrown");
342 sentry.log_caught_exception_message(postprocessor(projPaths, exception_message_extractor<E>::get(e)));
345 catch(
const std::exception& e)
347 std::string msg{append_lines(
"Unexpected exception thrown (caught by std::exception&):",
"\"").append(e.what()).append(
"\"\n")};
349 sentry.log_failure(msg);
354 sentry.log_failure(
"Unknown exception thrown\n");
361 template<
class CheckType, test_mode Mode, std::input_or_output_iterator Iter, std::input_or_output_iterator PredictionIter,
class Advisor>
372 std::input_or_output_iterator Iter,
373 std::sentinel_for<Iter> Sentinel,
374 std::input_or_output_iterator PredictionIter,
375 std::sentinel_for<PredictionIter> PredictionSentinel,
376 class Advisor = null_advisor
378 requires supports_iterator_range_check<CheckType, Mode, Iter, PredictionIter, Advisor>
380 std::string description,
384 PredictionIter predictionFirst,
385 PredictionSentinel predictionLast,
389 [&description]() -> std::string&& {
390 return description.empty() || description.back() ==
'\n' ? std::move(description) : std::move(description.append(
"\n"));
394 sentinel<Mode> sentry{logger, info()};
397 const auto actualSize{fixed_width_unsigned_cast(std::ranges::distance(first, last))};
398 const auto predictedSize{fixed_width_unsigned_cast(std::ranges::distance(predictionFirst, predictionLast))};
399 if(
check(equality,
"Container size wrong", logger, actualSize, predictedSize))
401 auto predictionIter{predictionFirst};
403 for(; predictionIter != predictionLast; std::ranges::advance(predictionIter, 1), std::ranges::advance(iter, 1))
405 const auto dist{std::ranges::distance(predictionFirst, predictionIter)};
406 auto mess{std::format(
"Element {} of range incorrect", dist)};
407 if(!
check(flavour, std::move(mess), logger, *iter, *predictionIter, advisor)) equal =
false;
423 template<
class Compare, test_mode Mode,
class T,
class Advisor=null_advisor>
424 requires potential_comparator_for<Compare, T>
426 std::string description,
432 sentinel<Mode> sentry{logger, add_type_info<T>(std::move(description))};
434 if constexpr(std::is_invocable_r_v<bool, Compare, T, T>)
436 using finality = final_message_constant<!faithful_range<T>>;
437 binary_comparison(finality{}, sentry, std::move(compare), obtained, prediction, advisor);
441 check(std::move(compare),
"", logger, obtained.begin(), obtained.end(), prediction.begin(), prediction.end(), advisor);
444 return !sentry.failure_detected();
455 template<
class CheckType, test_mode Mode, faithful_range T, faithful_range U,
class Advisor>
456 inline constexpr bool supports_range_check_v{
457 requires(T& t, U& u) {
463 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor>
466 template<
class CheckType, test_mode Mode, faithful_range T, faithful_range U,
class Advisor>
467 requires impl::supports_range_check_v<CheckType, Mode, T, U, Advisor>
470 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor>
475 template<test_mode Mode,
class T,
class Advisor>
479 || supports_range_check_v<equality_check_t, Mode, T, T, Advisor>
500 template<test_mode Mode,
class T,
class Advisor=null_advisor>
501 requires supports_equality_check<Mode, T, Advisor>
503 std::string description,
509 sentinel<Mode> sentry{logger, add_type_info<T>(std::move(description))};
511 if constexpr(deep_equality_comparable<T>)
513 using finality = final_message_constant<!(tests_against_with_or_without_tutor<equality_check_t, Mode, T, T, tutor<Advisor>> || faithful_range<T>)>;
514 binary_comparison(finality{}, sentry, std::ranges::equal_to{}, obtained, prediction, advisor);
517 if constexpr(tests_against_with_or_without_tutor<equality_check_t, Mode, T, T, tutor<Advisor>>)
519 select_test(equality_check_t{}, logger, obtained, prediction, advisor);
521 else if constexpr(supports_range_check_v<equality_check_t, Mode, T, T, Advisor>)
523 check(equality,
"", logger, std::begin(obtained), std::end(obtained), std::begin(prediction), std::end(prediction), advisor);
526 return !sentry.failure_detected();
531 template<test_mode Mode,
class T,
class Advisor>
534 || supports_range_check_v<simple_equality_check_t, Mode, T, T, Advisor>
539 template<test_mode Mode,
class T,
class Advisor=null_advisor>
540 requires supports_simple_equality_check<Mode, T, Advisor>
542 std::string description,
548 sentinel<Mode> sentry{logger, add_type_info<T>(std::move(description))};
550 using finality = final_message_constant<!faithful_range<T>>;
551 binary_comparison(finality{}, sentry, std::ranges::equal_to{}, obtained, prediction, advisor);
553 if constexpr(supports_range_check_v<simple_equality_check_t, Mode, T, T, Advisor>)
555 check(simple_equality,
"", logger, std::begin(obtained), std::end(obtained), std::begin(prediction), std::end(prediction), advisor);
558 return !sentry.failure_detected();
575 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor>
577 is_general_equivalence_check<CheckType>
579 || supports_range_check_v<CheckType, Mode, T, U, Advisor>
580 || checkable_against_fallback<CheckType, Mode, T, U, tutor<Advisor>>)
583 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor=null_advisor>
584 requires supports_generalized_equivalence_check<CheckType, Mode, T, U, Advisor>
585 bool check(CheckType flavour, std::string description, test_logger<Mode>& logger,
const T& obtained,
const U& prediction, tutor<Advisor> advisor={})
587 if constexpr(tests_against_with_or_without_tutor<CheckType, Mode, T, U, tutor<Advisor>>)
589 return general_equivalence_check(flavour,
590 std::move(description),
596 else if constexpr(supports_range_check_v<CheckType, Mode, T, U, Advisor>)
598 return check(flavour,
599 add_type_info<T>(std::move(description)),
601 std::begin(obtained),
603 std::begin(prediction),
604 std::end(prediction),
607 else if constexpr(checkable_against_fallback<CheckType, Mode, T, U, tutor<Advisor>>)
609 using fallback =
typename CheckType::fallback;
610 return check(fallback{},
611 std::move(description),
619 static_assert(dependent_false<CheckType>::value,
"Should never be triggered; indicates mismatch between requirement and logic");
625 template<minimal_reporting_permitted MinimalReporting, test_mode Mode,
class T,
class U,
class Advisor>
628 || has_elementary_check_against<Mode, T, U, tutor<Advisor>>
629 || supports_range_check_v<with_best_available_check_t<MinimalReporting>, Mode, T, U, Advisor>
631 || ((MinimalReporting == minimal_reporting_permitted::yes) && std::equality_comparable_with<T, U>)
636 template<minimal_reporting_permitted MinimalReporting, test_mode Mode,
class T,
class U,
class Advisor=null_advisor>
637 requires supports_best_available_check<MinimalReporting, Mode, T, U, Advisor>
639 std::string description,
645 if constexpr(tests_against_with_or_without_tutor<with_best_available_check_t<MinimalReporting>, Mode, T, U, tutor<Advisor>>)
647 sentinel<Mode> sentry{logger, add_type_info<T>(std::move(description))};
649 if constexpr(std::is_same_v<T, U> && deep_equality_comparable<T>)
651 binary_comparison(is_not_final_message_t{}, sentry, std::ranges::equal_to{}, obtained, prediction, advisor);
654 select_test(with_best_available, logger, obtained, prediction, advisor);
656 return !sentry.failure_detected();
658 else if constexpr(std::is_same_v<T, U> && tests_against_with_or_without_tutor<equality_check_t, Mode, T, U, tutor<Advisor>>)
660 return check(equality, description, logger, obtained, prediction, advisor);
662 else if constexpr(tests_against_with_or_without_tutor<equivalence_check_t, Mode, T, U, tutor<Advisor>>)
664 return check(equivalence, description, logger, obtained, prediction, advisor);
666 else if constexpr(tests_against_with_or_without_tutor<weak_equivalence_check_t, Mode, T, U, tutor<Advisor>>)
668 return check(weak_equivalence, description, logger, obtained, prediction, advisor);
670 else if constexpr(supports_range_check_v<with_best_available_check_t<MinimalReporting>, Mode, T, U, Advisor>)
672 return check(with_best_available, description, logger, std::begin(obtained), std::end(obtained), std::begin(prediction), std::end(prediction), advisor);
674 else if constexpr(std::is_same_v<T, U> && deep_equality_comparable<T> && reportable<T>)
676 return check(simple_equality, description, logger, obtained, prediction, advisor);
678 else if constexpr((MinimalReporting == minimal_reporting_permitted::yes) && std::equality_comparable_with<T, U>)
680 return check(description, logger, obtained == prediction,advisor);
684 static_assert(
false);
688 template<test_mode Mode,
class Advisor=null_advisor>
689 bool check(std::string description, test_logger<Mode>& logger,
const bool obtained, tutor<Advisor> advisor={})
691 return check(equality, std::move(description), logger, obtained,
true, std::move(advisor));
708 template<test_mode Mode,
class Extender>
721 template<
class T,
class Advisor = null_advisor,
class Self>
722 requires supports_equality_check<Mode, T, Advisor>
725 return testing::check(equality, self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
728 template<
class T,
class Advisor = null_advisor,
class Self>
729 requires supports_simple_equality_check<Mode, T, Advisor>
732 return testing::check(simple_equality, self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
735 template<
class T,
class U, minimal_reporting_permitted MinimalReporting,
class Advisor = null_advisor,
class Self>
736 requires supports_best_available_check<MinimalReporting, Mode, T, U, Advisor>
740 self.report(description),
747 template<
class ValueBasedCustomizer,
class T,
class U,
class Advisor = null_advisor,
class Self>
748 requires supports_generalized_equivalence_check<general_equivalence_check_t<ValueBasedCustomizer>, Mode, T, U, Advisor>
751 return testing::check(
checker, self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
754 template<
class ValueBasedCustomizer,
class T,
class U,
class Advisor = null_advisor,
class Self>
755 requires supports_generalized_equivalence_check<general_weak_equivalence_check_t<ValueBasedCustomizer>, Mode, T, U, Advisor>
758 return testing::check(
checker, self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
761 template<
class Compare,
class T,
class Advisor = null_advisor,
class Self>
762 requires potential_comparator_for<Compare, T>
763 bool check(
this Self& self, Compare compare,
const reporter& description,
const T& obtained,
const T& prediction,
tutor<Advisor> advisor = {})
765 return testing::check(std::move(compare), self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
768 template<
class Advisor=null_advisor,
class Self>
771 return testing::check(self.report(description), self.m_Logger, obtained, std::move(advisor));
777 std::input_or_output_iterator Iter,
778 std::sentinel_for<Iter> Sentinel,
779 std::input_or_output_iterator PredictionIter,
780 std::sentinel_for<PredictionIter> PredictionSentinel,
784 requires supports_iterator_range_check<Compare, Mode, Iter, PredictionIter, Advisor>
785 bool check(
this Self& self,
790 PredictionIter predictionFirst,
791 PredictionSentinel predictionLast,
794 return testing::check(std::move(compare), self.report(description), self.m_Logger, first, last, predictionFirst, predictionLast, std::move(advisor));
804 bool check_exception_thrown(
this Self& self,
const reporter& description, Fn&& function, Postprocessor postprocessor={})
806 return testing::check_exception_thrown<E>(self.report(description), self.m_Logger, std::forward<Fn>(function), self.get_project_paths(), std::move(postprocessor));
809#define STATIC_CHECK(...) (check("", [&](){ static_assert(__VA_ARGS__); return true; }()))
811 template<
class Stream>
813 friend Stream& operator<<(Stream& os,
const checker& c)
820 log_summary summary(std::string_view prefix,
const log_summary::duration delta)
const
825 void reset_results()
noexcept { m_Logger.reset_results(); }
828 bool has_critical_failures()
const noexcept
830 return m_Logger.results().critical_failures > 0;
834 : m_Logger{std::move(recovery)}
843 std::size_t checks()
const noexcept {
return m_Logger.checks(); }
846 std::size_t failures()
const noexcept {
return m_Logger.failures(); }
851 return m_Logger.exceptions_detected_by_sentinel();
857 return {m_Logger, std::move(message)};
861 std::string_view top_level_message()
const
863 return m_Logger.top_level_message();
867 const failure_output& failure_messages()
const noexcept
869 return m_Logger.results().failure_messages;
Utilities for the advice framework, which provides hints for certain failures.
Utilities for performing checks with respect to a binary operator.
Core declarations / definitions used in the testing framework.
constexpr bool supports_simple_equality_check
Condition for applying an equality check.
Definition: FreeCheckers.hpp:532
constexpr bool supports_generalized_equivalence_check
The workhorse for (weak) equivalence checking.
Definition: FreeCheckers.hpp:576
constexpr bool supports_best_available_check
Condition for applying the best available check.
Definition: FreeCheckers.hpp:626
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:379
constexpr bool supports_equality_check
Condition for applying an equality check.
Definition: FreeCheckers.hpp:476
constexpr bool supports_iterator_range_check
Condition for applying a check across a range of values.
Definition: FreeCheckers.hpp:362
bool general_equivalence_check(CheckType flavour, std::string description, test_logger< Mode > &logger, const T &obtained, const U &predicted, tutor< Advisor > advisor)
generic function that generates a check from any class providing a static check method.
Definition: FreeCheckers.hpp:260
Utilities for recording the outcome of tests.
test_mode
Specifies whether tests are run as standard tests or in false postive/negative mode.
Definition: TestMode.hpp:20
Meta-programming utilities.
Exposes elementary check methods, with the option to plug in arbitrary Extenders to compose functiona...
Definition: FreeCheckers.hpp:710
Summaries data generated by the logger, for the purposes of reporting.
Definition: TestLogger.hpp:299
Paths used by the project.
Definition: ProjectPaths.hpp:467
Definition: Output.hpp:176
Definition: TestLogger.hpp:277
Definition: TestLogger.hpp:183
class template used to wrap function objects which proffer advice.
Definition: Advice.hpp:127
Concept to work around the fact that currently the stl typically underconstrains operator==.
Definition: Concepts.hpp:105
Supplements std::invocable.
Definition: Concepts.hpp:24
A concept which is realized by a T const& which may be serialized to a Stream&.
Definition: Concepts.hpp:61
Definition: FreeCheckers.hpp:193
Definition: Output.hpp:140
Definition: CoreInfrastructure.hpp:54
Definition: FreeCheckers.hpp:190
Definition: FreeCheckers.hpp:185
Holds paths to files where recovery information will be written if the path is not empty.
Definition: TestLogger.hpp:36
Definition: FreeCheckers.hpp:300
Definition: FreeCheckers.hpp:82
Definition: FreeCheckers.hpp:99
Definition: FreeCheckers.hpp:87
Definition: FreeCheckers.hpp:107
Represents the absence of advice.
Definition: Advice.hpp:93
Definition: FreeCheckers.hpp:83
Definition: FreeCheckers.hpp:464
Definition: CoreInfrastructure.hpp:99
class template, specializations of which implement various comparisons for the specified type.
Definition: FreeCheckers.hpp:78
Definition: FreeCheckers.hpp:128