Compare commits

...

4 Commits

Author SHA1 Message Date
Axel Meyer
aaeb8d3650 Bump lib-core.maps-test to v0.4.0 and pin maps dep to 0.4.0
Test-lib follows the lib it exercises; the new asserts in init.lua
exercise the v0.4.0 write APIs that did not exist in 0.3.0.
2026-05-24 00:51:12 +02:00
Axel Meyer
e082a39596 Add save_to_disk round-trip and determinism asserts
Verifies the saved JSON is readable, that two successive saves
produce byte-identical output, and that re-loading the saved file
recovers the same cell values that were written before saving.
Uses engine.module.dir_of to write the round-trip fixture to the
lib maps/ directory (asset sandbox rejects absolute temp paths).
2026-05-24 00:43:31 +02:00
Axel Meyer
e399f6c6f8 Add asserts for set_roof write API
Covers value validation (rejects 2), bounds rejection, and the
basic call paths that set up roof state for the save-roundtrip
test in the next commit.
2026-05-24 00:34:18 +02:00
Axel Meyer
8453b1fc8f Add asserts for set_cell_gid write API
Covers happy-path round-trip, bounds rejection (positive and negative),
unknown-layer rejection, and on-demand layer allocation.
2026-05-24 00:25:12 +02:00
3 changed files with 129 additions and 2 deletions

111
init.lua
View File

