#ifndef GUF_TEST_HPP #define GUF_TEST_HPP #include #include #include #include #include #include extern "C" { #include "guf_common.h" #include "guf_assert.h" } #define TEST_CHECK(COND) (check((COND), GUF_STRINGIFY(COND), __LINE__, __FILE__)) struct Test { private: std::chrono::steady_clock::time_point time_start, time_end; protected: std::stack check_name_stack; std::string full_check_name = ""; void push_check_name(const std::string& check_name) { check_name_stack.push(check_name); full_check_name = full_check_name + "::" + check_name; } void pop_check_name() { const size_t sep_idx = full_check_name.rfind("::"); GUF_ASSERT_RELEASE(sep_idx != std::string::npos); full_check_name = full_check_name.substr(0, sep_idx); check_name_stack.pop(); } bool check(bool cond, std::string_view msg, size_t line, std::string_view fname) { if (!cond) { std::cerr << name << full_check_name << ": "; std::cerr << "FAILED CHECK (" << msg << ") on line " << line << " in file " << fname << "\n"; \ ++num_failed_checks; } else { ++num_passed_checks; } return cond; } public: const std::string name {}; std::chrono::duration runtime_ms {0}; bool passed {false}, done {false}; size_t num_failed_checks {0}, num_passed_checks {0}; Test(const std::string& nm) : name{nm} {} virtual ~Test() = default; size_t total_checks() const { return num_passed_checks + num_failed_checks; } virtual void run() = 0; void before_run() { time_start = std::chrono::steady_clock::now(); } void after_run() { done = true; passed = (num_failed_checks == 0); time_end = std::chrono::steady_clock::now(); runtime_ms = std::chrono::duration_cast(time_end - time_start); } friend std::ostream& operator<<(std::ostream &os, const Test& tst) { std::ios_base::fmtflags os_flags = os.flags(); std::streamsize os_precision = os.precision(); os << tst.name << ": " << (tst.passed ? "PASS" : "FAIL"); os << std::fixed << std::setprecision(2); os << " (" << tst.num_passed_checks << "/" << tst.total_checks() << ") in " << tst.runtime_ms.count() << " ms"; os.precision(os_precision); os.flags(os_flags); return os; } }; template<> struct std::hash> { std::size_t operator()(const std::unique_ptr& test) const { if (test.get() == nullptr) { return 0; } return std::hash()(test.get()->name); } }; #endif