// // Created by Patrick Maschek on 08/04/2024. // #ifndef CONST_CONTAINER_TEST_DEFINE_TEST_HPP_ #define CONST_CONTAINER_TEST_DEFINE_TEST_HPP_ #include #include #include #include #include #include "test_ret_val.h" template struct quick_test_def; enum class EvalFlag { RUNTIME, CONSTEVAL, RUNTIME_CONSTEVAL }; struct test_def_base {}; template struct test_def_func_base : public test_def_base { using FuncType = Func; const char *name; Func func; EvalFlag evalFlag = EvalFlag::RUNTIME; consteval test_def_func_base(const char * name, Func func) : name(name), func(func) {} consteval test_def_func_base(const char * name, Func func, EvalFlag evalFlag) : name(name), func(func), evalFlag(evalFlag) {} virtual constexpr std::invoke_result_t operator()() const { return std::apply(func, std::make_tuple()); } consteval auto run_consteval() const { return operator()(); } }; template struct test_definition : public test_def_func_base { using base = test_def_func_base; static constexpr std::size_t ARG_SIZE = sizeof...(Args); std::tuple args; consteval test_definition(const char *name, Func func, Args ...args) : test_def_func_base(name, func), args(std::make_tuple(args...)) {} consteval test_definition(const char *name, Func func, EvalFlag evalFlag, Args ...args) : test_def_func_base(name, func, evalFlag), args(std::make_tuple(args...)) {} constexpr std::invoke_result_t operator()() const override { return std::apply(base::func, args); } }; template requires (sizeof...(TestDefs) == 0 || (std::derived_from> && ...)) struct test_suite { template using with_added_test_t = test_suite; static constexpr std::size_t TEST_NR = sizeof...(TestDefs); const char *name; std::tuple tests; consteval test_suite(const char *name, std::tuple tests) : name(name), tests(tests) {} template [[nodiscard]] constexpr auto get_tests() const { return get_tests_delegate(std::make_index_sequence()); } template [[nodiscard]] constexpr auto get_tests_delegate(std::index_sequence) const { return std::array { (std::reference_wrapper(std::get(tests)), ...) }; } }; template struct quick_test_def { Suite current; template consteval auto operator()(const char *name, Func func, EvalFlag evalFlag, Args... args) const { auto test = test_definition(name, func, evalFlag, args...); auto suite = test_suite(name, std::tuple_cat(current.tests, std::make_tuple(test))); return quick_test_def { .current = suite }; } constexpr operator Suite() const { return current; } }; template test_suite(quick_test_def>) -> test_suite; consteval auto define_tests(const char *name) { return quick_test_def { test_suite<>{ name, std::make_tuple() } }; } template requires (std::derived_from>) static consteval ret_val_s run_consteval_test(const Test& test) { return test(); } template void evaluate_tests(const test_suite& tests) { for (auto [i, test_ref] : std::ranges::views::enumerate(tests.get_tests())) { auto test = test_ref.get(); std::cout << "Running Test: \"" << test.name << "\"\n"; if (test.evalFlag == EvalFlag::RUNTIME || test.evalFlag == EvalFlag::RUNTIME_CONSTEVAL) { ret_val_s ret = test(); std::cout << "Result of Runtime Evaluation of Test \"" << ret.test_name << "\" (number: " << i << "): " << (ret.val == ReturnCode::PASSED ? "PASSED" : "FAILED") << "\n" << "\t" << ret.msg << "\n"; } std::optional cret; if (test.evalFlag == EvalFlag::CONSTEVAL || test.evalFlag == EvalFlag::RUNTIME_CONSTEVAL) { test.run_consteval(); } if (cret.has_value()) { auto ret = cret.value(); std::cout << "Result of Consteval Evaluation of Test \"" << ret.test_name << "\" (number: " << i << "): " << (ret.val == ReturnCode::PASSED ? "PASSED" : "FAILED") << "\n" << "\t" << ret.msg << "\n"; } } } #endif //CONST_CONTAINER_TEST_DEFINE_TEST_HPP_