Files
aee-arcade/tests/test_tilemap_writer.cpp
T

178 lines
6.1 KiB
C++

// Smoke test per a TilemapWriter::saveDataBlock. Stand-alone: no obre
// finestra, no necessita SDL. Comprova que:
//
// T1 round-trip pur — escriure el mateix `data:` no canvia el fitxer.
// T2 modificació — canviar un tile només toca el bloc `data:` i la
// resta del fitxer queda byte a byte idèntica.
// T3 format — el bloc generat respecta ` - [v0, v1, ...]` amb
// alineació a dreta (màx. amplada) i `]\n` final.
// T4 error path — fitxer sense `data:` a columna 0 → retorna false
// i no escriu.
//
// Execució: `./build/test_tilemap_writer` o via `ctest`.
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include "game/map/tilemap_writer.hpp"
namespace {
// El fixture usa max_width=3 (valor 230) perquè coincideixi amb el format
// que el writer emet per a la mateixa graella: així T1 pot comparar byte
// a byte el fitxer original amb el reescrit amb la mateixa data.
constexpr const char* FIXTURE =
"# Capçalera amb comentaris\n"
"tileset: data/tilesets/tomb.gif\n"
"tile_size: 16\n"
"origin: [-8, 0]\n"
"\n"
"# Bloc geometry preservat\n"
"geometry:\n"
" grid_cols: 4\n"
" grid_rows: 5\n"
"\n"
"data:\n"
" - [ 1, 2, 3]\n"
" - [ 4,230, 6]\n"
" - [ 7, 8, 9]\n"
"\n"
"# Epíleg amb comentari\n"
"entities:\n"
" - type: torch\n"
" position: [4, 2]\n";
auto readFile(const std::string& path) -> std::string {
std::ifstream in(path, std::ios::binary);
std::ostringstream ss;
ss << in.rdbuf();
return ss.str();
}
void writeFile(const std::string& path, const std::string& content) {
std::ofstream out(path, std::ios::binary | std::ios::trunc);
out.write(content.data(), static_cast<std::streamsize>(content.size()));
}
auto tmpPath(const char* name) -> std::string {
auto p = std::filesystem::temp_directory_path() / name;
return p.string();
}
// Extrau el substring entre "data:\n" i la pròxima línia buida o EOF.
// Útil per aïllar el bloc `data:` i comparar preàmbul/epíleg a banda.
auto splitAroundData(const std::string& content) -> std::tuple<std::string, std::string, std::string> {
const auto DATA_LINE = content.find("\ndata:\n");
assert(DATA_LINE != std::string::npos);
const auto DATA_START = DATA_LINE + std::string("\ndata:\n").size();
auto block_end = DATA_START;
while (block_end < content.size()) {
const char C = content[block_end];
if (C != ' ' && C != '\t') { break; }
const auto NL = content.find('\n', block_end);
block_end = (NL == std::string::npos) ? content.size() : NL + 1;
}
return {
content.substr(0, DATA_START),
content.substr(DATA_START, block_end - DATA_START),
content.substr(block_end),
};
}
void T1_roundTrip() {
const auto PATH = tmpPath("aee_test_tw_t1.yaml");
writeFile(PATH, FIXTURE);
const std::vector<std::vector<int>> data = {
{1, 2, 3}, {4, 230, 6}, {7, 8, 9}
};
const bool OK = TilemapWriter::saveDataBlock(PATH, data);
assert(OK);
const auto AFTER = readFile(PATH);
assert(AFTER == FIXTURE);
std::filesystem::remove(PATH);
std::printf("T1 round-trip: OK\n");
}
void T2_modificacio() {
const auto PATH = tmpPath("aee_test_tw_t2.yaml");
writeFile(PATH, FIXTURE);
// Dades distintes al fixture però mantenint max_width=3 (pel 231).
const std::vector<std::vector<int>> data = {
{10, 20, 30}, {40, 231, 60}, {70, 80, 90}
};
const bool OK = TilemapWriter::saveDataBlock(PATH, data);
assert(OK);
const auto AFTER = readFile(PATH);
const auto [PRE_BEFORE, DATA_BEFORE, POST_BEFORE] = splitAroundData(FIXTURE);
const auto [PRE_AFTER, DATA_AFTER, POST_AFTER] = splitAroundData(AFTER);
// Preàmbul i epíleg byte a byte idèntics.
assert(PRE_BEFORE == PRE_AFTER);
assert(POST_BEFORE == POST_AFTER);
// El bloc data ha canviat.
assert(DATA_BEFORE != DATA_AFTER);
// Valor nou present.
assert(AFTER.find("231") != std::string::npos);
std::filesystem::remove(PATH);
std::printf("T2 modificació: OK\n");
}
void T3_format() {
const auto PATH = tmpPath("aee_test_tw_t3.yaml");
writeFile(PATH, FIXTURE);
// max_width=3 → valors a 3 chars, separats per ','.
const std::vector<std::vector<int>> data = {
{1, 2, 3},
{230, 1, 1},
{1, 1, 1}
};
const bool OK = TilemapWriter::saveDataBlock(PATH, data);
assert(OK);
const auto AFTER = readFile(PATH);
// Primera fila: " - [ 1, 2, 3]\n"
assert(AFTER.find(" - [ 1, 2, 3]\n") != std::string::npos);
// Fila amb valor de 3 chars: " - [230, 1, 1]\n"
assert(AFTER.find(" - [230, 1, 1]\n") != std::string::npos);
std::filesystem::remove(PATH);
std::printf("T3 format: OK\n");
}
void T4_errorPath() {
const auto PATH = tmpPath("aee_test_tw_t4.yaml");
// Fitxer sense "data:" a columna 0.
writeFile(PATH, "tileset: x\ntile_size: 16\n# sense data\n");
const auto BEFORE = readFile(PATH);
const std::vector<std::vector<int>> data = {{1, 2, 3}};
const bool OK = TilemapWriter::saveDataBlock(PATH, data);
assert(!OK);
const auto AFTER = readFile(PATH);
assert(AFTER == BEFORE); // fitxer intacte
std::filesystem::remove(PATH);
std::printf("T4 error path: OK\n");
}
} // namespace
auto main() -> int {
T1_roundTrip();
T2_modificacio();
T3_format();
T4_errorPath();
std::printf("tilemap_writer: tots els tests OK\n");
return 0;
}