libguf/src/test/test.hpp
2025-12-21 18:17:22 +01:00

109 lines
2.8 KiB
C++
Executable File

#ifndef GUF_TEST_HPP
#define GUF_TEST_HPP
#include <stack>
#include <string>
#include <cstdio>
#include <iostream>
#include <chrono>
#include <iomanip>
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<std::string> 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<float, std::milli> 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<decltype(runtime_ms)>(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::unique_ptr<Test>> {
std::size_t operator()(const std::unique_ptr<Test>& test) const
{
if (test.get() == nullptr) {
return 0;
}
return std::hash<std::string>()(test.get()->name);
}
};
#endif