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 constexpr size_type defaultOffset{30}, defaultCount{60}, npos{string_view_type::npos};
101 const auto sz{std::ranges::min(obtained.size(), prediction.size())};
102
103 auto newlineBackwards{ [pos](string_view_type sv){ return pos < sv.size() ? sv.rfind('\n', pos) : npos; } };
104
105 const auto loc{std::ranges::min(newlineBackwards(prediction), newlineBackwards(obtained))};
106
107 const size_type offset{loc < npos ? std::ranges::min(defaultOffset, pos - loc) : defaultOffset};
108
109 const auto lpos{pos < offset ? 0 :
110 pos < sz ? pos - offset : sz - std::ranges::min(sz, offset)};
111
112 struct message{ std::string mess; bool trunc{}; };
113
114 auto make{
115 [](string_view_type sv, size_type lpos) -> message {
116 std::string mess{lpos > 0 ? "..." : ""};
117
118 const bool newline{(lpos < sv.size()) && (sv[lpos] == '\n')};
119 if(newline) mess.append("\\n");
120
121 const auto rpos{sv.find('\n', lpos+1)};
122 const auto count{rpos == npos ? defaultCount : rpos - lpos};
123
124 if(newline && (count == 1))
125 {
126 mess.append("\\n");
127 }
128 else
129 {
130 if(newline) ++lpos;
131 appender(mess, sv, lpos, count);
132 }
133
134 const bool trunc{lpos + count < sv.size()};
135 if(trunc) mess.append("...");
136
137 return {mess, trunc};
138 }
139 };
140
141 const auto[obMess,obTrunc]{make(obtained, lpos)};
142 const auto[prMess,prTrunc]{make(prediction, lpos)};
143
144 const bool trunc{lpos > 0 || obTrunc || prTrunc};
145 const auto message{append_lines(info, trunc ? "Surrounding substring(s):" : "Full strings:",
146 prediction_message(obMess, prMess))};
147
148 if constexpr(std::invocable<tutor<Advisor>, Char, Char>)
149 {
150
151 return tutor{
152 [message, advisor] (Char a, Char b) {
153 auto m{message};
154 return append_advice(m, {advisor, a, b});
155 },
156 "\n"
157 };
158 }
159 else
160 {
161 return tutor{
162 [message](const auto&, const auto&) { return message; },
163 "\n"
164 };
165 }
166 }
167
168 public:
169 template<test_mode Mode, class Advisor>
170 static void test(equality_check_t, test_logger<Mode>& logger, string_view_type obtained, string_view_type prediction, const tutor<Advisor>& advisor)
171 {
172 auto iters{std::ranges::mismatch(obtained, prediction)};
173
174 if((iters.in1 != obtained.end()) && (iters.in2 != prediction.end()))
175 {
176 const auto dist{std::ranges::distance(obtained.begin(), iters.in1)};
177 auto adv{make_advisor("", obtained, prediction, dist, advisor)};
178
179 const auto numLines{std::count(prediction.begin(), iters.in2, '\n')};
180
181 const auto mess{
182 [dist,numLines]() {
183 std::string m{"First difference detected "};
184 numLines > 0 ? m.append("on line ").append(std::to_string(numLines+1))
185 : m.append("at character ").append(std::to_string(dist));
186
187 return m.append(":");
188 }()
189 };
190
191 check(equality, mess, logger, *(iters.in1), *(iters.in2), adv);
192 }
193 else if((iters.in1 != obtained.end()) || (iters.in2 != prediction.end()))
194 {
195 auto checker{
196 [&logger, obtained, prediction, &advisor](auto begin, auto iter, std::string_view state, std::string_view adjective){
197 const auto dist{std::ranges::distance(begin, iter)};
198 const auto info{std::string{"First "}.append(state).append(" character: ").append(display_character(*iter))};
199 auto adv{make_advisor(info, obtained, prediction, dist, advisor)};
200
201 const auto mess{append_lines("Lengths differ", std::string{"Obtained string is too "}.append(adjective))};
202
203 check(equality, mess, logger, obtained.size(), prediction.size(), adv);
204 }
205 };
206
207 if(iters.in2 != prediction.end())
208 {
209 checker(prediction.begin(), iters.in2, "missing", "short");
210 }
211 else if(iters.in1 != obtained.end())
212 {
213 checker(obtained.begin(), iters.in1, "excess", "long");
214 }
215 }
216 }
217
218 template<test_mode Mode, class Advisor, class Allocator>
219 static void test(equivalence_check_t, test_logger<Mode>& logger, string_view_type obtained, string_type<Allocator> prediction, const tutor<Advisor>& advisor)
220 {
221 test(equality, logger, obtained, string_view_type{prediction}, advisor);
222 }
223 };
224
230 template<class Char, class Traits, alloc Allocator>
231 struct value_tester<std::basic_string<Char, Traits, Allocator>>
232 {
233 using string_type = std::basic_string<Char, Traits, Allocator>;
234 using string_view_type = std::basic_string_view<Char, Traits>;
235
236 template<test_mode Mode, class Advisor>
237 static void test(equality_check_t, test_logger<Mode>& logger, const string_type& obtained, const string_type& prediction, tutor<Advisor> advisor)
238 {
240
241 tester::test(equality_check_t{}, logger, string_view_type{obtained}, string_view_type{prediction}, std::move(advisor));
242 }
243
244 template<test_mode Mode, std::size_t N, class Advisor>
245 static void test(equivalence_check_t, test_logger<Mode>& logger, const string_type& obtained, char const (&prediction)[N], tutor<Advisor> advisor)
246 {
248
249 tester::test(equality_check_t{}, logger, string_view_type{obtained}, string_view_type{prediction}, std::move(advisor));
250 }
251
252 template<test_mode Mode, class Advisor>
253 static void test(equivalence_check_t, test_logger<Mode>& logger, const string_type& obtained, std::basic_string_view<Char, Traits> prediction, tutor<Advisor> advisor)
254 {
256
257 tester::test(equality_check_t{}, logger, string_view_type{obtained}, string_view_type{prediction}, std::move(advisor));
258 }
259 };
260
263 template<class S, class T>
264 struct value_tester<std::pair<S, T>>
265 {
266 template<class CheckType, test_mode Mode, class U, class V, class Advisor>
267 requires ( std::is_same_v<std::remove_cvref_t<S>, std::remove_cvref_t<U>>
268 && std::is_same_v<std::remove_cvref_t<T>, std::remove_cvref_t<V>>)
269 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)
270 {
271 check_elements(flavour, logger, value, prediction, std::move(advisor));
272 }
273
274 template<class CheckType, test_mode Mode, class Advisor>
275 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)
276 {
277 check_elements(equality, logger, value, prediction, std::move(advisor));
278 }
279
280 private:
281 template<class CheckType, test_mode Mode, class U, class V, class Advisor>
282 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)
283 {
284 check(CheckType{}, "First element of pair is incorrect", logger, value.first, prediction.first, advisor);
285 check(CheckType{}, "Second element of pair is incorrect", logger, value.second, prediction.second, advisor);
286 }
287 };
288
291 template<class... T>
292 struct value_tester<std::tuple<T...>>
293 {
294 private:
295 template<std::size_t I = 0, class CheckType, test_mode Mode, class... U, class Advisor>
296 requires (I < sizeof...(T))
297 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)
298 {
299 check(flavour, std::format("Element {} of tuple incorrect", I), logger, std::get<I>(value), std::get<I>(prediction), advisor);
300 check_tuple_elements<I+1>(flavour, logger, value, prediction, advisor);
301 }
302
303 template<std::size_t I = 0, class CheckType, test_mode Mode, class... U, class Advisor>
304 static void check_tuple_elements(CheckType, test_logger<Mode>&, const std::tuple<T...>&, const std::tuple<U...>&, const tutor<Advisor>&)
305 {}
306
307 public:
308 template<class CheckType, test_mode Mode, class... U, class Advisor>
309 requires ((sizeof...(T) == sizeof...(U)) && (std::is_same_v<std::remove_cvref_t<T>, std::remove_cvref_t<U>> && ...))
310 static void test(CheckType flavour, test_logger<Mode>& logger, const std::tuple<T...>& value, const std::tuple<U...>& prediction, const tutor<Advisor>& advisor)
311 {
312 check_tuple_elements(flavour, logger, value, prediction, advisor);
313 }
314
315 template<class CheckType, test_mode Mode, class Advisor>
316 static void test(equality_check_t, test_logger<Mode>& logger, const std::tuple<T...>& value, const std::tuple<T...>& prediction, const tutor<Advisor>& advisor)
317 {
318 check_tuple_elements(equality, logger, value, prediction, advisor);
319 }
320 };
321
322 [[nodiscard]]
323 std::string path_check_preamble(std::string_view prefix, const std::filesystem::path& path, const std::filesystem::path& prediction);
324
328 {
329 template<test_mode Mode>
330 void operator()(test_logger<Mode>& logger, const std::filesystem::path& file, const std::filesystem::path& prediction) const
331 {
332 const auto [reducedWorking, reducedPrediction] {get_reduced_file_content(file, prediction)};
333
334 testing::check(report_failed_read(file), logger, static_cast<bool>(reducedWorking));
335 testing::check(report_failed_read(prediction), logger, static_cast<bool>(reducedPrediction));
336
337 if(reducedWorking && reducedPrediction)
338 {
339 check(equality, path_check_preamble("Contents of", file, prediction), logger, reducedWorking.value(), reducedPrediction.value());
340 }
341 }
342 };
343
344 template<class T>
345 inline constexpr bool is_file_comparer_v{
346 std::invocable<T, test_logger<test_mode::standard>&, std::filesystem::path, std::filesystem::path>
347 };
348
352 template<class DefaultComparer, class... Comparers>
353 requires (is_file_comparer_v<DefaultComparer> && (is_file_comparer_v<Comparers> && ...))
355 {
356 public:
357 [[nodiscard]]
358 constexpr static std::size_t size() noexcept
359 {
360 return 1 + sizeof...(Comparers);
361 }
362
363 template<class... Extensions>
364 requires (sizeof...(Extensions) == size()) && (std::is_constructible_v<std::string, Extensions> && ...)
365 general_file_checker(Extensions... extensions)
366 : m_Factory{std::move(extensions)...}
367 {}
368
369 template<test_mode Mode>
370 void check_file(test_logger<Mode>& logger, const std::filesystem::path& file, const std::filesystem::path& prediction) const
371 {
372 const auto checker{m_Factory.template make_or<DefaultComparer>(file.extension().string())};
373 std::visit([&logger, &file, &prediction](auto&& fn){ fn(logger, file, prediction); }, checker);
374 }
375 private:
376 using factory = object::factory<DefaultComparer, Comparers...>;
377
378 factory m_Factory;
379 };
380
381 template<class DefaultComparer, class... Comparers>
383
384 template<class DefaultComparer, class... Comparers>
386
400 template<>
401 struct value_tester<std::filesystem::path>
402 {
403 template<class DefaultComparer, class... Comparers, test_mode Mode>
405 test_logger<Mode>& logger,
406 const std::filesystem::path& path,
407 const std::filesystem::path& prediction)
408 {
409 namespace fs = std::filesystem;
410
411 auto pred{
412 [&logger](const fs::path& pathFinalToken, const fs::path& predictionFinalToken)
413 {
414 return check(equality, "Final path token", logger, pathFinalToken, predictionFinalToken);
415 }
416 };
417
418 check_path(logger, checker.customizer, path, prediction, pred);
419 }
420
421 template<test_mode Mode>
422 static void test(equivalence_check_t, test_logger<Mode>& logger, const std::filesystem::path& path, const std::filesystem::path& prediction)
423 {
424 test(basic_path_equivalence, logger, path, prediction);
425 }
426
427 template<class DefaultComparer, class... Comparers, test_mode Mode>
429 test_logger<Mode>& logger,
430 const std::filesystem::path& path,
431 const std::filesystem::path& prediction)
432 {
433 namespace fs = std::filesystem;
434 check_path(logger, checker.customizer, path, prediction, [](const fs::path&, const fs::path&) { return true; });
435 }
436
437 template<test_mode Mode>
438 static void test(weak_equivalence_check_t, test_logger<Mode>& logger, const std::filesystem::path& path, const std::filesystem::path& prediction)
439 {
440 test(basic_path_weak_equivalence, logger, path, prediction);
441 }
442 private:
443 constexpr static std::array<std::string_view, 2>
444 excluded_files{".DS_Store", ".keep"};
445
446 constexpr static std::array<std::string_view, 1>
447 excluded_extensions{seqpat};
448
450
451 static const basic_file_checker_t basic_file_checker;
452
453 static const general_equivalence_check_t<basic_file_checker_t> basic_path_equivalence;
454 static const general_weak_equivalence_check_t<basic_file_checker_t> basic_path_weak_equivalence;
455
456 template<test_mode Mode, class Customization, invocable_r<bool, std::filesystem::path, std::filesystem::path> FinalTokenComparison>
457 static void check_path(test_logger<Mode>& logger, const Customization& custom, const std::filesystem::path& path, const std::filesystem::path& prediction, FinalTokenComparison compare)
458 {
459 namespace fs = std::filesystem;
460
461 const auto pathType{fs::status(path).type()};
462 const auto predictionType{fs::status(prediction).type()};
463
464 if(check(equality, path_check_preamble("Path type", path, prediction), logger, pathType, predictionType))
465 {
466 if(!path.empty())
467 {
468 const auto pathFinalToken{back(path)};
469 const auto predictionFinalToken{back(prediction)};
470 if(compare(pathFinalToken, predictionFinalToken))
471 {
472 switch(pathType)
473 {
474 case fs::file_type::regular:
475 check_file(logger, custom, path, prediction);
476 break;
477 case fs::file_type::directory:
478 check_directory(logger, custom, path, prediction, compare);
479 break;
480 default:
481 throw std::logic_error{std::string{"Detailed equivalance check for paths of type '"}
482 .append(serializer<fs::file_type>::make(pathType)).append("' not currently implemented")};
483 }
484 }
485 }
486 }
487 }
488
489 template<test_mode Mode, class Customization, invocable_r<bool, std::filesystem::path, std::filesystem::path> FinalTokenComparison>
490 static void check_directory(test_logger<Mode>& logger, const Customization& custom, const std::filesystem::path& dir, const std::filesystem::path& prediction, FinalTokenComparison compare)
491 {
492 namespace fs = std::filesystem;
493
494 auto generator{
495 [](const fs::path& dir) {
496 std::vector<fs::path> paths{};
497 for(const auto& p : fs::directory_iterator(dir))
498 {
499 if( std::ranges::find(excluded_files, p.path().filename()) == excluded_files.end()
500 && std::ranges::find(excluded_extensions, p.path().extension()) == excluded_extensions.end())
501 {
502 paths.push_back(p);
503 }
504 }
505
506 std::ranges::sort(paths);
507
508 return paths;
509 }
510 };
511
512 const std::vector<fs::path> paths{generator(dir)}, predictedPaths{generator(prediction)};
513
514 check(equality, std::string{"Number of directory entries for "}.append(dir.generic_string()),
515 logger,
516 paths.size(),
517 predictedPaths.size());
518
519 const auto iters{std::ranges::mismatch(paths, predictedPaths,
520 [&dir,&prediction](const fs::path& lhs, const fs::path& rhs) {
521 return fs::relative(lhs, dir) == fs::relative(rhs, prediction);
522 })};
523 if((iters.in1 != paths.end()) && (iters.in2 != predictedPaths.end()))
524 {
525 check(equality, "First directory entry mismatch", logger, *iters.in1, *iters.in2);
526 }
527 else if(iters.in1 != paths.end())
528 {
529 check(equality, "First directory entry mismatch", logger, *iters.in1, fs::path{});
530 }
531 else if(iters.in2 != predictedPaths.end())
532 {
533 check(equality, "First directory entry mismatch", logger, fs::path{}, *iters.in2);
534 }
535 else
536 {
537 for(std::size_t i{}; i < paths.size(); ++i)
538 {
539 check_path(logger, custom, paths[i], predictedPaths[i], compare);
540 }
541 }
542 }
543
544 template<test_mode Mode, class Customization>
545 static void check_file(test_logger<Mode>& logger, const Customization& custom, const std::filesystem::path& file, const std::filesystem::path& prediction)
546 {
547 custom.check_file(logger, file, prediction);
548 }
549 };
550
553 template<class... Ts>
554 struct value_tester<std::variant<Ts...>>
555 {
556 using type = std::variant<Ts...>;
557
558 template<class CheckType, test_mode Mode, class Advisor>
559 static void test(CheckType flavour, test_logger<Mode>& logger, const type& obtained, const type& prediction, tutor<Advisor> advisor)
560 {
561 if(check(equality, "Variant Index", logger, obtained.index(), prediction.index()))
562 {
563 check_value(flavour, logger, obtained, prediction, advisor, std::make_index_sequence<sizeof...(Ts)>());
564 }
565 }
566 private:
567 template<class CheckType, test_mode Mode, class Advisor, std::size_t... I>
568 static void check_value(CheckType flavour, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor, std::index_sequence<I...>)
569 {
570 (check_value<I>(flavour, logger, obtained, prediction, advisor), ...);
571 }
572
573 template<std::size_t I, class CheckType, test_mode Mode, class Advisor>
574 static void check_value(CheckType flavour, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
575 {
576 if(auto pObtained{std::get_if<I>(&obtained)})
577 {
578 if(auto pPrediction{std::get_if<I>(&prediction)})
579 {
580 check(flavour, "Variant Contents", logger, *pObtained, *pPrediction, advisor);
581 }
582 else
583 {
584 throw std::logic_error{"Inconsistant variant access"};
585 }
586 }
587 }
588 };
589
592 template<class T>
593 struct value_tester<std::optional<T>>
594 {
595 using type = std::optional<T>;
596
597 template<class CheckType, test_mode Mode, class Advisor>
598 static void test(CheckType flavour, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
599 {
600 if(obtained && prediction)
601 {
602 check(flavour, "Contents of optional", logger, *obtained, *prediction, advisor);
603 }
604 else
605 {
606 const bool obtainedIsNull{obtained}, predictionIsNull{prediction};
607
608 check(equality,
609 nullable_type_message(obtainedIsNull, predictionIsNull),
610 logger,
611 static_cast<bool>(obtained),
612 static_cast<bool>(prediction));
613 }
614 }
615 };
616
624 template<>
625 struct value_tester<std::any>
626 {
627 using type = std::any;
628
629 template<test_mode Mode, class T, class Advisor>
630 static void test(equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const T& prediction, const tutor<Advisor>& advisor)
631 {
632 if(check("Has value", logger, obtained.has_value()))
633 {
634 try
635 {
636 const auto& val{std::any_cast<T>(obtained)};
637 check(with_best_available, "Value held by std::any", logger, val, prediction, advisor);
638 }
639 catch(const std::bad_any_cast&)
640 {
641 check("std::any does not hold the expected type", logger, false);
642 }
643 }
644 }
645 };
646
658 template<class T>
659 struct value_tester<T*>
660 {
661 using type = T*;
662
663 template<test_mode Mode, class Advisor>
664 static void test(equivalence_check_t, test_logger<Mode>& logger, type obtained, type prediction, const tutor<Advisor>& advisor)
665 {
666 if(obtained && prediction)
667 {
668 check(with_best_available, "Pointees differ", logger, *obtained, *prediction, advisor);
669 }
670 else
671 {
672 const auto obtainedIsNull{static_cast<bool>(obtained)}, predictionIsNull{static_cast<bool>(prediction)};
673
674 check(equality,
675 nullable_type_message(obtainedIsNull, predictionIsNull),
676 logger,
677 obtainedIsNull,
678 predictionIsNull);
679 }
680 }
681 };
682
690 template<class T>
692 {
693 using type = T;
694
695 template<test_mode Mode, class Advisor>
696 static void test(equality_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
697 {
698 check(equality, "Underlying pointers differ", logger, obtained.get(), prediction.get(), advisor);
699 }
700 protected:
701 ~smart_pointer_tester() = default;
702
703 template<test_mode Mode, class Advisor>
704 static void test_pointees(test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
705 {
706 if(obtained && prediction)
707 {
708 check(with_best_available, "Pointees differ", logger, *obtained, *prediction, advisor);
709 }
710 else
711 {
712 const bool obtainedIsNull{obtained}, predictionIsNull{prediction};
713
714 check(equality,
715 nullable_type_message(obtainedIsNull, predictionIsNull),
716 logger,
717 static_cast<bool>(obtained),
718 static_cast<bool>(prediction));
719 }
720 }
721 };
722
733 template<class T>
734 struct value_tester<std::unique_ptr<T>> : smart_pointer_tester<std::unique_ptr<T>>
735 {
736 using type = std::unique_ptr<T>;
738 using base_t::test;
739
740 template<test_mode Mode, class Advisor>
741 static void test(equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
742 {
743 using base_t = base_t;
744 base_t::test_pointees(logger, obtained, prediction, advisor);
745 }
746 };
747
758 template<class T>
759 struct value_tester<std::shared_ptr<T>> : smart_pointer_tester<std::shared_ptr<T>>
760 {
761 using type = std::shared_ptr<T>;
763 using base_t::test;
764
765 template<test_mode Mode, class Advisor>
766 static void test(equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
767 {
768 base_t::test_pointees(logger, obtained, prediction, advisor);
769 }
770 };
771
778 template<class T>
779 struct value_tester<std::weak_ptr<T>>
780 {
781 using type = std::weak_ptr<T>;
782
783 template<test_mode Mode>
784 static void test(equality_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction)
785 {
786 check(equality, "Underlying pointers differ", logger, obtained.lock(), prediction.lock());
787 }
788
789 template<test_mode Mode>
790 static void test(equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction)
791 {
792 check(equivalence, "Underlying pointers differ", logger, obtained.lock(), prediction.lock());
793 }
794 };
795
796
802 template<class R, class... Args>
803 struct value_tester<std::function<R (Args...)>>
804 {
805 using type = std::function<R (Args...)>;
806
807 template<test_mode Mode>
808 static void test(weak_equivalence_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction)
809 {
810 const bool obtainedIsNull{obtained}, predictionIsNull{prediction};
811
812 check(nullable_type_message(obtainedIsNull, predictionIsNull),
813 logger,
814 (obtainedIsNull && predictionIsNull) || (!obtainedIsNull && !predictionIsNull));
815 }
816 };
817
824 template<class Clock, class Duration>
825 struct value_tester<std::chrono::time_point<Clock, Duration>>
826 {
827 using type = std::chrono::time_point<Clock, Duration>;
828
829 template<test_mode Mode, class Advisor>
830 static void test(equality_check_t, test_logger<Mode>& logger, const type& obtained, const type& prediction, const tutor<Advisor>& advisor)
831 {
832 using ns = std::chrono::nanoseconds;
833 check(equality,
834 "Time since epoch",
835 logger,
836 std::chrono::duration_cast<ns>(obtained.time_since_epoch()),
837 std::chrono::duration_cast<ns>(prediction.time_since_epoch()), advisor);
838 }
839 };
840}
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:377
Utilities for reading/writing to files.
Exposes elementary check methods, with the option to plug in arbitrary Extenders to compose functiona...
Definition: FreeCheckers.hpp:708
A file checker, which accepts a variadic set of file comparison function objects.
Definition: ConcreteTypeCheckers.hpp:355
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:692
Function object for comparing files via reading their contents into strings.
Definition: ConcreteTypeCheckers.hpp:328
class template, specializations of which implement various comparisons for the specified type.
Definition: FreeCheckers.hpp:78