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 [] (CheckType flavour, std::string desc) -> std::string {
269 return append_lines(desc,
270 "Comparison performed using:",
272 std::format(
"Checking for {} with:", to_string(flavour)), make_type_info<U>()).append(
"\n");
276 sentinel<Mode> sentry{logger, msg(flavour, std::move(description))};
278 select_test(flavour, logger, obtained, predicted, advisor);
280 return !sentry.failure_detected();
283 template<
bool IsFinalMessage, test_mode Mode,
class Compare,
class T,
class Advisor>
284 requires std::is_invocable_r_v<bool, Compare, T, T> && (!IsFinalMessage || reportable<T>)
285 void binary_comparison(final_message_constant<IsFinalMessage>, sentinel<Mode>& sentry, Compare compare,
const T& obtained,
const T& prediction, tutor<Advisor> advisor)
288 if(!compare(obtained, prediction))
290 std::string message{failure_reporter<Compare>::reporter(final_message_constant<IsFinalMessage>{}, compare, obtained, prediction)};
291 append_advice(message, {advisor, obtained, prediction});
293 sentry.log_failure(message);
300 std::string operator()(
const project_paths& projPaths, std::string message)
const
302 constexpr auto npos{std::string::npos};
303 if(
const auto pos{message.find(projPaths.project_root().generic_string())}; pos < npos)
305 const auto len{projPaths.project_root().generic_string().size()};
306 message.erase(pos, len + 1);
322 bool check_exception_thrown(std::string description,
test_logger<Mode>& logger, Fn&& function,
const project_paths& projPaths, Postprocessor postprocessor={})
325 [&description]() -> std::string&& {
326 return std::move(append_lines(description,
"Expected Exception Type:", make_type_info<E>()));
330 sentinel<Mode> sentry{logger, message};
334 std::forward<Fn>(function)();
335 sentry.log_failure(
"No exception thrown");
340 sentry.log_caught_exception_message(postprocessor(projPaths, exception_message_extractor<E>::get(e)));
343 catch(
const std::exception& e)
345 std::string msg{append_lines(
"Unexpected exception thrown (caught by std::exception&):",
"\"").append(e.what()).append(
"\"\n")};
347 sentry.log_failure(msg);
352 sentry.log_failure(
"Unknown exception thrown\n");
359 template<
class CheckType, test_mode Mode, std::input_or_output_iterator Iter, std::input_or_output_iterator PredictionIter,
class Advisor>
370 std::input_or_output_iterator Iter,
371 std::sentinel_for<Iter> Sentinel,
372 std::input_or_output_iterator PredictionIter,
373 std::sentinel_for<PredictionIter> PredictionSentinel,
374 class Advisor = null_advisor
376 requires supports_iterator_range_check<CheckType, Mode, Iter, PredictionIter, Advisor>
378 std::string description,
382 PredictionIter predictionFirst,
383 PredictionSentinel predictionLast,
387 [&description]() -> std::string&& {
388 return description.empty() || description.back() ==
'\n' ? std::move(description) : std::move(description.append(
"\n"));
392 sentinel<Mode> sentry{logger, info()};
395 const auto actualSize{fixed_width_unsigned_cast(std::ranges::distance(first, last))};
396 const auto predictedSize{fixed_width_unsigned_cast(std::ranges::distance(predictionFirst, predictionLast))};
397 if(
check(equality,
"Container size wrong", logger, actualSize, predictedSize))
399 auto predictionIter{predictionFirst};
401 for(; predictionIter != predictionLast; std::ranges::advance(predictionIter, 1), std::ranges::advance(iter, 1))
403 const auto dist{std::ranges::distance(predictionFirst, predictionIter)};
404 auto mess{std::format(
"Element {} of range incorrect", dist)};
405 if(!
check(flavour, std::move(mess), logger, *iter, *predictionIter, advisor)) equal =
false;
421 template<
class Compare, test_mode Mode,
class T,
class Advisor=null_advisor>
422 requires potential_comparator_for<Compare, T>
424 std::string description,
430 sentinel<Mode> sentry{logger, add_type_info<T>(std::move(description))};
432 if constexpr(std::is_invocable_r_v<bool, Compare, T, T>)
434 using finality = final_message_constant<!faithful_range<T>>;
435 binary_comparison(finality{}, sentry, std::move(compare), obtained, prediction, advisor);
439 check(std::move(compare),
"", logger, obtained.begin(), obtained.end(), prediction.begin(), prediction.end(), advisor);
442 return !sentry.failure_detected();
453 template<
class CheckType, test_mode Mode, faithful_range T, faithful_range U,
class Advisor>
454 inline constexpr bool supports_range_check_v{
455 requires(T& t, U& u) {
461 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor>
464 template<
class CheckType, test_mode Mode, faithful_range T, faithful_range U,
class Advisor>
465 requires impl::supports_range_check_v<CheckType, Mode, T, U, Advisor>
468 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor>
473 template<test_mode Mode,
class T,
class Advisor>
477 || supports_range_check_v<equality_check_t, Mode, T, T, Advisor>
498 template<test_mode Mode,
class T,
class Advisor=null_advisor>
499 requires supports_equality_check<Mode, T, Advisor>
501 std::string description,
507 sentinel<Mode> sentry{logger, add_type_info<T>(std::move(description))};
509 if constexpr(deep_equality_comparable<T>)
511 using finality = final_message_constant<!(tests_against_with_or_without_tutor<equality_check_t, Mode, T, T, tutor<Advisor>> || faithful_range<T>)>;
512 binary_comparison(finality{}, sentry, std::ranges::equal_to{}, obtained, prediction, advisor);
515 if constexpr(tests_against_with_or_without_tutor<equality_check_t, Mode, T, T, tutor<Advisor>>)
517 select_test(equality_check_t{}, logger, obtained, prediction, advisor);
519 else if constexpr(supports_range_check_v<equality_check_t, Mode, T, T, Advisor>)
521 check(equality,
"", logger, std::begin(obtained), std::end(obtained), std::begin(prediction), std::end(prediction), advisor);
524 return !sentry.failure_detected();
529 template<test_mode Mode,
class T,
class Advisor>
532 || supports_range_check_v<simple_equality_check_t, Mode, T, T, Advisor>
537 template<test_mode Mode,
class T,
class Advisor=null_advisor>
538 requires supports_simple_equality_check<Mode, T, Advisor>
540 std::string description,
546 sentinel<Mode> sentry{logger, add_type_info<T>(std::move(description))};
548 using finality = final_message_constant<!faithful_range<T>>;
549 binary_comparison(finality{}, sentry, std::ranges::equal_to{}, obtained, prediction, advisor);
551 if constexpr(supports_range_check_v<simple_equality_check_t, Mode, T, T, Advisor>)
553 check(simple_equality,
"", logger, std::begin(obtained), std::end(obtained), std::begin(prediction), std::end(prediction), advisor);
556 return !sentry.failure_detected();
573 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor>
575 is_general_equivalence_check<CheckType>
577 || supports_range_check_v<CheckType, Mode, T, U, Advisor>
578 || checkable_against_fallback<CheckType, Mode, T, U, tutor<Advisor>>)
581 template<
class CheckType, test_mode Mode,
class T,
class U,
class Advisor=null_advisor>
582 requires supports_generalized_equivalence_check<CheckType, Mode, T, U, Advisor>
583 bool check(CheckType flavour, std::string description, test_logger<Mode>& logger,
const T& obtained,
const U& prediction, tutor<Advisor> advisor={})
585 if constexpr(tests_against_with_or_without_tutor<CheckType, Mode, T, U, tutor<Advisor>>)
587 return general_equivalence_check(flavour,
588 std::move(description),
594 else if constexpr(supports_range_check_v<CheckType, Mode, T, U, Advisor>)
596 return check(flavour,
597 add_type_info<T>(std::move(description)),
599 std::begin(obtained),
601 std::begin(prediction),
602 std::end(prediction),
605 else if constexpr(checkable_against_fallback<CheckType, Mode, T, U, tutor<Advisor>>)
607 using fallback =
typename CheckType::fallback;
608 return check(fallback{},
609 std::move(description),
617 static_assert(dependent_false<CheckType>::value,
"Should never be triggered; indicates mismatch between requirement and logic");
623 template<minimal_reporting_permitted MinimalReporting, test_mode Mode,
class T,
class U,
class Advisor>
626 || has_elementary_check_against<Mode, T, U, tutor<Advisor>>
627 || supports_range_check_v<with_best_available_check_t<MinimalReporting>, Mode, T, U, Advisor>
629 || ((MinimalReporting == minimal_reporting_permitted::yes) && std::equality_comparable_with<T, U>)
634 template<minimal_reporting_permitted MinimalReporting, test_mode Mode,
class T,
class U,
class Advisor=null_advisor>
635 requires supports_best_available_check<MinimalReporting, Mode, T, U, Advisor>
637 std::string description,
643 if constexpr(tests_against_with_or_without_tutor<with_best_available_check_t<MinimalReporting>, Mode, T, U, tutor<Advisor>>)
645 sentinel<Mode> sentry{logger, add_type_info<T>(std::move(description))};
647 if constexpr(std::is_same_v<T, U> && deep_equality_comparable<T>)
649 binary_comparison(is_not_final_message_t{}, sentry, std::ranges::equal_to{}, obtained, prediction, advisor);
652 select_test(with_best_available, logger, obtained, prediction, advisor);
654 return !sentry.failure_detected();
656 else if constexpr(std::is_same_v<T, U> && tests_against_with_or_without_tutor<equality_check_t, Mode, T, U, tutor<Advisor>>)
658 return check(equality, description, logger, obtained, prediction, advisor);
660 else if constexpr(tests_against_with_or_without_tutor<equivalence_check_t, Mode, T, U, tutor<Advisor>>)
662 return check(equivalence, description, logger, obtained, prediction, advisor);
664 else if constexpr(tests_against_with_or_without_tutor<weak_equivalence_check_t, Mode, T, U, tutor<Advisor>>)
666 return check(weak_equivalence, description, logger, obtained, prediction, advisor);
668 else if constexpr(supports_range_check_v<with_best_available_check_t<MinimalReporting>, Mode, T, U, Advisor>)
670 return check(with_best_available, description, logger, std::begin(obtained), std::end(obtained), std::begin(prediction), std::end(prediction), advisor);
672 else if constexpr(std::is_same_v<T, U> && deep_equality_comparable<T> && reportable<T>)
674 return check(simple_equality, description, logger, obtained, prediction, advisor);
676 else if constexpr((MinimalReporting == minimal_reporting_permitted::yes) && std::equality_comparable_with<T, U>)
678 return check(description, logger, obtained == prediction,advisor);
682 static_assert(
false);
686 template<test_mode Mode,
class Advisor=null_advisor>
687 bool check(std::string description, test_logger<Mode>& logger,
const bool obtained, tutor<Advisor> advisor={})
689 return check(equality, std::move(description), logger, obtained,
true, std::move(advisor));
706 template<test_mode Mode,
class Extender>
711 constexpr static test_mode mode{Mode};
719 template<
class T,
class Advisor = null_advisor,
class Self>
720 requires supports_equality_check<Mode, T, Advisor>
723 return testing::check(equality, self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
726 template<
class T,
class Advisor = null_advisor,
class Self>
727 requires supports_simple_equality_check<Mode, T, Advisor>
730 return testing::check(simple_equality, self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
733 template<
class T,
class U, minimal_reporting_permitted MinimalReporting,
class Advisor = null_advisor,
class Self>
734 requires supports_best_available_check<MinimalReporting, Mode, T, U, Advisor>
738 self.report(description),
745 template<
class ValueBasedCustomizer,
class T,
class U,
class Advisor = null_advisor,
class Self>
746 requires supports_generalized_equivalence_check<general_equivalence_check_t<ValueBasedCustomizer>, Mode, T, U, Advisor>
749 return testing::check(
checker, self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
752 template<
class ValueBasedCustomizer,
class T,
class U,
class Advisor = null_advisor,
class Self>
753 requires supports_generalized_equivalence_check<general_weak_equivalence_check_t<ValueBasedCustomizer>, Mode, T, U, Advisor>
756 return testing::check(
checker, self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
759 template<
class Compare,
class T,
class Advisor = null_advisor,
class Self>
760 requires potential_comparator_for<Compare, T>
761 bool check(
this Self& self, Compare compare,
const reporter& description,
const T& obtained,
const T& prediction,
tutor<Advisor> advisor = {})
763 return testing::check(std::move(compare), self.report(description), self.m_Logger, obtained, prediction, std::move(advisor));
766 template<
class Advisor=null_advisor,
class Self>
769 return testing::check(self.report(description), self.m_Logger, obtained, std::move(advisor));
775 std::input_or_output_iterator Iter,
776 std::sentinel_for<Iter> Sentinel,
777 std::input_or_output_iterator PredictionIter,
778 std::sentinel_for<PredictionIter> PredictionSentinel,
782 requires supports_iterator_range_check<Compare, Mode, Iter, PredictionIter, Advisor>
783 bool check(
this Self& self,
788 PredictionIter predictionFirst,
789 PredictionSentinel predictionLast,
792 return testing::check(std::move(compare), self.report(description), self.m_Logger, first, last, predictionFirst, predictionLast, std::move(advisor));
802 bool check_exception_thrown(
this Self& self,
const reporter& description, Fn&& function, Postprocessor postprocessor={})
804 return testing::check_exception_thrown<E>(self.report(description), self.m_Logger, std::forward<Fn>(function), self.get_project_paths(), std::move(postprocessor));
807#define STATIC_CHECK(...) (check("", [](){ static_assert(__VA_ARGS__); return true; }()))
809 template<
class Stream>
811 friend Stream& operator<<(Stream& os,
const checker& c)
818 log_summary summary(std::string_view prefix,
const log_summary::duration delta)
const
823 void reset_results()
noexcept { m_Logger.reset_results(); }
826 bool has_critical_failures()
const noexcept
828 return m_Logger.results().critical_failures > 0;
832 : m_Logger{std::move(recovery)}
841 std::size_t checks()
const noexcept {
return m_Logger.checks(); }
844 std::size_t failures()
const noexcept {
return m_Logger.failures(); }
849 return m_Logger.exceptions_detected_by_sentinel();
855 return {m_Logger, std::move(message)};
859 std::string_view top_level_message()
const
861 return m_Logger.top_level_message();
865 const failure_output& failure_messages()
const noexcept
867 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:530
constexpr bool supports_generalized_equivalence_check
The workhorse for (weak) equivalence checking.
Definition: FreeCheckers.hpp:574
constexpr bool supports_best_available_check
Condition for applying the best available check.
Definition: FreeCheckers.hpp:624
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
constexpr bool supports_equality_check
Condition for applying an equality check.
Definition: FreeCheckers.hpp:474
constexpr bool supports_iterator_range_check
Condition for applying a check across a range of values.
Definition: FreeCheckers.hpp:360
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.
Meta-programming utilities.
Exposes elementary check methods, with the option to plug in arbitrary Extenders to compose functiona...
Definition: FreeCheckers.hpp:708
Summaries data generated by the logger, for the purposes of reporting.
Definition: TestLogger.hpp:299
Paths used by the project.
Definition: ProjectPaths.hpp:469
Definition: Output.hpp:186
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:298
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:462
Definition: CoreInfrastructure.hpp:99
class template, specializations of which implement various comparisons for the specified type.
Definition: FreeCheckers.hpp:78
Definition: FreeCheckers.hpp:128