222 lines
7.9 KiB
Python
222 lines
7.9 KiB
Python
|
|
"""Unit tests for LDtk project parsing."""
|
||
|
|
import mcrfpy
|
||
|
|
import sys
|
||
|
|
import os
|
||
|
|
|
||
|
|
def test_load_project():
|
||
|
|
"""Test basic project loading."""
|
||
|
|
proj = mcrfpy.LdtkProject("../tests/fixtures/test_project.ldtk")
|
||
|
|
assert proj is not None, "Failed to create LdtkProject"
|
||
|
|
print(f" repr: {repr(proj)}")
|
||
|
|
return proj
|
||
|
|
|
||
|
|
def test_version(proj):
|
||
|
|
"""Test version property."""
|
||
|
|
assert proj.version == "1.5.3", f"Expected version '1.5.3', got '{proj.version}'"
|
||
|
|
print(f" version: {proj.version}")
|
||
|
|
|
||
|
|
def test_tileset_names(proj):
|
||
|
|
"""Test tileset enumeration."""
|
||
|
|
names = proj.tileset_names
|
||
|
|
assert isinstance(names, list), f"Expected list, got {type(names)}"
|
||
|
|
assert len(names) == 1, f"Expected 1 tileset, got {len(names)}"
|
||
|
|
assert names[0] == "Test_Tileset", f"Expected 'Test_Tileset', got '{names[0]}'"
|
||
|
|
print(f" tileset_names: {names}")
|
||
|
|
|
||
|
|
def test_ruleset_names(proj):
|
||
|
|
"""Test ruleset enumeration."""
|
||
|
|
names = proj.ruleset_names
|
||
|
|
assert isinstance(names, list), f"Expected list, got {type(names)}"
|
||
|
|
assert len(names) == 1, f"Expected 1 ruleset, got {len(names)}"
|
||
|
|
assert names[0] == "Terrain", f"Expected 'Terrain', got '{names[0]}'"
|
||
|
|
print(f" ruleset_names: {names}")
|
||
|
|
|
||
|
|
def test_level_names(proj):
|
||
|
|
"""Test level enumeration."""
|
||
|
|
names = proj.level_names
|
||
|
|
assert isinstance(names, list), f"Expected list, got {type(names)}"
|
||
|
|
assert len(names) == 1, f"Expected 1 level, got {len(names)}"
|
||
|
|
assert names[0] == "Level_0", f"Expected 'Level_0', got '{names[0]}'"
|
||
|
|
print(f" level_names: {names}")
|
||
|
|
|
||
|
|
def test_enums(proj):
|
||
|
|
"""Test enum access."""
|
||
|
|
enums = proj.enums
|
||
|
|
assert isinstance(enums, list), f"Expected list, got {type(enums)}"
|
||
|
|
assert len(enums) == 1, f"Expected 1 enum, got {len(enums)}"
|
||
|
|
assert enums[0]["identifier"] == "TileType"
|
||
|
|
print(f" enums: {len(enums)} enum(s), first = {enums[0]['identifier']}")
|
||
|
|
|
||
|
|
def test_tileset_access(proj):
|
||
|
|
"""Test tileset retrieval."""
|
||
|
|
ts = proj.tileset("Test_Tileset")
|
||
|
|
assert ts is not None, "Failed to get tileset"
|
||
|
|
print(f" tileset: {repr(ts)}")
|
||
|
|
assert ts.name == "Test_Tileset", f"Expected 'Test_Tileset', got '{ts.name}'"
|
||
|
|
assert ts.tile_width == 16, f"Expected tile_width 16, got {ts.tile_width}"
|
||
|
|
assert ts.tile_height == 16, f"Expected tile_height 16, got {ts.tile_height}"
|
||
|
|
assert ts.columns == 4, f"Expected columns 4, got {ts.columns}"
|
||
|
|
assert ts.tile_count == 16, f"Expected tile_count 16, got {ts.tile_count}"
|
||
|
|
|
||
|
|
def test_tileset_not_found(proj):
|
||
|
|
"""Test KeyError for missing tileset."""
|
||
|
|
try:
|
||
|
|
proj.tileset("Nonexistent")
|
||
|
|
assert False, "Should have raised KeyError"
|
||
|
|
except KeyError:
|
||
|
|
pass
|
||
|
|
print(" KeyError raised for missing tileset: OK")
|
||
|
|
|
||
|
|
def test_ruleset_access(proj):
|
||
|
|
"""Test ruleset retrieval."""
|
||
|
|
rs = proj.ruleset("Terrain")
|
||
|
|
assert rs is not None, "Failed to get ruleset"
|
||
|
|
print(f" ruleset: {repr(rs)}")
|
||
|
|
assert rs.name == "Terrain", f"Expected 'Terrain', got '{rs.name}'"
|
||
|
|
assert rs.grid_size == 16, f"Expected grid_size 16, got {rs.grid_size}"
|
||
|
|
assert rs.value_count == 3, f"Expected 3 values, got {rs.value_count}"
|
||
|
|
assert rs.group_count == 2, f"Expected 2 groups, got {rs.group_count}"
|
||
|
|
assert rs.rule_count == 3, f"Expected 3 rules, got {rs.rule_count}"
|
||
|
|
|
||
|
|
def test_ruleset_values(proj):
|
||
|
|
"""Test IntGrid value definitions."""
|
||
|
|
rs = proj.ruleset("Terrain")
|
||
|
|
values = rs.values
|
||
|
|
assert len(values) == 3, f"Expected 3 values, got {len(values)}"
|
||
|
|
assert values[0]["value"] == 1
|
||
|
|
assert values[0]["name"] == "wall"
|
||
|
|
assert values[1]["value"] == 2
|
||
|
|
assert values[1]["name"] == "floor"
|
||
|
|
assert values[2]["value"] == 3
|
||
|
|
assert values[2]["name"] == "water"
|
||
|
|
print(f" values: {values}")
|
||
|
|
|
||
|
|
def test_terrain_enum(proj):
|
||
|
|
"""Test terrain_enum() generation."""
|
||
|
|
rs = proj.ruleset("Terrain")
|
||
|
|
Terrain = rs.terrain_enum()
|
||
|
|
assert Terrain is not None, "Failed to create terrain enum"
|
||
|
|
assert Terrain.NONE == 0, f"Expected NONE=0, got {Terrain.NONE}"
|
||
|
|
assert Terrain.WALL == 1, f"Expected WALL=1, got {Terrain.WALL}"
|
||
|
|
assert Terrain.FLOOR == 2, f"Expected FLOOR=2, got {Terrain.FLOOR}"
|
||
|
|
assert Terrain.WATER == 3, f"Expected WATER=3, got {Terrain.WATER}"
|
||
|
|
print(f" terrain enum: {list(Terrain)}")
|
||
|
|
|
||
|
|
def test_level_access(proj):
|
||
|
|
"""Test level data retrieval."""
|
||
|
|
level = proj.level("Level_0")
|
||
|
|
assert isinstance(level, dict), f"Expected dict, got {type(level)}"
|
||
|
|
assert level["name"] == "Level_0"
|
||
|
|
assert level["width_px"] == 80
|
||
|
|
assert level["height_px"] == 80
|
||
|
|
assert level["world_x"] == 0
|
||
|
|
assert level["world_y"] == 0
|
||
|
|
print(f" level: {level['name']} ({level['width_px']}x{level['height_px']}px)")
|
||
|
|
|
||
|
|
def test_level_layers(proj):
|
||
|
|
"""Test level layer data."""
|
||
|
|
level = proj.level("Level_0")
|
||
|
|
layers = level["layers"]
|
||
|
|
assert len(layers) == 1, f"Expected 1 layer, got {len(layers)}"
|
||
|
|
|
||
|
|
layer = layers[0]
|
||
|
|
assert layer["name"] == "Terrain"
|
||
|
|
assert layer["type"] == "IntGrid"
|
||
|
|
assert layer["width"] == 5
|
||
|
|
assert layer["height"] == 5
|
||
|
|
print(f" layer: {layer['name']} ({layer['type']}) {layer['width']}x{layer['height']}")
|
||
|
|
|
||
|
|
def test_level_intgrid(proj):
|
||
|
|
"""Test IntGrid CSV data."""
|
||
|
|
level = proj.level("Level_0")
|
||
|
|
layer = level["layers"][0]
|
||
|
|
intgrid = layer["intgrid"]
|
||
|
|
assert len(intgrid) == 25, f"Expected 25 cells, got {len(intgrid)}"
|
||
|
|
# Check corners are walls (1)
|
||
|
|
assert intgrid[0] == 1, f"Expected wall at (0,0), got {intgrid[0]}"
|
||
|
|
assert intgrid[4] == 1, f"Expected wall at (4,0), got {intgrid[4]}"
|
||
|
|
# Check center is water (3)
|
||
|
|
assert intgrid[12] == 3, f"Expected water at (2,2), got {intgrid[12]}"
|
||
|
|
# Check floor tiles (2)
|
||
|
|
assert intgrid[6] == 2, f"Expected floor at (1,1), got {intgrid[6]}"
|
||
|
|
print(f" intgrid: {intgrid[:5]}... ({len(intgrid)} cells)")
|
||
|
|
|
||
|
|
def test_level_auto_tiles(proj):
|
||
|
|
"""Test pre-computed auto-layer tiles."""
|
||
|
|
level = proj.level("Level_0")
|
||
|
|
layer = level["layers"][0]
|
||
|
|
auto_tiles = layer["auto_tiles"]
|
||
|
|
assert len(auto_tiles) > 0, f"Expected auto tiles, got {len(auto_tiles)}"
|
||
|
|
# Check first tile structure
|
||
|
|
t = auto_tiles[0]
|
||
|
|
assert "tile_id" in t, f"Missing tile_id in auto tile: {t}"
|
||
|
|
assert "x" in t, f"Missing x in auto tile: {t}"
|
||
|
|
assert "y" in t, f"Missing y in auto tile: {t}"
|
||
|
|
assert "flip" in t, f"Missing flip in auto tile: {t}"
|
||
|
|
print(f" auto_tiles: {len(auto_tiles)} tiles, first = {auto_tiles[0]}")
|
||
|
|
|
||
|
|
def test_level_not_found(proj):
|
||
|
|
"""Test KeyError for missing level."""
|
||
|
|
try:
|
||
|
|
proj.level("Nonexistent")
|
||
|
|
assert False, "Should have raised KeyError"
|
||
|
|
except KeyError:
|
||
|
|
pass
|
||
|
|
print(" KeyError raised for missing level: OK")
|
||
|
|
|
||
|
|
def test_load_nonexistent():
|
||
|
|
"""Test IOError for missing file."""
|
||
|
|
try:
|
||
|
|
mcrfpy.LdtkProject("nonexistent.ldtk")
|
||
|
|
assert False, "Should have raised IOError"
|
||
|
|
except IOError:
|
||
|
|
pass
|
||
|
|
print(" IOError raised for missing file: OK")
|
||
|
|
|
||
|
|
# Run all tests
|
||
|
|
tests = [
|
||
|
|
("load_project", None),
|
||
|
|
("version", None),
|
||
|
|
("tileset_names", None),
|
||
|
|
("ruleset_names", None),
|
||
|
|
("level_names", None),
|
||
|
|
("enums", None),
|
||
|
|
("tileset_access", None),
|
||
|
|
("tileset_not_found", None),
|
||
|
|
("ruleset_access", None),
|
||
|
|
("ruleset_values", None),
|
||
|
|
("terrain_enum", None),
|
||
|
|
("level_access", None),
|
||
|
|
("level_layers", None),
|
||
|
|
("level_intgrid", None),
|
||
|
|
("level_auto_tiles", None),
|
||
|
|
("level_not_found", None),
|
||
|
|
("load_nonexistent", None),
|
||
|
|
]
|
||
|
|
|
||
|
|
passed = 0
|
||
|
|
failed = 0
|
||
|
|
proj = None
|
||
|
|
|
||
|
|
# First test returns the project
|
||
|
|
print("=== LDtk Parse Tests ===")
|
||
|
|
for name, func in tests:
|
||
|
|
try:
|
||
|
|
test_fn = globals()[f"test_{name}"]
|
||
|
|
print(f"[TEST] {name}...")
|
||
|
|
if name == "load_project":
|
||
|
|
proj = test_fn()
|
||
|
|
elif name in ("load_nonexistent",):
|
||
|
|
test_fn()
|
||
|
|
else:
|
||
|
|
test_fn(proj)
|
||
|
|
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)
|