Files
EnderTheCoder 23f8be394c feat(assets): read retail assets through the vendored libra3assets library
Vendor libra3assets (C++26 modules: BIG4, RefPack, BinaryAsset, CSF, CkMp map)
under third_party/ and add the ra3_assets target. ra3.fs, ra3.map and ra3.terrain
become thin adapters over it:

- ra3.fs delegates RefPack and the BIG4 index/payload reads (index-only, payloads
  read on demand).
- ra3.map decodes ObjectsList and CSF via map_document/csf_table; starts come from
  player_starts(), replacing the off-by-one whole-buffer scan.
- ra3.terrain takes the CkMp container and HeightMapData from map_document, with
  BlendTileData (not modelled by the library) parsed from the chunk payload.

Adds an opt-in real-asset check (OPENRA3_TEST_ASSETS) plus the library's own unit
suite as ra3assets_unit.
2026-09-29 23:58:13 +08:00

84 lines
2.8 KiB
C++

#ifndef RA3TEST_HPP
#define RA3TEST_HPP
// A tiny dependency-free test harness (registry + CHECK macros). Each unit-test
// executable compiles test_main.cpp and links the sources under test.
#include <functional>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace ra3test {
struct test_case {
std::string name;
std::function<void()> fn;
};
inline auto registry() -> std::vector<test_case> & {
static std::vector<test_case> tests;
return tests;
}
inline auto failure_count() -> int & {
static int count = 0;
return count;
}
struct registrar {
registrar(std::string name, std::function<void()> fn) { registry().push_back({std::move(name), std::move(fn)}); }
};
inline auto report_failure(const std::string &expr, const std::string &file, int line) -> void {
++failure_count();
std::cerr << " FAIL " << file << ':' << line << " " << expr << '\n';
}
inline auto check(bool condition, const std::string &expr, const std::string &file, int line) -> void {
if (!condition) report_failure(expr, file, line);
}
inline auto run_all() -> int {
int passed = 0;
for (auto &test: registry()) {
const int before = failure_count();
std::cout << "[ RUN ] " << test.name << '\n';
try {
test.fn();
} catch (const std::exception &exc) {
report_failure(std::string("uncaught exception: ") + exc.what(), __FILE__, __LINE__);
}
if (failure_count() == before) {
++passed;
std::cout << "[ OK ] " << test.name << '\n';
}
}
std::cout << "\n" << passed << '/' << registry().size() << " tests passed, " << failure_count() << " failure(s)\n";
return failure_count() == 0 ? 0 : 1;
}
} // namespace ra3test
#define TEST(name) \
static void name(); \
static ::ra3test::registrar ra3test_reg_##name(#name, name); \
static void name()
#define CHECK(cond) ::ra3test::check((cond), #cond, __FILE__, __LINE__)
#define CHECK_EQ(a, b) ::ra3test::check_eq((a), (b), #a, #b, __FILE__, __LINE__)
namespace ra3test {
template<typename A, typename B>
auto check_eq(const A &a, const B &b, const std::string &ea, const std::string &eb, const std::string &file, int line) -> void {
if (!(a == b)) {
std::ostringstream os;
os << ea << " == " << eb;
report_failure(os.str(), file, line);
}
}
}
#endif // RA3TEST_HPP