183 lines
5.7 KiB
Python
183 lines
5.7 KiB
Python
"""Unit tests for LDtk auto-rule apply (resolve + write to TileLayer)."""
|
|
import mcrfpy
|
|
import sys
|
|
|
|
def test_apply_basic():
|
|
"""Test applying rules to a TileLayer."""
|
|
proj = mcrfpy.LdtkProject("../tests/fixtures/test_project.ldtk")
|
|
rs = proj.ruleset("Terrain")
|
|
ts = proj.tileset("Test_Tileset")
|
|
texture = ts.to_texture()
|
|
|
|
# Create DiscreteMap
|
|
dm = mcrfpy.DiscreteMap((5, 5), fill=0)
|
|
for y in range(5):
|
|
for x in range(5):
|
|
if x == 0 or x == 4 or y == 0 or y == 4:
|
|
dm.set(x, y, 1)
|
|
elif x == 2 and y == 2:
|
|
dm.set(x, y, 3)
|
|
else:
|
|
dm.set(x, y, 2)
|
|
|
|
# Create TileLayer and apply
|
|
layer = mcrfpy.TileLayer(name="terrain", texture=texture, grid_size=(5, 5))
|
|
rs.apply(dm, layer, seed=0)
|
|
|
|
# Verify some tiles were written
|
|
wall_tile = layer.at(0, 0)
|
|
assert wall_tile == 0, f"Expected wall tile 0 at (0,0), got {wall_tile}"
|
|
|
|
floor_tile = layer.at(1, 1)
|
|
assert floor_tile >= 0, f"Expected floor tile at (1,1), got {floor_tile}"
|
|
|
|
# Empty cells (water, value=3) should still be -1 (no rule matches water)
|
|
water_tile = layer.at(2, 2)
|
|
assert water_tile == -1, f"Expected -1 at water (2,2), got {water_tile}"
|
|
|
|
print(f" applied: wall={wall_tile}, floor={floor_tile}, water={water_tile}")
|
|
|
|
def test_apply_preserves_unmatched():
|
|
"""Test that unmatched cells retain their original value."""
|
|
proj = mcrfpy.LdtkProject("../tests/fixtures/test_project.ldtk")
|
|
rs = proj.ruleset("Terrain")
|
|
ts = proj.tileset("Test_Tileset")
|
|
texture = ts.to_texture()
|
|
|
|
# Pre-fill layer with a sentinel value
|
|
layer = mcrfpy.TileLayer(name="test", texture=texture, grid_size=(3, 3))
|
|
layer.fill(99)
|
|
|
|
# Create empty map - no rules will match
|
|
dm = mcrfpy.DiscreteMap((3, 3), fill=0)
|
|
rs.apply(dm, layer, seed=0)
|
|
|
|
# All cells should still be 99 (no rules matched)
|
|
for y in range(3):
|
|
for x in range(3):
|
|
val = layer.at(x, y)
|
|
assert val == 99, f"Expected 99 at ({x},{y}), got {val}"
|
|
print(" unmatched cells preserved: OK")
|
|
|
|
def test_apply_type_errors():
|
|
"""Test that apply raises TypeError for wrong argument types."""
|
|
proj = mcrfpy.LdtkProject("../tests/fixtures/test_project.ldtk")
|
|
rs = proj.ruleset("Terrain")
|
|
|
|
# Wrong first argument type
|
|
try:
|
|
rs.apply("not_a_dmap", None, seed=0)
|
|
assert False, "Should have raised TypeError"
|
|
except TypeError:
|
|
pass
|
|
|
|
# Wrong second argument type
|
|
dm = mcrfpy.DiscreteMap((3, 3))
|
|
try:
|
|
rs.apply(dm, "not_a_layer", seed=0)
|
|
assert False, "Should have raised TypeError"
|
|
except TypeError:
|
|
pass
|
|
|
|
print(" type errors raised correctly: OK")
|
|
|
|
def test_apply_clipping():
|
|
"""Test that apply clips to the smaller of map/layer dimensions."""
|
|
proj = mcrfpy.LdtkProject("../tests/fixtures/test_project.ldtk")
|
|
rs = proj.ruleset("Terrain")
|
|
ts = proj.tileset("Test_Tileset")
|
|
texture = ts.to_texture()
|
|
|
|
# DiscreteMap larger than TileLayer
|
|
dm = mcrfpy.DiscreteMap((10, 10), fill=1)
|
|
layer = mcrfpy.TileLayer(name="small", texture=texture, grid_size=(3, 3))
|
|
layer.fill(-1)
|
|
|
|
rs.apply(dm, layer, seed=0)
|
|
|
|
# Layer should have tiles written only within its bounds
|
|
for y in range(3):
|
|
for x in range(3):
|
|
val = layer.at(x, y)
|
|
assert val >= 0, f"Expected tile at ({x},{y}), got {val}"
|
|
print(" clipping (large map, small layer): OK")
|
|
|
|
# DiscreteMap smaller than TileLayer
|
|
dm2 = mcrfpy.DiscreteMap((2, 2), fill=1)
|
|
layer2 = mcrfpy.TileLayer(name="big", texture=texture, grid_size=(5, 5))
|
|
layer2.fill(88)
|
|
|
|
rs.apply(dm2, layer2, seed=0)
|
|
|
|
# Only (0,0)-(1,1) should be overwritten
|
|
for y in range(2):
|
|
for x in range(2):
|
|
val = layer2.at(x, y)
|
|
assert val >= 0, f"Expected tile at ({x},{y}), got {val}"
|
|
|
|
# (3,3) should still be the fill value
|
|
assert layer2.at(3, 3) == 88, f"Expected 88 at (3,3), got {layer2.at(3, 3)}"
|
|
print(" clipping (small map, large layer): OK")
|
|
|
|
def test_resolve_type_error():
|
|
"""Test that resolve raises TypeError for wrong argument."""
|
|
proj = mcrfpy.LdtkProject("../tests/fixtures/test_project.ldtk")
|
|
rs = proj.ruleset("Terrain")
|
|
|
|
try:
|
|
rs.resolve("not_a_dmap", seed=0)
|
|
assert False, "Should have raised TypeError"
|
|
except TypeError:
|
|
pass
|
|
print(" resolve TypeError: OK")
|
|
|
|
def test_precomputed_tiles():
|
|
"""Test loading pre-computed auto-layer tiles from a level."""
|
|
proj = mcrfpy.LdtkProject("../tests/fixtures/test_project.ldtk")
|
|
ts = proj.tileset("Test_Tileset")
|
|
texture = ts.to_texture()
|
|
|
|
level = proj.level("Level_0")
|
|
layer_info = level["layers"][0]
|
|
|
|
# Create TileLayer and write pre-computed tiles
|
|
layer = mcrfpy.TileLayer(name="precomp", texture=texture, grid_size=(5, 5))
|
|
layer.fill(-1)
|
|
|
|
for tile in layer_info["auto_tiles"]:
|
|
x, y = tile["x"], tile["y"]
|
|
if 0 <= x < 5 and 0 <= y < 5:
|
|
layer.set((x, y), tile["tile_id"])
|
|
|
|
# Verify some tiles were written
|
|
assert layer.at(0, 0) == 0, f"Expected tile 0 at (0,0), got {layer.at(0, 0)}"
|
|
print(f" precomputed tiles loaded: first = {layer.at(0, 0)}")
|
|
|
|
# Run tests
|
|
tests = [
|
|
test_apply_basic,
|
|
test_apply_preserves_unmatched,
|
|
test_apply_type_errors,
|
|
test_apply_clipping,
|
|
test_resolve_type_error,
|
|
test_precomputed_tiles,
|
|
]
|
|
|
|
passed = 0
|
|
failed = 0
|
|
print("=== LDtk Apply Tests ===")
|
|
for test in tests:
|
|
name = test.__name__
|
|
try:
|
|
print(f"[TEST] {name}...")
|
|
test()
|
|
passed += 1
|
|
print(f" PASS")
|
|
except Exception as e:
|
|
failed += 1
|
|
print(f" FAIL: {e}")
|
|
|
|
print(f"\n=== Results: {passed} passed, {failed} failed ===")
|
|
if failed > 0:
|
|
sys.exit(1)
|
|
sys.exit(0)
|