1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4 
5 #pragma once
6 
7 #include <cmath>
8 #include <fstream>
9 #include <iostream>
10 #include <map>
11 #include <memory>
12 #include <mutex>
13 #include <sstream>
14 #include <string>
15 #include <thread>
16 #include <vector>
17 
18 #include "caf/deep_to_string.hpp"
19 #include "caf/fwd.hpp"
20 #include "caf/logger.hpp"
21 #include "caf/optional.hpp"
22 #include "caf/term.hpp"
23 #include "caf/variant.hpp"
24 
25 #include "caf/detail/arg_wrapper.hpp"
26 #include "caf/detail/type_traits.hpp"
27 
28 namespace caf::test {
29 
30 // -- Function objects for implementing CAF_CHECK_* macros ---------------------
31 
32 template <class F>
33 struct negated {
34   template <class T, class U>
operator ()caf::test::negated35   bool operator()(const T& x, const U& y) {
36     F f;
37     return !f(x, y);
38   }
39 };
40 
41 template <class T, class Comparator>
42 struct compare_visitor {
43   const T& rhs;
44 
45   template <class U>
operator ()caf::test::compare_visitor46   bool operator()(const U& lhs) const {
47     Comparator f;
48     return f(lhs, rhs);
49   }
50 };
51 
52 struct equality_operator {
53   static constexpr bool default_value = false;
54 
55   template <class T, class U,
56             detail::enable_if_t<((std::is_floating_point<T>::value
57                                   && std::is_convertible<U, double>::value)
58                                  || (std::is_floating_point<U>::value
59                                      && std::is_convertible<T, double>::value))
60                                   && detail::is_comparable<T, U>::value,
61                                 int> = 0>
operator ()caf::test::equality_operator62   bool operator()(const T& t, const U& u) const {
63     auto x = static_cast<long double>(t);
64     auto y = static_cast<long double>(u);
65     auto max = std::max(std::abs(x), std::abs(y));
66     auto dif = std::abs(x - y);
67     return dif <= max * 1e-5l;
68   }
69 
70   template <class T, class U,
71             detail::enable_if_t<!((std::is_floating_point<T>::value
72                                    && std::is_convertible<U, double>::value)
73                                   || (std::is_floating_point<U>::value
74                                       && std::is_convertible<T, double>::value))
75                                   && detail::is_comparable<T, U>::value,
76                                 int> = 0>
operator ()caf::test::equality_operator77   bool operator()(const T& x, const U& y) const {
78     return x == y;
79   }
80 
81   template <
82     class T, class U,
83     typename std::enable_if<!detail::is_comparable<T, U>::value, int>::type = 0>
operator ()caf::test::equality_operator84   bool operator()(const T&, const U&) const {
85     return default_value;
86   }
87 };
88 
89 struct inequality_operator {
90   static constexpr bool default_value = true;
91 
92   template <class T, class U,
93             typename std::enable_if<(std::is_floating_point<T>::value
94                                      || std::is_floating_point<U>::value)
95                                       && detail::is_comparable<T, U>::value,
96                                     int>::type
97             = 0>
operator ()caf::test::inequality_operator98   bool operator()(const T& x, const U& y) const {
99     equality_operator f;
100     return !f(x, y);
101   }
102 
103   template <class T, class U,
104             typename std::enable_if<!std::is_floating_point<T>::value
105                                       && !std::is_floating_point<U>::value
106                                       && detail::is_comparable<T, U>::value,
107                                     int>::type
108             = 0>
operator ()caf::test::inequality_operator109   bool operator()(const T& x, const U& y) const {
110     return x != y;
111   }
112 
113   template <
114     class T, class U,
115     typename std::enable_if<!detail::is_comparable<T, U>::value, int>::type = 0>
operator ()caf::test::inequality_operator116   bool operator()(const T&, const U&) const {
117     return default_value;
118   }
119 };
120 
121 template <class F, class T>
122 struct comparison_unbox_helper {
123   const F& f;
124   const T& rhs;
125 
126   template <class U>
operator ()caf::test::comparison_unbox_helper127   bool operator()(const U& lhs) const {
128     return f(lhs, rhs);
129   }
130 };
131 
132 template <class Operator>
133 class comparison {
134 public:
135   // -- default case -----------------------------------------------------------
136 
137   template <class T, class U>
operator ()(const T & x,const U & y) const138   bool operator()(const T& x, const U& y) const {
139     std::integral_constant<bool, SumType<T>()> lhs_is_sum_type;
140     std::integral_constant<bool, SumType<U>()> rhs_is_sum_type;
141     return cmp(x, y, lhs_is_sum_type, rhs_is_sum_type);
142   }
143 
144 private:
145   // -- automagic unboxing of sum types ----------------------------------------
146 
147   template <class T, class U>
cmp(const T & x,const U & y,std::false_type,std::false_type) const148   bool cmp(const T& x, const U& y, std::false_type, std::false_type) const {
149     Operator f;
150     return f(x, y);
151   }
152 
153   template <class T, class U>
cmp(const T & x,const U & y,std::true_type,std::false_type) const154   bool cmp(const T& x, const U& y, std::true_type, std::false_type) const {
155     Operator f;
156     auto inner_x = caf::get_if<U>(&x);
157     return inner_x ? f(*inner_x, y) : Operator::default_value;
158   }
159 
160   template <class T, class U>
cmp(const T & x,const U & y,std::false_type,std::true_type) const161   bool cmp(const T& x, const U& y, std::false_type, std::true_type) const {
162     Operator f;
163     auto inner_y = caf::get_if<T>(&y);
164     return inner_y ? f(x, *inner_y) : Operator::default_value;
165   }
166 
167   template <class T, class U>
cmp(const T & x,const U & y,std::true_type,std::true_type) const168   bool cmp(const T& x, const U& y, std::true_type, std::true_type) const {
169     comparison_unbox_helper<comparison, U> f{*this, y};
170     return visit(f, x);
171   }
172 };
173 
174 using equal_to = comparison<equality_operator>;
175 
176 using not_equal_to = comparison<inequality_operator>;
177 
178 struct less_than {
179   template <class T, class U>
operator ()caf::test::less_than180   bool operator()(const T& x, const U& y) {
181     return x < y;
182   }
183 };
184 
185 struct less_than_or_equal {
186   template <class T, class U>
operator ()caf::test::less_than_or_equal187   bool operator()(const T& x, const U& y) {
188     return x <= y;
189   }
190 };
191 
192 struct greater_than {
193   template <class T, class U>
operator ()caf::test::greater_than194   bool operator()(const T& x, const U& y) {
195     return x > y;
196   }
197 };
198 
199 struct greater_than_or_equal {
200   template <class T, class U>
operator ()caf::test::greater_than_or_equal201   bool operator()(const T& x, const U& y) {
202     return x >= y;
203   }
204 };
205 
206 // -- Core components of the unit testing abstraction --------------------------
207 
208 /// Default test-running function.
209 /// This function will be called automatically unless you define
210 /// `CAF_TEST_NO_MAIN` before including `caf/test/unit_test.hpp`. In
211 /// the latter case you will have to provide you own `main` function,
212 /// where you may want to call `caf::test::main` from.
213 int main(int argc, char** argv);
214 
215 /// A sequence of *checks*.
216 class test {
217 public:
218   test(std::string test_name, bool disabled_by_default);
219 
220   virtual ~test();
221 
222   size_t expected_failures() const;
223 
224   void pass();
225 
226   void fail(bool expected);
227 
228   const std::string& name() const;
229 
good()230   size_t good() {
231     return good_;
232   }
233 
bad()234   size_t bad() {
235     return bad_;
236   }
237 
disabled() const238   bool disabled() const noexcept {
239     return disabled_;
240   }
241 
242   virtual void run_test_impl() = 0;
243 
244 private:
245   size_t expected_failures_;
246   std::string name_;
247   size_t good_;
248   size_t bad_;
249   bool disabled_;
250 };
251 
252 struct dummy_fixture {};
253 
254 template <class T>
255 class test_impl : public test {
256 public:
test_impl(std::string test_name,bool disabled_by_default)257   test_impl(std::string test_name, bool disabled_by_default)
258     : test(std::move(test_name), disabled_by_default) {
259     // nop
260   }
261 
run_test_impl()262   void run_test_impl() override {
263     T impl;
264     impl.run_test_impl();
265   }
266 };
267 
268 namespace detail {
269 
270 [[noreturn]] void requirement_failed(const std::string& msg);
271 
272 // constructs spacing given a line number.
273 const char* fill(size_t line);
274 
275 void remove_trailing_spaces(std::string& x);
276 
277 } // namespace detail
278 
279 /// Logs messages for the test framework.
280 class logger {
281 public:
282   enum class level : int {
283     quiet = 0,
284     error = 1,
285     info = 2,
286     verbose = 3,
287     massive = 4
288   };
289 
290   static bool init(int lvl_cons, int lvl_file, const std::string& logfile);
291 
292   static logger& instance();
293 
294   template <class T>
log(level lvl,const T & x)295   void log(level lvl, const T& x) {
296     struct simple_fwd_t {
297       const T& operator()(const T& y) const {
298         return y;
299       }
300     };
301     using fwd =
302       typename std::conditional<std::is_same<char, T>::value
303                                   || std::is_convertible<T, std::string>::value
304                                   || std::is_same<caf::term, T>::value,
305                                 simple_fwd_t, deep_to_string_t>::type;
306     fwd f;
307     auto y = f(x);
308     if (lvl <= level_console_)
309       *console_ << y;
310     if (lvl <= level_file_)
311       file_ << y;
312   }
313 
log(level lvl,const std::nullptr_t &)314   void log(level lvl, const std::nullptr_t&) {
315     log(lvl, "null");
316   }
317 
318   /// Output stream for logging purposes.
319   class stream {
320   public:
321     stream(logger& parent, level lvl);
322 
323     stream(const stream&) = default;
324 
325     struct reset_flags_t {};
326 
operator <<(reset_flags_t)327     stream& operator<<(reset_flags_t) {
328       return *this;
329     }
330 
331     template <class T>
operator <<(const T & x)332     stream& operator<<(const T& x) {
333       parent_.log(lvl_, x);
334       return *this;
335     }
336 
337   private:
338     logger& parent_;
339     level lvl_;
340   };
341 
342   stream error();
343   stream info();
344   stream verbose();
345   stream massive();
346 
347   void disable_colors();
348 
349 private:
350   logger();
351 
352   level level_console_;
353   level level_file_;
354   std::ostream* console_;
355   std::ofstream file_;
356   std::ostringstream dummy_;
357 };
358 
359 /// Drives unit test execution.
360 class engine {
361 public:
362   /// Sets external command line arguments.
363   /// @param argc The argument counter.
364   /// @param argv The argument vectors.
365   static void args(int argc, char** argv);
366 
367   /// Retrieves the argument counter.
368   /// @returns The number of arguments set via ::args or 0.
369   static int argc();
370 
371   /// Retrieves the argument vector.
372   /// @returns The argument vector set via ::args or `nullptr`.
373   static char** argv();
374 
375   /// Sets path of current executable.
376   /// @param argv The path of current executable.
377   static void path(char* argv);
378 
379   /// Retrieves the path of current executable
380   /// @returns The path to executable set via ::path(char*) or `nullptr`.
381   static char* path();
382 
383   /// Returns the maximum number of seconds a test case is allowed to run.
384   static int max_runtime();
385 
386   /// Sets the maximum number of seconds a test case is
387   /// allowed to run to `value`.
388   static void max_runtime(int value);
389 
390   /// Adds a test to the engine.
391   /// @param cstr_name The name of the suite.
392   /// @param ptr The test to register.
393   static void add(const char* cstr_name, std::unique_ptr<test> ptr);
394 
395   /// Invokes tests in all suites.
396   /// @param colorize Whether to colorize the output.
397   /// @param log_file The filename of the log output. The empty string means
398   ///                 that no log file will be written.
399   /// @param verbosity_console The log verbosity level on the console.
400   /// @param verbosity_file The log verbosity level in the log file.
401   /// @param suites_str Regular expression for including test suites.
402   /// @param not_suites_str Regular expression for excluding test suites.
403   /// @param tests_str Regular expression for individually selecting tests.
404   /// @param not_tests_str Regular expression for individually disabling tests.
405   /// @returns `true` if all tests succeeded.
406   static bool
407   run(bool colorize, const std::string& log_file, int verbosity_console,
408       int verbosity_file, const std::string& suites_str,
409       const std::string& not_suites_str, const std::string& tests_str,
410       const std::string& not_tests_str);
411 
412   static const char* last_check_file();
413   static void last_check_file(const char* file);
414 
415   static size_t last_check_line();
416   static void last_check_line(size_t line);
417 
418   static test* current_test();
419 
420   static std::vector<std::string> available_suites();
421 
422   static std::vector<std::string> available_tests(const std::string& suite);
423 
424 private:
425   engine() = default;
426 
427   static engine& instance();
428 
429   static std::string render(std::chrono::microseconds t);
430 
431   int argc_ = 0;
432   char** argv_ = nullptr;
433   char* path_ = nullptr;
434   bool colorize_ = false;
435   const char* check_file_ = "<none>";
436   size_t check_line_ = 0;
437   test* current_test_ = nullptr;
438   std::map<std::string, std::vector<std::unique_ptr<test>>> suites_;
439   int max_runtime_ = 30; // 30s per default
440 };
441 
442 namespace detail {
443 
444 template <class T>
445 struct adder {
addercaf::test::detail::adder446   adder(const char* suite_name, const char* test_name, bool disabled) {
447     engine::add(suite_name, std::unique_ptr<T>{new T(test_name, disabled)});
448   }
449 };
450 
451 bool check(test* parent, const char* file, size_t line, const char* expr,
452            bool should_fail, bool result);
453 
454 template <class T, class U>
check(test * parent,const char * file,size_t line,const char * expr,bool should_fail,bool result,const T & x,const U & y)455 bool check(test* parent, const char* file, size_t line, const char* expr,
456            bool should_fail, bool result, const T& x, const U& y) {
457   auto out = logger::instance().massive();
458   if (result) {
459     out << term::green << "** " << term::blue << file << term::yellow << ":"
460         << term::blue << line << fill(line) << term::reset << expr << '\n';
461     parent->pass();
462   } else {
463     out << term::red << "!! " << term::blue << file << term::yellow << ":"
464         << term::blue << line << fill(line) << term::reset << expr
465         << term::magenta << " (" << term::red << x << term::magenta << " !! "
466         << term::red << y << term::magenta << ')' << term::reset_endl;
467     parent->fail(should_fail);
468   }
469   return result;
470 }
471 
472 bool check_un(bool result, const char* file, size_t line, const char* expr);
473 
474 bool check_bin(bool result, const char* file, size_t line, const char* expr,
475                const std::string& lhs, const std::string& rhs);
476 
477 } // namespace detail
478 } // namespace caf::test
479 
480 // on the global namespace so that it can hidden via namespace-scoping
481 using caf_test_case_auto_fixture = caf::test::dummy_fixture;
482 
483 #define CAF_TEST_PRINT(level, msg, colorcode)                                  \
484   (::caf::test::logger::instance().level()                                     \
485    << ::caf::term::colorcode << "  -> " << ::caf::term::reset                  \
486    << ::caf::test::logger::stream::reset_flags_t{} << msg << " [line "         \
487    << __LINE__ << "]\n")
488 
489 #define CAF_TEST_PRINT_ERROR(msg) CAF_TEST_PRINT(info, msg, red)
490 #define CAF_TEST_PRINT_INFO(msg) CAF_TEST_PRINT(info, msg, yellow)
491 #define CAF_TEST_PRINT_VERBOSE(msg) CAF_TEST_PRINT(verbose, msg, yellow)
492 
493 #define CAF_PASTE_CONCAT(lhs, rhs) lhs##rhs
494 
495 #define CAF_PASTE(lhs, rhs) CAF_PASTE_CONCAT(lhs, rhs)
496 
497 #define CAF_UNIQUE(name) CAF_PASTE(name, __LINE__)
498 
499 #ifndef CAF_SUITE
500 #  define CAF_SUITE unnamed
501 #endif
502 
503 #define CAF_STR(s) #s
504 
505 #define CAF_XSTR(s) CAF_STR(s)
506 
507 #define CAF_FUNC_EXPR(func, x_expr, y_expr) #func "(" #x_expr ", " #y_expr ")"
508 
509 #define CAF_ERROR(msg)                                                         \
510   do {                                                                         \
511     CAF_TEST_PRINT_ERROR(msg);                                                 \
512     ::caf::test::engine::current_test()->fail(false);                          \
513     ::caf::test::engine::last_check_file(__FILE__);                            \
514     ::caf::test::engine::last_check_line(__LINE__);                            \
515   } while (false)
516 
517 #define CAF_CHECK(...)                                                         \
518   [](bool expr_result) {                                                       \
519     return ::caf::test::detail::check_un(expr_result, __FILE__, __LINE__,      \
520                                          #__VA_ARGS__);                        \
521   }(static_cast<bool>(__VA_ARGS__))
522 
523 #define CAF_CHECK_FUNC(func, x_expr, y_expr)                                   \
524   [](auto&& x_val, auto&& y_val) {                                             \
525     func comparator;                                                           \
526     auto caf_check_res                                                         \
527       = ::caf::test::detail::check(::caf::test::engine::current_test(),        \
528                                    __FILE__, __LINE__,                         \
529                                    CAF_FUNC_EXPR(func, x_expr, y_expr), false, \
530                                    comparator(x_val, y_val), x_val, y_val);    \
531     return caf_check_res;                                                      \
532   }(x_expr, y_expr)
533 
534 #define CAF_FAIL(msg)                                                          \
535   do {                                                                         \
536     CAF_TEST_PRINT_ERROR(msg);                                                 \
537     ::caf::test::engine::current_test()->fail(false);                          \
538     ::caf::test::detail::requirement_failed("test failure");                   \
539   } while (false)
540 
541 #define CAF_REQUIRE(...)                                                       \
542   do {                                                                         \
543     auto CAF_UNIQUE(__result)                                                  \
544       = ::caf::test::detail::check(::caf::test::engine::current_test(),        \
545                                    __FILE__, __LINE__, #__VA_ARGS__, false,    \
546                                    static_cast<bool>(__VA_ARGS__));            \
547     if (!CAF_UNIQUE(__result))                                                 \
548       ::caf::test::detail::requirement_failed(#__VA_ARGS__);                   \
549   } while (false)
550 
551 #define CAF_REQUIRE_FUNC(func, x_expr, y_expr)                                 \
552   do {                                                                         \
553     func comparator;                                                           \
554     auto&& x_val___ = x_expr;                                                  \
555     auto&& y_val___ = y_expr;                                                  \
556     auto CAF_UNIQUE(__result) = ::caf::test::detail::check(                    \
557       ::caf::test::engine::current_test(), __FILE__, __LINE__,                 \
558       CAF_FUNC_EXPR(func, x_expr, y_expr), false,                              \
559       comparator(x_val___, y_val___), x_val___, y_val___);                     \
560     if (!CAF_UNIQUE(__result))                                                 \
561       ::caf::test::detail::requirement_failed(                                 \
562         CAF_FUNC_EXPR(func, x_expr, y_expr));                                  \
563   } while (false)
564 
565 #define CAF_TEST_IMPL(name, disabled_by_default)                               \
566   namespace {                                                                  \
567   struct CAF_UNIQUE(test) : caf_test_case_auto_fixture {                       \
568     void run_test_impl();                                                      \
569   };                                                                           \
570   ::caf::test::detail::adder<::caf::test::test_impl<CAF_UNIQUE(test)>>         \
571     CAF_UNIQUE(a){CAF_XSTR(CAF_SUITE), CAF_XSTR(name), disabled_by_default};   \
572   }                                                                            \
573   void CAF_UNIQUE(test)::run_test_impl()
574 
575 #define CAF_TEST(name) CAF_TEST_IMPL(name, false)
576 
577 #define CAF_TEST_DISABLED(name) CAF_TEST_IMPL(name, true)
578 
579 #define CAF_TEST_FIXTURE_SCOPE(scope_name, fixture_name)                       \
580   namespace scope_name {                                                       \
581   using caf_test_case_auto_fixture = fixture_name;
582 
583 #define CAF_TEST_FIXTURE_SCOPE_END() } // namespace <scope_name>
584 
585 // -- Convenience macros -------------------------------------------------------
586 
587 #define CAF_MESSAGE(msg)                                                       \
588   do {                                                                         \
589     CAF_LOG_INFO(msg);                                                         \
590     CAF_TEST_PRINT_VERBOSE(msg);                                               \
591   } while (false)
592 
593 // -- CAF_CHECK* predicate family ----------------------------------------------
594 
595 #define CAF_CHECK_EQUAL(x_expr, y_expr)                                        \
596   [](const auto& x_val, const auto& y_val) {                                   \
597     return ::caf::test::detail::check_bin(                                     \
598       x_val == y_val, __FILE__, __LINE__, #x_expr " == " #y_expr,              \
599       caf::detail::stringification_inspector::render(x_val),                   \
600       caf::detail::stringification_inspector::render(y_val));                  \
601   }(x_expr, y_expr)
602 
603 #define CAF_CHECK_NOT_EQUAL(x_expr, y_expr)                                    \
604   [](const auto& x_val, const auto& y_val) {                                   \
605     return ::caf::test::detail::check_bin(                                     \
606       x_val != y_val, __FILE__, __LINE__, #x_expr " != " #y_expr,              \
607       caf::detail::stringification_inspector::render(x_val),                   \
608       caf::detail::stringification_inspector::render(y_val));                  \
609   }(x_expr, y_expr)
610 
611 #define CAF_CHECK_LESS(x_expr, y_expr)                                         \
612   [](const auto& x_val, const auto& y_val) {                                   \
613     return ::caf::test::detail::check_bin(                                     \
614       x_val < y_val, __FILE__, __LINE__, #x_expr " < " #y_expr,                \
615       caf::detail::stringification_inspector::render(x_val),                   \
616       caf::detail::stringification_inspector::render(y_val));                  \
617   }(x_expr, y_expr)
618 
619 #define CAF_CHECK_NOT_LESS(x_expr, y_expr)                                     \
620   [](const auto& x_val, const auto& y_val) {                                   \
621     return ::caf::test::detail::check_bin(                                     \
622       !(x_val < y_val), __FILE__, __LINE__, "not " #x_expr " < " #y_expr,      \
623       caf::detail::stringification_inspector::render(x_val),                   \
624       caf::detail::stringification_inspector::render(y_val));                  \
625   }(x_expr, y_expr)
626 
627 #define CAF_CHECK_LESS_OR_EQUAL(x_expr, y_expr)                                \
628   [](const auto& x_val, const auto& y_val) {                                   \
629     return ::caf::test::detail::check_bin(                                     \
630       x_val <= y_val, __FILE__, __LINE__, #x_expr " <= " #y_expr,              \
631       caf::detail::stringification_inspector::render(x_val),                   \
632       caf::detail::stringification_inspector::render(y_val));                  \
633   }(x_expr, y_expr)
634 
635 #define CAF_CHECK_NOT_LESS_OR_EQUAL(x_expr, y_expr)                            \
636   [](const auto& x_val, const auto& y_val) {                                   \
637     return ::caf::test::detail::check_bin(                                     \
638       !(x_val <= y_val), __FILE__, __LINE__, "not " #x_expr " <= " #y_expr,    \
639       caf::detail::stringification_inspector::render(x_val),                   \
640       caf::detail::stringification_inspector::render(y_val));                  \
641   }(x_expr, y_expr)
642 
643 #define CAF_CHECK_GREATER(x_expr, y_expr)                                      \
644   [](const auto& x_val, const auto& y_val) {                                   \
645     return ::caf::test::detail::check_bin(                                     \
646       x_val > y_val, __FILE__, __LINE__, #x_expr " > " #y_expr,                \
647       caf::detail::stringification_inspector::render(x_val),                   \
648       caf::detail::stringification_inspector::render(y_val));                  \
649   }(x_expr, y_expr)
650 
651 #define CAF_CHECK_NOT_GREATER(x_expr, y_expr)                                  \
652   [](const auto& x_val, const auto& y_val) {                                   \
653     return ::caf::test::detail::check_bin(                                     \
654       !(x_val > y_val), __FILE__, __LINE__, "not " #x_expr " > " #y_expr,      \
655       caf::detail::stringification_inspector::render(x_val),                   \
656       caf::detail::stringification_inspector::render(y_val));                  \
657   }(x_expr, y_expr)
658 
659 #define CAF_CHECK_GREATER_OR_EQUAL(x_expr, y_expr)                             \
660   [](const auto& x_val, const auto& y_val) {                                   \
661     return ::caf::test::detail::check_bin(                                     \
662       x_val >= y_val, __FILE__, __LINE__, #x_expr " >= " #y_expr,              \
663       caf::detail::stringification_inspector::render(x_val),                   \
664       caf::detail::stringification_inspector::render(y_val));                  \
665   }(x_expr, y_expr)
666 
667 #define CAF_CHECK_NOT_GREATER_OR_EQUAL(x_expr, y_expr)                         \
668   [](const auto& x_val, const auto& y_val) {                                   \
669     return ::caf::test::detail::check_bin(                                     \
670       !(x_val >= y_val), __FILE__, __LINE__, "not " #x_expr " >= " #y_expr,    \
671       caf::detail::stringification_inspector::render(x_val),                   \
672       caf::detail::stringification_inspector::render(y_val));                  \
673   }(x_expr, y_expr)
674 
675 // -- CAF_CHECK* predicate family ----------------------------------------------
676 
677 #define CAF_REQUIRE_EQUAL(x, y) CAF_REQUIRE_FUNC(::caf::test::equal_to, x, y)
678 
679 #define CAF_REQUIRE_NOT_EQUAL(x, y)                                            \
680   CAF_REQUIRE_FUNC(::caf::test::not_equal_to, x, y)
681 
682 #define CAF_REQUIRE_LESS(x, y) CAF_REQUIRE_FUNC(::caf::test::less_than, x, y)
683 
684 #define CAF_REQUIRE_NOT_LESS(x, y)                                             \
685   CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::less_than>, x, y)
686 
687 #define CAF_REQUIRE_LESS_OR_EQUAL(x, y)                                        \
688   CAF_REQUIRE_FUNC(::caf::test::less_than_or_equal, x, y)
689 
690 #define CAF_REQUIRE_NOT_LESS_OR_EQUAL(x, y)                                    \
691   CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::less_than_or_equal>, x, y)
692 
693 #define CAF_REQUIRE_GREATER(x, y)                                              \
694   CAF_REQUIRE_FUNC(::caf::test::greater_than, x, y)
695 
696 #define CAF_REQUIRE_NOT_GREATER(x, y)                                          \
697   CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::greater_than>, x, y)
698 
699 #define CAF_REQUIRE_GREATER_OR_EQUAL(x, y)                                     \
700   CAF_REQUIRE_FUNC(::caf::test::greater_than_or_equal, x, y)
701 
702 #define CAF_REQUIRE_NOT_GREATER_OR_EQUAL(x, y)                                 \
703   CAF_REQUIRE_FUNC(::caf::test::negated<::caf::test::greater_than_or_equal>,   \
704                    x, y)
705