@@ -246,6 +246,117 @@ function M.run_tests(ctx)
local list = maps.list()
engine.test.equals(#list, 2, "maps.list returns 2 registered maps")
-- ---------- v0.4.0: Write APIs — set_cell_gid ----------
-- Build a fresh v2 map for write tests (separate from earlier load-based tests).
-- maps.create only handles the v1 path (requires tilemap_table); use maps.load
-- with a fixture file for the v2 path instead.
local write_id = maps.load("maps/write_test.map.json")
maps.set_current(write_id)
-- Happy path
local gid_painted = maps.encode_gid(0, 1, 0)
maps.set_cell_gid("surface", 1, 2, gid_painted)
engine.test.equals(maps.cell_gid("surface", 1, 2), gid_painted,
"set_cell_gid writes the cell that cell_gid reads back")
-- Bounds-error: x >= w
local ok1, err1 = pcall(function() maps.set_cell_gid("surface", 4, 0, gid_painted) end)
engine.test.assert(not ok1, "set_cell_gid rejects x out of bounds")
engine.test.assert(err1 and err1:find("bounds"), "set_cell_gid bounds-error mentions 'bounds'")
-- Bounds-error: negative coord
local ok2 = pcall(function() maps.set_cell_gid("surface", -1, 0, gid_painted) end)
engine.test.assert(not ok2, "set_cell_gid rejects negative x")
-- Unknown-layer error
local ok3, err3 = pcall(function() maps.set_cell_gid("bogus_layer", 0, 0, gid_painted) end)
engine.test.assert(not ok3, "set_cell_gid rejects unknown layer")
engine.test.assert(err3 and err3:find("layer"), "unknown-layer error mentions 'layer'")
-- On-demand layer allocation
engine.test.assert(maps.cell_gid("wall", 0, 0) == 0,
"wall layer empty before any write")
maps.set_cell_gid("wall", 2, 2, gid_painted)
engine.test.equals(maps.cell_gid("wall", 2, 2), gid_painted,
"set_cell_gid auto-allocates the wall layer")
engine.test.equals(maps.cell_gid("wall", 0, 0), 0,
"auto-allocated layer initialised to all-zero except the written cell")
-- ---------- v0.4.0: set_roof ----------
-- Roof should be nil before any write. No direct cell_roof reader exists yet
-- (out of scope for v0.4.0). Verify behaviourally via JSON round-trip in Task 3.
-- Happy path: set roof cells, no assertion on read since no public reader
maps.set_roof(1, 1, 1)
maps.set_roof(1, 1, 0) -- toggle back
maps.set_roof(2, 2, 1) -- leave this one set for Task 3's round-trip test
-- Value validation
local ok_v, err_v = pcall(function() maps.set_roof(0, 0, 2) end)
engine.test.assert(not ok_v, "set_roof rejects value=2")
engine.test.assert(err_v and err_v:find("0.*1"), "set_roof error mentions 0 or 1")
-- Bounds rejection
local ok_b = pcall(function() maps.set_roof(4, 0, 1) end)
engine.test.assert(not ok_b, "set_roof rejects x out of bounds")
-- ---------- v0.4.0: save_to_disk ----------
-- Map state from Task 1+2: surface(1,2) = gid_painted, wall(2,2) = gid_painted,
-- roof(2,2) = 1. Save and re-load to verify round-trip.
-- Re-establish deterministic state (defensive — earlier asserts may have toggled).
maps.set_cell_gid("surface", 1, 2, gid_painted)
maps.set_cell_gid("wall", 2, 2, gid_painted)
maps.set_roof(2, 2, 1)
-- Pick a temp path. Use TEMP env var for Windows compatibility; fall back to
-- os.tmpname() on POSIX (where it returns a writable path).
local tmp_dir = os.getenv("TEMP") or os.getenv("TMP") or "/tmp"
-- Unique suffix via os.clock() + os.time() to avoid collisions.
local tmp_suffix = string.format("sporel_maps_test_%d_%d", os.time(), math.floor(os.clock() * 1e6) % 1000000)
local out_path = tmp_dir .. "/" .. tmp_suffix .. "_1.json"
local out_path_2 = tmp_dir .. "/" .. tmp_suffix .. "_2.json"
local rl_path = tmp_dir .. "/" .. tmp_suffix .. "_rl.json"
maps.save_to_disk("write_test", out_path)
-- Re-read raw bytes for byte-determinism test
local f1 = io.open(out_path, "r")
engine.test.assert(f1 ~= nil, "save_to_disk wrote a readable file")
local bytes_1 = f1:read("*a")
f1:close()
-- Save a second time; bytes must match exactly
maps.save_to_disk("write_test", out_path_2)
local f2 = io.open(out_path_2, "r")
local bytes_2 = f2:read("*a")
f2:close()
engine.test.equals(bytes_1, bytes_2,
"save_to_disk is byte-deterministic across calls")
-- Verify JSON has expected keys
engine.test.assert(bytes_1:find('"schema_version"') ~= nil, "saved file has schema_version key")
engine.test.assert(bytes_1:find('"surface"') ~= nil, "saved file has surface key")
engine.test.assert(bytes_1:find('"wall"') ~= nil, "saved file has wall key (auto-allocated)")
engine.test.assert(bytes_1:find('"roof"') ~= nil, "saved file has roof key (set in Task 2)")
-- Re-load (with patched id to avoid registry collision).
-- engine.asset.load_json only accepts relative (sandboxed) paths, so write
-- the round-trip file into the maps-test lib's own maps/ directory where
-- the asset resolver can find it by relative path.
local lib_dir = engine.module.dir_of("lib-core.maps-test")
local rewritten = bytes_1:gsub('"id"%s*:%s*"write_test"', '"id":"write_test_reloaded"')
local abs_rl_path = lib_dir .. "/maps/write_test_reloaded.map.json"
local rf = io.open(abs_rl_path, "w"); rf:write(rewritten); rf:close()
maps.load("maps/write_test_reloaded.map.json")
engine.test.equals(maps.cell_gid("surface", 1, 2, "write_test_reloaded"), gid_painted,
"save round-trip preserves surface cell")
engine.test.equals(maps.cell_gid("wall", 2, 2, "write_test_reloaded"), gid_painted,
"save round-trip preserves wall cell")
-- Cleanup
os.remove(out_path); os.remove(out_path_2); os.remove(abs_rl_path)
engine.exit(engine.test.failures())
end

View File

@@ -1,9 +1,9 @@
{
"id": "lib-core.maps-test",
"role": "test",
"version": "0.3.0",
"version": "0.4.0",
"api_min": "0.1",
"deps": [
{"id": "lib-core.maps", "version": "0.3.0"}
{"id": "lib-core.maps", "version": "0.4.0"}
]
}

16
maps/write_test.map.json Normal file
View File

@@ -0,0 +1,16 @@
{
"schema_version": 2,
"id": "write_test",
"size": { "w": 4, "h": 4 },
"atlases": ["demo_tilemap"],
"layers": {
"surface": {
"tiles": [
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
]
}
}
}