Files
OpenRA3/tests/ra3_tests.cpp
T

148 lines
6.6 KiB
C++

#include <cstdint>
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
import ra3;
namespace {
int failures = 0;
auto check(bool condition, std::string_view what) -> void {
if (!condition) {
std::printf("FAIL: %.*s\n", static_cast<int>(what.size()), what.data());
++failures;
}
}
auto append_be32(std::vector<std::uint8_t> &out, std::uint32_t value) -> void {
out.push_back(static_cast<std::uint8_t>(value >> 24U));
out.push_back(static_cast<std::uint8_t>(value >> 16U));
out.push_back(static_cast<std::uint8_t>(value >> 8U));
out.push_back(static_cast<std::uint8_t>(value));
}
auto append_le32(std::vector<std::uint8_t> &out, std::uint32_t value) -> void {
out.push_back(static_cast<std::uint8_t>(value));
out.push_back(static_cast<std::uint8_t>(value >> 8U));
out.push_back(static_cast<std::uint8_t>(value >> 16U));
out.push_back(static_cast<std::uint8_t>(value >> 24U));
}
/** Build a one-entry BIG4 archive and return its path. */
auto make_test_archive(const std::filesystem::path &path) -> void {
const std::string name = "data\\hello.txt";
const std::string payload = "hello big4";
std::vector<std::uint8_t> index;
append_be32(index, 0); // offset, patched below
append_be32(index, static_cast<std::uint32_t>(payload.size()));
index.insert(index.end(), name.begin(), name.end());
index.push_back(0);
const std::uint32_t payload_offset = 16U + static_cast<std::uint32_t>(index.size());
std::vector<std::uint8_t> index_patched;
append_be32(index_patched, payload_offset);
index_patched.insert(index_patched.end(), index.begin() + 4, index.end());
std::vector<std::uint8_t> file;
file.insert(file.end(), {'B', 'I', 'G', '4'});
append_le32(file, static_cast<std::uint32_t>(16U + index_patched.size() + payload.size()));
append_be32(file, 1U);
append_le32(file, 0U);
file.insert(file.end(), index_patched.begin(), index_patched.end());
file.insert(file.end(), payload.begin(), payload.end());
std::ofstream out(path, std::ios::binary);
out.write(reinterpret_cast<const char *>(file.data()), static_cast<std::streamsize>(file.size()));
}
}
auto main() -> int {
using namespace ra3;
check(core::make_name_key("Tank") == core::make_name_key("tank"), "name keys are case-insensitive");
core::message_stream stream;
stream.append(core::message_id::begin_block);
stream.append(core::message_id::new_game);
check(stream.size() == 2U, "message stream counts entries");
check(stream.pop()->id == core::message_id::begin_block, "message stream is FIFO");
logic::game_logic simulation;
simulation.prepare_new_game(7U);
check(simulation.starting(), "prepare raises the starting flag");
simulation.start_new_game();
check(!simulation.messages().empty(), "start appends new_game");
simulation.update();
check(simulation.frame() == 1U, "update advances the frame counter");
check(simulation.messages().empty(), "update drains the message stream");
auto &side = simulation.players().add(std::make_unique<logic::player>(0U, "Test"));
side.set_money(250);
side.add_money(-100);
check(side.money() == 150, "money clamps and accumulates");
check(game::to_string(game::faction::soviet) == "Soviet", "faction names round-trip");
// RefPack: a literal-only stream decodes to its payload.
const std::vector<core::uint8> refpack{0x10, 0xFB, 0x00, 0x00, 0x03, 0xFF, 'A', 'B', 'C'};
check(fs::is_refpack(refpack), "literal RefPack stream is recognised");
const auto decoded = fs::refpack_decompress(refpack);
check(decoded.size() == 3U && decoded[0] == 'A' && decoded[2] == 'C', "RefPack literals decode");
check(fs::maybe_decompress(std::vector<core::uint8>{'x', 'y'}).size() == 2U, "non-RefPack passes through");
// BIG4: build and read a synthetic archive.
const auto archive_path = std::filesystem::temp_directory_path() / "openra3_test.big";
make_test_archive(archive_path);
const auto archive = fs::big_archive::open(archive_path);
check(archive.size() == 1U, "BIG4 index has one entry");
check(archive.contains("data\\hello.txt"), "BIG4 entry is indexed");
const auto payload = archive.read("data\\hello.txt");
check(std::string(payload.begin(), payload.end()) == "hello big4", "BIG4 payload reads back");
std::error_code ignored;
std::filesystem::remove(archive_path, ignored);
// TGA: a 2x2 top-origin truecolor image decodes with correct pixels.
std::vector<core::uint8> tga{0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 24, 0x20};
const core::uint8 tga_pixels[] = {0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255};
tga.insert(tga.end(), tga_pixels, tga_pixels + 12);
const auto decoded_tga = render::decode_tga(tga);
check(decoded_tga.width() == 2U && decoded_tga.height() == 2U, "TGA dimensions");
check(decoded_tga.data()[0] == render::argb(255, 0, 0), "TGA top-left is red");
check(decoded_tga.data()[3] == render::argb(255, 255, 255), "TGA bottom-right is white");
const auto bmp = render::encode_bmp(decoded_tga);
check(bmp.size() > 54U && bmp[0] == 'B' && bmp[1] == 'M', "BMP header");
check(bmp[18] == 2U && bmp[19] == 0U && bmp[22] == 2U, "BMP dimensions");
// Compositing keeps the source size and accepts markers.
render::scene_options scene;
scene.world_width = 100.0;
scene.world_height = 100.0;
const std::vector<render::marker> markers{{10.0, 20.0, render::red, 3}};
const auto composed = render::compose(decoded_tga, markers, scene);
check(composed.width() == 2U && composed.height() == 2U, "compose preserves size");
// Skirmish: deterministic, resolves within the frame cap.
skirmish::match_config config;
config.seed = 42U;
const auto starts = skirmish::builtin_start_positions();
auto first = skirmish::skirmish_match::create(config, starts);
const auto first_result = first.run();
auto second = skirmish::skirmish_match::create(config, starts);
const auto second_result = second.run();
check(first_result.decided, "skirmish reaches a decision");
check(first_result.winner == 0 || first_result.winner == 1, "skirmish has a winner");
check(first_result.frames == second_result.frames && first_result.winner == second_result.winner, "skirmish is deterministic");
if (failures == 0) {
std::puts("ra3_tests: OK");
}
return failures == 0 ? 0 : 1;
}