Sequoia
Loading...
Searching...
No Matches
ConcreteTypeCheckers.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2020. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
8#pragma once
9
55
56#include <any>
57#include <array>
58#include <format>
59#include <functional>
60#include <memory>
61#include <optional>
62#include <tuple>
63#include <variant>
64
65namespace sequoia::testing
66{
72 template<class Char, class Traits>
73 struct value_tester<std::basic_string_view<Char, Traits>>
74 {
75 using string_view_type = std::basic_string_view<Char, Traits>;
76
77 template<class Allocator>
78 using string_type = std::basic_string<Char, Traits, Allocator>;
79 private:
80 using iter_type = typename string_view_type::const_iterator;
81 using size_type = typename string_view_type::size_type;
82
83 static void appender(std::string& mess, string_view_type sv, size_type pos, size_type count)
84 {
85 if constexpr(sizeof(char) >= sizeof(Char))
86 {
87 mess.append(sv.substr(pos, count));
88 }
89 else
90 {
91 if(pos > sv.size()) throw std::out_of_range{"pos out of range"};
92 const auto end{count > sv.size() - pos ? sv.size() : pos + count};
93 std::ranges::transform(sv.begin() + pos, sv.begin() + end, std::back_inserter(mess), [](Char c) { return static_cast<char>(c); });
94 }
95 }
96
97 template<class Advisor>
98 static auto make_advisor(std::string_view info, string_view_type obtained, string_view_type prediction, size_type pos, const tutor<Advisor>& advisor)
99 {
100 if constexpr(std::invocable<tutor<Advisor>, Char, Char>)
101 {
102 return
103 tutor{
104 [=, &advisor] (Char a, Char b) {
105 auto m{build_preliminary_message(info, obtained, prediction, pos)};
106 return append_advice(m, {advisor, a, b});
107 },
108 "\n"
109 };
110 }
111 else
112 {
113 return
114 tutor{
115 [=](const auto&, const auto&) {
116 return build_preliminary_message(info, obtained, prediction, pos);
117 },
118 "\n"
119 };
120 }
121 }
122
123 [[nodiscard]]
124 static std::string build_preliminary_message(std::string_view info, string_view_type obtained, string_view_type prediction, size_type pos)
125 {
126 constexpr size_type defaultOffset{30}, defaultCount{60}, npos{string_view_type::npos};
127 const auto sz{std::ranges::min(obtained.size(), prediction.size())};
128
129 auto newlineBackwards{ [pos](string_view_type sv){ return pos < sv.size() ? sv.rfind('\n', pos) : npos; } };
130
131 const auto loc{std::ranges::min(newlineBackwards(prediction), newlineBackwards(obtained))};
132
133 const size_type offset{loc < npos ? std::ranges::min(defaultOffset, pos - loc) : defaultOffset};
134
135 const auto startPos{pos < offset ? 0 :
136 pos < sz ? pos - offset :
137 sz - std::ranges::min(sz, offset)};
138
139 struct message{ std::string mess; bool trunc{}; };
140
141 auto make{
142 [](string_view_type sv, size_type lpos) -> message {
143 std::string mess{lpos > 0 ? "..." : ""};
144
145 const bool newline{(lpos < sv.size()) && (sv[lpos] == '\n')};
146 if(newline) mess.append("\\n");
147
148 const auto rpos{sv.find('\n', lpos+1)};
149 const auto count{rpos == npos ? defaultCount : rpos - lpos};
150
151 if(newline && (count == 1))
152 {
153 mess.append("\\n");
154 }
155 else
156 {
157 if(newline) ++lpos;
158 appender(mess, sv, lpos, count);
159 }
160
161 const bool trunc{lpos + count < sv.size()};
162 if(trunc) mess.append("...");
163
164 return {mess, trunc};
165 }
166 };
167
168 const auto[obMess,obTrunc]{make(obtained , startPos)};
169 const auto[prMess,prTrunc]{make(prediction, startPos)};
170
171 const bool trunc{startPos > 0 || obTrunc || prTrunc};
172 return append_lines(info, trunc ? "Surrounding substring(s):" : "Full strings:", prediction_message(obMess, prMess));
173 }
174 public:
175 template<test_mode Mode, class Advisor>
176 static void test(equality_check_t, test_logger<Mode>& logger, string_view_type obtained, string_view_type prediction, const tutor<Advisor>& advisor)
177 {
178 auto iters{std::ranges::mismatch(obtained, prediction)};
179
180 if((iters.in1 != obtained.end()) && (iters.in2 != prediction.end()))
181 {
182 const auto dist{std::ranges::distance(obtained.begin(), iters.in1)};
183 auto adv{make_advisor("", obtained, prediction, dist, advisor)};
184
185 const auto numLines{std::count(prediction.begin(), iters.in2, '\n')};
186
187 const auto mess{
188 [dist,numLines]() {
189 std::string m{"First difference detected "};
190 numLines > 0 ? m.append("on line ").append(std::to_string(numLines+1))
191 : m.append("at character ").append(std::to_string(dist));
192
193 return m.append(":");
194 }()
195 };
196
197 check(equality, mess, logger, *(iters.in1), *(iters.in2), adv);
198 }
199 else if((iters.in1 != obtained.end()) || (iters.in2 != prediction.end()))
200 {
201 auto checker{
202 [&logger, obtained, prediction, &advisor](auto begin, auto iter, std::string_view state, std::string_view adjective){
203 const auto dist{std::ranges::distance(begin, iter)};
204 const auto info{std::string{"First "}.append(state).append(" character: ").append(display_character(*iter))};
205 auto adv{make_advisor(info, obtained, prediction, dist, advisor)};
206
207 const auto mess{append_lines("Lengths differ", std::string{"Obtained string is too "}.append(adjective))};
208
209 check(equality, mess, logger, obtained.size(), prediction.size(), adv);
210 }
211 };
212
213 if(iters.in2 != prediction.end())
214 {
215 checker(prediction.begin(), iters.in2, "missing", "short");
216 }
217 else if(iters.in1 != obtained.end())
218 {
219 checker(obtained.begin(), iters.in1, "excess", "long");
220 }
221 }
222 }
223
224 template<test_mode Mode, class Advisor, class Allocator>
225 static void test(equivalence_check_t, test_logger<Mode>& logger, string_view_type obtained, string_type<Allocator> prediction, const tutor<Advisor>& advisor)
226 {
227 test(equality, logger, obtained, string_view_type{prediction}, advisor);
228 }
229 };
230
236 template<class Char, class Traits, alloc Allocator>
237 struct value_tester<std::basic_string<Char, Traits, Allocator>>
238 {
239 using string_type = std::basic_string<Char, Traits, Allocator>;
240 using string_view_type = std::basic_string_view<Char, Traits>;
241
242 template<test_mode Mode, class Advisor>
243 static void test(equality_check_t, test_logger<Mode>& logger, const string_type& obtained, const string_type& prediction, tutor<Advisor> advisor)
244 {
246
247 tester::test(equality_check_t{}, logger, string_view_type{obtained}, string_view_type{prediction}, std::move(advisor));
248 }
249
250 template<test_mode Mode, std::size_t N, class Advisor>
251 static void test(equivalence_check_t, test_logger<Mode>& logger, const string_type& obtained, char const (&prediction)[N], tutor<Advisor> advisor)
252 {
254
255 tester::test(equality_check_t{}, logger, string_view_type{obtained}, string_view_type{prediction}, std::move(advisor));
256 }
257
258 template<test_mode Mode, class Advisor>
259 static void test(equivalence_check_t, test_logger<Mode>& logger, const string_type& obtained, std::basic_string_view<Char, Traits> prediction, tutor<Advisor> advisor)
260 {
262
263 tester::test(equality_check_t{}, logger, string_view_type{obtained}, string_view_type{prediction}, std::move(advisor));
264 }
265 };
266
269 template<class S, class T>
270 struct value_tester<std::pair<S, T>>
271 {
272 template<class CheckType, test_mode Mode, class U, class V, class Advisor>
273 requires ( std::is_same_v<std::remove_cvref_t<S>, std::remove_cvref_t<U>>
274 && std::is_same_v<std::remove_cvref_t<T>, std::remove_cvref_t<V>>)
275 static void test(CheckType flavour, test_logger<Mode>& logger, const std::pair<S, T>& value, const std::pair<U, V>& prediction, const tutor<Advisor>& advisor)
276 {
277 check_elements(flavour, logger, value, prediction, std::move(advisor));
278 }
279
280 template<class CheckType, test_mode Mode, class Advisor>
281 static void test(equality_check_t, test_logger<Mode>& logger, const std::pair<S, T>& value, const std::pair<S, T>& prediction, const tutor<Advisor>& advisor)
282 {
283 check_elements(equality, logger, value, prediction, std::move(advisor));
284 }
285
286 private:
287 template<class CheckType, test_mode Mode, class U, class V, class Advisor>
288 static void check_elements(CheckType, test_logger<Mode>& logger, const std::pair<S, T>& value, const std::pair<U, V>& prediction, const tutor<Advisor>& advisor)
289 {
290 check(CheckType{}, "First element of pair is incorrect", logger, value.first, prediction.first, advisor);
291 check(CheckType{}, "Second element of pair is incorrect", logger, value.second, prediction.second, advisor);
292 }
293 };
294
297 template<class... T>
298 struct value_tester<std::tuple<T...>>
299 {
300 private:
301 template<std::size_t I = 0, class CheckType, test_mode Mode, class... U, class Advisor>
302 requires (I < sizeof...(T))
303 static void check_tuple_elements(CheckType flavour, test_logger<Mode>& logger, const std::tuple<T...>& value, const std::tuple<U...>& prediction, const tutor<Advisor>& advisor)
304 {
305 check(flavour, std::format("Element {} of tuple incorrect", I), logger, std::get<I>(value), std::get<I>(prediction), advisor);
306 check_tuple_elements<I+1>(flavour, logger, value, prediction, advisor);
307 }
308
309 template<std::size_t I = 0, class CheckType, test_mode Mode, class... U, class Advisor>
310 static void check_tuple_elements(CheckType, test_logger<Mode>&, const std::tuple<T...>&, const std::tuple<U...>&, const tutor<Advisor>&)
311 {}
312
313 public:
314 template<class CheckType, test_mode Mode, class... U, class Advisor>
315 requires ((sizeof...(T) == sizeof...(U)) && (std::is_same_v<std::remove_cvref_t<T>, std::remove_cvref_t<U>> && ...))
316 static void test(CheckType flavour, test_logger<Mode>& logger, const std::tuple<T...>& value, const std::tuple<U...>& prediction, const tutor<Advisor>& advisor)
317 {
318 check_tuple_elements(flavour, logger, value, prediction, advisor);
319 }
320
321 template<class CheckType, test_mode Mode, class Advisor>
322 static void test(equality_check_t, test_logger<Mode>& logger, const std::tuple<T...>& value, const std::tuple<T...>& prediction, const tutor<Advisor>& advisor)
323 {
324 check_tuple_elements(equality, logger, value, prediction, advisor);
325 }
326 };
327
328 [[nodiscard]]
329 std::string path_check_preamble(std::string_view prefix, const std::filesystem::path& path, const std::filesystem::path& prediction);
330
334 {
335 template<test_mode Mode>
336 void operator()(test_logger<Mode>& logger, const std::filesystem::path& file, const std::filesystem::path& prediction) const
337 {
338 const auto [reducedWorking, reducedPrediction] {get_reduced_file_content(file, prediction)};
339
340 testing::check(report_failed_read(file), logger, static_cast<bool>(reducedWorking));
341 testing::check(report_failed_read(prediction), logger, static_cast<bool>(reducedPrediction));
342
343 if(reducedWorking && reducedPrediction)
344 {
345 check(equality, path_check_preamble("Contents of", file, prediction), logger, reducedWorking.value(), reducedPrediction.value());
346 }
347 }
348 };
349
350 template<class T>
351 inline constexpr bool is_file_comparer_v{
352 std::invocable<T, test_logger<test_mode::standard>&, std::filesystem::path, std::filesystem::path>
353 };
354
358 template<class DefaultComparer, class... Comparers>
359 requires (is_file_comparer_v<DefaultComparer> && (is_file_comparer_v<Comparers> && ...))
361 {
362 public:
363 [[nodiscard]]
364 constexpr static std::size_t size() noexcept
365 {
366 return 1 + sizeof...(Comparers);
367 }
368
369 template<class... Extensions>
370 requires (sizeof...(Extensions) == size()) && (std::is_constructible_v<std::string, Extensions> && ...)
371 general_file_checker(Extensions... extensions)
372 : m_Factory{std::move(extensions)...}
373 {}
374
375 template<test_mode Mode>
376 void check_file(test_logger<Mode>& logger, const std::filesystem::path& file, const std::filesystem::path& prediction) const
377 {
378 const auto checker{m_Factory.template make_or<DefaultComparer>(file.extension().string())};
379 std::visit([&logger, &file, &prediction](auto&& fn){ fn(logger, file, prediction); }, checker);
380 }
381 private:
382 using factory = object::factory<DefaultComparer, Comparers...>;
383
384 factory m_Factory;
385 };
386
387 template<class DefaultComparer, class... Comparers>
389
390 template<class DefaultComparer, class... Comparers>
392
406 template<>
407 struct value_tester<std::filesystem::path>
408 {
409 template<class DefaultComparer, class... Comparers, test_mode Mode>
411 test_logger<Mode>& logger,
412 const std::filesystem::path& path,
413 const std::filesystem::path& prediction)
414 {
415 namespace fs = std::filesystem;
416
417 auto pred{
418 [&logger](const fs::path& pathFinalToken, const fs::path& predictionFinalToken)
419 {
420 return check(equality, "Final path token", logger, pathFinalToken, predictionFinalToken);
421 }
422 };
423
424 check_path(logger, checker.customizer, path, prediction, pred);
425 }
426
427 template<test_mode Mode>
428 static void test(equivalence_check_t, test_logger<Mode>& logger, const std::filesystem::path& path, const std::filesystem::path& prediction)
429 {
430 test(basic_path_equivalence, logger, path, prediction);
431 }
432
433 template<class DefaultComparer, class... Comparers, test_mode Mode>
435 test_logger<Mode>& logger,
436 const std::filesystem::path& path,
437 const std::filesystem::path& prediction)
438 {
439 namespace fs = std::filesystem;
440 check_path(logger, checker.customizer, path, prediction, [](const fs::path&, const fs::path&) { return true; });
441 }
442
443 template<test_mode Mode>
444 static void test(weak_equivalence_check_t, test_logger<Mode>& logger, const std::filesystem::path& path, const std::filesystem::path& prediction)
445 {
446 test(basic_path_weak_equivalence, logger, path, prediction);
447 }
448 private:
449 constexpr static std::array<std::string_view, 2>
450 excluded_files{".DS_Store", ".keep"};
451
452 constexpr static std::array<std::string_view, 1>
453 excluded_extensions{seqpat};
454
456
457 static const basic_file_checker_t basic_file_checker;
458
459 static const general_equivalence_check_t<basic_file_checker_t> basic_path_equivalence;
460 static const general_weak_equivalence_check_t<basic_file_checker_t> basic_path_weak_equivalence;
461
462 template<test_mode Mode, class Customization, invocable_r<bool, std::filesystem::path, std::filesystem::path> FinalTokenComparison>
463 static void check_path(test_logger<Mode>& logger, const Customization& custom, const std::filesystem::path& path, const std::filesystem::path& prediction, FinalTokenComparison compare)
464 {
465 namespace fs = std::filesystem;
466
467 const auto pathType{fs::status(path).type()};
468 const auto predictionType{fs::status(prediction).type()};
469
470 if(check(equality, path_check_preamble("Path type", path, prediction), logger, pathType, predictionType))
471 {
472 if(!path.empty())
473 {
474 const auto pathFinalToken{back(path)};
475 const auto predictionFinalToken{back(prediction)};
476 if(compare(pathFinalToken, predictionFinalToken))
477 {
478 switch(pathType)
479 {
480 case fs::file_type::regular:
481 check_file(logger, custom, path, prediction);
482 break;
483 case fs::file_type::directory:
484 check_directory(logger, custom, path, prediction, compare);
485 break;
486 default:
487 throw std::logic_error{std::string{"Detailed equivalance check for paths of type '"}
488 .append(serializer<fs::file_type>::make(pathType)).append("' not currently implemented")};
489 }
490 }
491 }
492 }
493 }
494
495 template<test_mode Mode, class Customization, invocable_r<bool, std::filesystem::path, std::filesystem::path> FinalTokenComparison>
496 static void check_directory(test_logger<Mode>& logger, const Customization& custom, const std::filesystem::path& dir, const std::filesystem::path& prediction, FinalTokenComparison compare)
497 {
498 namespace fs = std::filesystem;
499
500 auto generator{
501 [](const fs::path& dirPath) {
502 std::vector<fs::path> paths{};
503 for(const auto& p : fs::directory_iterator(dirPath))
504 {
505 if( std::ranges::find(excluded_files, p.path().filename()) == excluded_files.end()
506 && std::ranges::find(excluded_extensions, p.path().extension()) == excluded_extensions.end())
507 {
508 paths.push_back(p);
509 }
510 }
511
512 std::ranges::sort(paths);
513
514 return paths;
515 }
516 };
517
518 const std::vector<fs::path> paths{generator(dir)}, predictedPaths{generator(prediction)};
519
520 check(
521 equality,
522 std::string{"Number of directory entries for "}.append(dir.generic_string()),
523 logger,
524 paths.size(),
525 predictedPaths.size()
526 );
527
528 const auto iters{std::ranges::mismatch(paths, predictedPaths,
529 [&dir,&prediction](const fs::path& lhs, const fs::path& rhs) {
530 return fs::relative(lhs, dir) == fs::relative(rhs, prediction);
531 })};
532 if((iters.in1 != paths.end()) && (iters.in2 != predictedPaths.end()))
533 {
534 check(equality, "First directory entry mismatch", logger, *iters.in1, *iters.in2);
535 }
536 else if(iters.in1 != paths.end())
537 {
538 check(equality, "First directory entry mismatch", logger, *iters.in1, fs::path{});
539 }
540 else if(iters.in2 != predictedPaths.end())
541 {
542 check(equality, "First directory entry mismatch", logger, fs::path{}, *iters.in2);
543 }
544 else
545 {
546 for(std::size_t i{}; i < paths.size(); ++i)
547 {
548 check_path(logger, custom, paths[i], predictedPaths[i], compare);
549 }
550 }
551 }
552
553 template<test_mode Mode, class Customization>
554 static void check_file(test_logger<Mode>& logger, const Customization& custom, const std::filesystem::path& file, const std::filesystem::path& prediction)
555 {
556 custom.check_file(logger, file, prediction);
557 }
558 };
559
562 template<class... Ts>
563 struct value_tester<std::variant<Ts...>>
564 {
565 using type = std::variant<Ts...>;
566
567 template<class CheckType, test_mode Mode, class Advisor>
568 static void test(CheckType flavour, test_logger<Mode>& logger, const type& obtained, const type& prediction, tutor<Advisor> advisor)
569 {
570 if(check(equality, "Variant Index", logger, obtained.index(), prediction.index()))
571 {
572 check_value(flavour, logger, obtained, prediction, advisor, std::make_index_sequence<sizeof...(Ts)>());
573 }
574 }
575 private:
576 template<class CheckType, test_mode Mode, class Advisor, std::size_t... I>
577 static void check_value(CheckType flavour, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor, std::index_sequence<I...>)
578 {
579 (check_value<I>(flavour, logger, obtained, prediction, advisor), ...);
580 }
581
582 template<std::size_t I, class CheckType, test_mode Mode, class Advisor>
583 static void check_value(CheckType flavour, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
584 {
585 if(auto pObtained{std::get_if<I>(&obtained)})
586 {
587 if(auto pPrediction{std::get_if<I>(&prediction)})
588 {
589 check(flavour, "Variant Contents", logger, *pObtained, *pPrediction, advisor);
590 }
591 else
592 {
593 throw std::logic_error{"Inconsistant variant access"};
594 }
595 }
596 }
597 };
598
601 template<class T>
602 struct value_tester<std::optional<T>>
603 {
604 using type = std::optional<T>;
605
606 template<class CheckType, test_mode Mode, class Advisor>
607 static void test(CheckType flavour, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
608 {
609 if(obtained && prediction)
610 {
611 check(flavour, "Contents of optional", logger, *obtained, *prediction, advisor);
612 }
613 else
614 {
615 const bool obtainedIsNull{obtained}, predictionIsNull{prediction};
616
617 check(equality,
618 nullable_type_message(obtainedIsNull, predictionIsNull),
619 logger,
620 static_cast<bool>(obtained),
621 static_cast<bool>(prediction));
622 }
623 }
624 };
625
633 template<>
634 struct value_tester<std::any>
635 {
636 using type = std::any;
637
638 template<test_mode Mode, class T, class Advisor>
639 static void test(equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const T& prediction, const tutor<Advisor>& advisor)
640 {
641 if(check("Has value", logger, obtained.has_value()))
642 {
643 try
644 {
645 const auto& val{std::any_cast<T>(obtained)};
646 check(with_best_available, "Value held by std::any", logger, val, prediction, advisor);
647 }
648 catch(const std::bad_any_cast&)
649 {
650 check("std::any does not hold the expected type", logger, false);
651 }
652 }
653 }
654 };
655
667 template<class T>
668 struct value_tester<T*>
669 {
670 using type = T*;
671
672 template<test_mode Mode, class Advisor>
673 static void test(equivalence_check_t, test_logger<Mode>& logger, type obtained, type prediction, const tutor<Advisor>& advisor)
674 {
675 if(obtained && prediction)
676 {
677 check(with_best_available, "Pointees differ", logger, *obtained, *prediction, advisor);
678 }
679 else
680 {
681 const auto obtainedIsNull{static_cast<bool>(obtained)}, predictionIsNull{static_cast<bool>(prediction)};
682
683 check(equality,
684 nullable_type_message(obtainedIsNull, predictionIsNull),
685 logger,
686 obtainedIsNull,
687 predictionIsNull);
688 }
689 }
690 };
691
699 template<class T>
701 {
702 using type = T;
703
704 template<test_mode Mode, class Advisor>
705 static void test(equality_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
706 {
707 check(equality, "Underlying pointers differ", logger, obtained.get(), prediction.get(), advisor);
708 }
709 protected:
710 ~smart_pointer_tester() = default;
711
712 template<test_mode Mode, class Advisor>
713 static void test_pointees(test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
714 {
715 if(obtained && prediction)
716 {
717 check(with_best_available, "Pointees differ", logger, *obtained, *prediction, advisor);
718 }
719 else
720 {
721 const bool obtainedIsNull{obtained}, predictionIsNull{prediction};
722
723 check(equality,
724 nullable_type_message(obtainedIsNull, predictionIsNull),
725 logger,
726 static_cast<bool>(obtained),
727 static_cast<bool>(prediction));
728 }
729 }
730 };
731
742 template<class T>
743 struct value_tester<std::unique_ptr<T>> : smart_pointer_tester<std::unique_ptr<T>>
744 {
745 using type = std::unique_ptr<T>;
747 using base_t::test;
748
749 template<test_mode Mode, class Advisor>
750 static void test(equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
751 {
752 base_t::test_pointees(logger, obtained, prediction, advisor);
753 }
754 };
755
766 template<class T>
767 struct value_tester<std::shared_ptr<T>> : smart_pointer_tester<std::shared_ptr<T>>
768 {
769 using type = std::shared_ptr<T>;
771 using base_t::test;
772
773 template<test_mode Mode, class Advisor>
774 static void test(equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
775 {
776 base_t::test_pointees(logger, obtained, prediction, advisor);
777 }
778 };
779
786 template<class T>
787 struct value_tester<std::weak_ptr<T>>
788 {
789 using type = std::weak_ptr<T>;
790
791 template<test_mode Mode>
792 static void test(equality_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction)
793 {
794 check(equality, "Underlying pointers differ", logger, obtained.lock(), prediction.lock());
795 }
796
797 template<test_mode Mode>
798 static void test(equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction)
799 {
800 check(equivalence, "Underlying pointers differ", logger, obtained.lock(), prediction.lock());
801 }
802 };
803
804
810 template<class R, class... Args>
811 struct value_tester<std::function<R (Args...)>>
812 {
813 using type = std::function<R (Args...)>;
814
815 template<test_mode Mode>
816 static void test(weak_equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction)
817 {
818 const bool obtainedIsNull{obtained}, predictionIsNull{prediction};
819
820 check(nullable_type_message(obtainedIsNull, predictionIsNull),
821 logger,
822 (obtainedIsNull && predictionIsNull) || (!obtainedIsNull && !predictionIsNull));
823 }
824 };
825
832 template<class Clock, class Duration>
833 struct value_tester<std::chrono::time_point<Clock, Duration>>
834 {
835 using type = std::chrono::time_point<Clock, Duration>;
836
837 template<test_mode Mode, class Advisor>
838 static void test(equality_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
839 {
840 using ns = std::chrono::nanoseconds;
841 check(equality,
842 "Time since epoch",
843 logger,
844 std::chrono::duration_cast<ns>(obtained.time_since_epoch()),
845 std::chrono::duration_cast<ns>(prediction.time_since_epoch()), advisor);
846 }
847 };
848}
Factory implementation(s)
Contains utilities for automatically editing certain files as part of the test creation process.
File paths and related utilities.
Extensions to the std::filesystem library.
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:379
Utilities for reading/writing to files.
test_mode
Specifies whether tests are run as standard tests or in false postive/negative mode.
Definition: TestMode.hpp:20
Exposes elementary check methods, with the option to plug in arbitrary Extenders to compose functiona...
Definition: FreeCheckers.hpp:710
A file checker, which accepts a variadic set of file comparison function objects.
Definition: ConcreteTypeCheckers.hpp:361
Definition: TestLogger.hpp:183
class template used to wrap function objects which proffer advice.
Definition: Advice.hpp:127
Definition: FreeCheckers.hpp:82
Definition: FreeCheckers.hpp:87
Specialize this struct template to provide custom serialization of a given class. .
Definition: CoreInfrastructure.hpp:28
Helper for testing smart pointers.
Definition: ConcreteTypeCheckers.hpp:701
Function object for comparing files via reading their contents into strings.
Definition: ConcreteTypeCheckers.hpp:334
class template, specializations of which implement various comparisons for the specified type.
Definition: FreeCheckers.hpp:78