// Minimal test helpers: CHECK macros, a scripted RNG, and a summary exit code. #pragma once #include #include #include #include #include #include "game/sim/rng.h" namespace simtest { inline int& failures() { static int n = 0; return n; } inline int& checks() { static int n = 0; return n; } inline void report(bool ok, const char* expr, const char* file, int line, const std::string& detail) { ++checks(); if (ok) return; ++failures(); std::fprintf(stderr, "FAIL %s:%d %s%s%s\n", file, line, expr, detail.empty() ? "" : " -- ", detail.c_str()); } template inline std::string pair_detail(const A& a, const B& b) { return "got " + std::to_string(a) + ", expected " + std::to_string(b); } inline bool near(double a, double b, double eps = 1e-9) { return std::fabs(a - b) <= eps; } inline int finish(const char* name) { std::printf("%s: %d checks, %d failures\n", name, checks(), failures()); return failures() == 0 ? 0 : 1; } // RNG that replays scripted values and counts draws; exhausting the script is a failure. struct ScriptedRng final : sots::sim::IRandom { std::vector floats; std::vector ints; std::size_t fi = 0, ii = 0; explicit ScriptedRng(std::vector f = {}, std::vector i = {}) : floats(std::move(f)), ints(std::move(i)) {} float NextFloat() override { if (fi >= floats.size()) { std::fprintf(stderr, "ScriptedRng: float script exhausted\n"); ++failures(); return 0.f; } return floats[fi++]; } std::uint32_t NextInt(std::uint32_t n) override { if (n == 0) return 0; if (ii >= ints.size()) { std::fprintf(stderr, "ScriptedRng: int script exhausted\n"); ++failures(); return 0; } return ints[ii++] % n; } std::size_t floatDraws() const { return fi; } std::size_t intDraws() const { return ii; } }; // Each operand is evaluated exactly once (operands may consume RNG draws). template inline void check_eq(const A& a, const B& b, const char* expr, const char* file, int line) { report(a == b, expr, file, line, pair_detail(a, b)); } template inline void check_near(const A& a, const B& b, double eps, const char* expr, const char* file, int line) { report(near(static_cast(a), static_cast(b), eps), expr, file, line, pair_detail(a, b)); } } // namespace simtest #define CHECK(expr) ::simtest::report((expr), #expr, __FILE__, __LINE__, "") #define CHECK_EQ(a, b) ::simtest::check_eq((a), (b), #a " == " #b, __FILE__, __LINE__) #define CHECK_NEAR(a, b, eps) ::simtest::check_near((a), (b), (eps), #a " ~= " #b, __FILE__, __LINE__)