// // 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 #include "test_ret_val.h" #include "CompileOptional.h" template struct quick_test_def; enum class EvalFlag { RUNTIME, CONSTEVAL, RUNTIME_CONSTEVAL }; class test_definition { public: [[nodiscard]] virtual constexpr ret_val_s evaluate() const = 0; [[nodiscard]] virtual const char *name() const = 0; [[nodiscard]] virtual EvalFlag evalFlag() const = 0; [[nodiscard]] virtual const ret_val_s &c_res() const = 0; }; template class test_definition_impl : public test_definition { public: using FuncType = Func; static constexpr std::size_t ARG_SIZE = sizeof...(Args); constexpr test_definition_impl(const char *name, Func func, EvalFlag evalFlag, Args ...args) : _name(name), _func(func), _evalFlag(evalFlag), _args(std::make_tuple(std::forward(args)...)), _c_res(_name, ReturnCode::FAILED, "Could not be evaluated at compile time") { if consteval { if (evalFlag == EvalFlag::RUNTIME_CONSTEVAL || evalFlag == EvalFlag::CONSTEVAL) { _c_res = evaluate(); } } } [[nodiscard]] constexpr ret_val_s evaluate() const override { return std::apply(_func, _args); } [[nodiscard]] const char *name() const override { return _name; } [[nodiscard]] EvalFlag evalFlag() const override { return _evalFlag; } [[nodiscard]] const ret_val_s &c_res() const override{ return _c_res; } private: const char *_name; EvalFlag _evalFlag; Func _func; std::tuple _args; ret_val_s _c_res; }; template requires (sizeof...(TestDefs) == 0 || (std::derived_from && ...)) class test_suite { public: static constexpr std::size_t TEST_NR = sizeof...(TestDefs); constexpr test_suite(const char *name, std::tuple tests) : _name(name), _tests(tests) {} int run() const { auto test_arr = expand_test_tuple(_tests, std::make_index_sequence()); std::cout << test_arr.size() << "\n"; std::cout << "--------------\n"; for (auto [i, test_ref] : std::ranges::views::enumerate(test_arr)) { const 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; std::string ret_exc_str; try { ret = test.evaluate(); } catch (std::exception& e) { ret_exc_str = e.what(); ret = ret_val_s(test.name(), ReturnCode::FAILED, ret_exc_str.c_str()); } std::cout << "Result of Runtime Evaluation of Test \"" << ret.test_name << "\" (number: " << i << "): " << (ret.val == ReturnCode::PASSED ? "PASSED" : "FAILED") << "\n" << "\t" << ret.msg << "\n"; } if (test.evalFlag() == EvalFlag::CONSTEVAL || test.evalFlag() == EvalFlag::RUNTIME_CONSTEVAL) { const ret_val_s& ret = test.c_res(); std::cout << "Result of Consteval Evaluation of Test \"" << ret.test_name << "\" (number: " << i << "): " << (ret.val == ReturnCode::PASSED ? "PASSED" : "FAILED") << "\n" << "\t" << ret.msg << "\n"; } } return 0; } private: const char *_name; std::tuple _tests; template static constexpr auto expand_test_tuple(const auto &tests, std::index_sequence) { return std::array { std::reference_wrapper( dynamic_cast(std::get(tests)) )... }; } friend class quick_test_def>; }; template struct quick_test_def { Suite current; template constexpr auto operator()(const char *name, Func func, Args... args) { return operator()(name, func, EvalFlag::RUNTIME, std::forward(args)...); } template constexpr auto operator()(const char *name, Func func, EvalFlag evalFlag, Args... args) { auto test = test_definition_impl(name, func, evalFlag, args...); auto new_suite = test_suite(current._name, std::tuple_cat(current._tests, std::make_tuple(test))); return quick_test_def { new_suite }; } constexpr operator Suite() { return current; } }; template test_suite(quick_test_def>) -> test_suite; constexpr auto define_tests(const char *name) { return quick_test_def { test_suite<>{ name, std::make_tuple() } }; } #endif //CONST_CONTAINER_TEST_DEFINE_TEST_HPP_