McRogueFace/tests/regression/issue_147_grid_layers.py
John McCardle 112f3571f5 test(suite): unrot 82 tests that were passing without ever running
The suite was reporting 331/331 while at least 82 of those tests asserted
nothing at all.

They raised during setup on APIs removed long ago -- add_layer(name=...),
GridPoint.color, mcrfpy.Animation(), entity.gridstate, mcrfpy.setScene,
assets/kenney_ice.png, GridData.compute_astar -- registered no timers, hit the
engine's auto-exit-when-no-timers path, exited 0, and were scored PASS. Their
assertions had not executed in months. test_metrics.py is the sharpest example:
the existing metrics test died on line 140 with a TypeError, which is precisely
why #341 (get_metrics counters reading 0) went unnoticed.

The engine no longer permits this (#350: a headless --exec script must call
sys.exit()), and run_tests.py no longer passes a test whose output contains a
Traceback. This commit repairs the 82 they exposed, migrating each to the
current API while preserving its original intent -- not deleting assertions to
make the command exit 0. Each repair was adversarially re-verified by a second
pass asking "is this still a test, or was it gutted?"; none were.

Two tests could not be made to pass because they were right and the engine was
wrong. Rather than paper over them they were left failing and the bugs fixed
separately in 48eef0b: DijkstraMap path order (#375) and layer-setter cache
invalidation (#376). Three integration tests had encoded the reversed Dijkstra
order as expected behavior; their assertions now state the real contract
(excludes the origin, ends at the root).

Suite: 334/334, every one of them actually asserting.

Refs #341, #350, #372
2026-07-14 07:29:23 -04:00

200 lines
6.3 KiB
Python

#!/usr/bin/env python3
"""
Regression test for issue #147: Dynamic Layer System for Grid
Tests:
1. ColorLayer creation and manipulation
2. TileLayer creation and manipulation
3. Layer z_index ordering relative to entities
4. Layer management (add_layer, remove_layer, layers property)
API notes (current contract):
- Layers are constructed standalone (TileLayer/ColorLayer ctors take kwargs) and then
attached with grid_data.add_layer(layer) -- add_layer takes an object, not kwargs.
- Layer management (add_layer / remove_layer / layer) lives on GridData
(grid.grid_data); the Grid view exposes the read-only `layers` tuple.
- Layers are looked up BY NAME, not by z_index (grid_data.layer(name)).
"""
import mcrfpy
import sys
print("=" * 60)
print("Issue #147 Regression Test: Dynamic Layer System for Grid")
print("=" * 60)
# Create test scene
test = mcrfpy.Scene("test")
mcrfpy.current_scene = test
ui = test.children
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
# Create grid with explicit empty layers (#150 migration)
grid = mcrfpy.Grid(pos=(50, 50), size=(400, 300), grid_size=(20, 15), texture=texture, layers=[])
ui.append(grid)
grid_data = grid.grid_data
print("\n--- Test 1: Initial state (no layers) ---")
if len(grid.layers) == 0:
print(" PASS: Grid starts with no layers (layers=[])")
else:
print(f" FAIL: Expected 0 layers, got {len(grid.layers)}")
sys.exit(1)
print("\n--- Test 2: Add ColorLayer ---")
color_layer = grid_data.add_layer(mcrfpy.ColorLayer(name="color", z_index=-1))
print(f" Created: {color_layer}")
if color_layer is not None:
print(" PASS: ColorLayer created")
else:
print(" FAIL: ColorLayer creation returned None")
sys.exit(1)
# Test ColorLayer properties
if color_layer.z_index == -1:
print(" PASS: ColorLayer z_index is -1")
else:
print(f" FAIL: Expected z_index -1, got {color_layer.z_index}")
sys.exit(1)
if color_layer.visible:
print(" PASS: ColorLayer is visible by default")
else:
print(" FAIL: ColorLayer should be visible by default")
sys.exit(1)
grid_size = color_layer.grid_size
if grid_size == (20, 15):
print(f" PASS: ColorLayer grid_size is {grid_size}")
else:
print(f" FAIL: Expected (20, 15), got {grid_size}")
sys.exit(1)
print("\n--- Test 3: ColorLayer cell access ---")
# Set a color
color_layer.set((5, 5), mcrfpy.Color(255, 0, 0, 128))
color = color_layer.at(5, 5)
if color.r == 255 and color.g == 0 and color.b == 0 and color.a == 128:
print(f" PASS: Color at (5,5) is {color.r}, {color.g}, {color.b}, {color.a}")
else:
print(f" FAIL: Color mismatch")
sys.exit(1)
# Fill entire layer
color_layer.fill(mcrfpy.Color(0, 0, 255, 64))
color = color_layer.at(0, 0)
if color.b == 255 and color.a == 64:
print(" PASS: ColorLayer fill works")
else:
print(" FAIL: ColorLayer fill did not work")
sys.exit(1)
print("\n--- Test 4: Add TileLayer ---")
tile_layer = grid_data.add_layer(mcrfpy.TileLayer(name="tile", z_index=-2, texture=texture))
print(f" Created: {tile_layer}")
if tile_layer is not None:
print(" PASS: TileLayer created")
else:
print(" FAIL: TileLayer creation returned None")
sys.exit(1)
if tile_layer.z_index == -2:
print(" PASS: TileLayer z_index is -2")
else:
print(f" FAIL: Expected z_index -2, got {tile_layer.z_index}")
sys.exit(1)
print("\n--- Test 5: TileLayer cell access ---")
# Set a tile
tile_layer.set((3, 3), 42)
tile = tile_layer.at(3, 3)
if tile == 42:
print(f" PASS: Tile at (3,3) is {tile}")
else:
print(f" FAIL: Expected 42, got {tile}")
sys.exit(1)
# Fill entire layer
tile_layer.fill(10)
tile = tile_layer.at(0, 0)
if tile == 10:
print(" PASS: TileLayer fill works")
else:
print(" FAIL: TileLayer fill did not work")
sys.exit(1)
print("\n--- Test 6: Layer ordering ---")
layers = grid.layers
if len(layers) == 2:
print(f" PASS: Grid has 2 layers")
else:
print(f" FAIL: Expected 2 layers, got {len(layers)}")
sys.exit(1)
# Layers should be sorted by z_index
if layers[0].z_index <= layers[1].z_index:
print(f" PASS: Layers sorted by z_index ({layers[0].z_index}, {layers[1].z_index})")
else:
print(f" FAIL: Layers not sorted")
sys.exit(1)
print("\n--- Test 7: Get layer by name ---")
# Layer lookup is by NAME now (grid.layer(z_index) no longer exists); z_index is still
# the ordering key, verified in Test 6. Note: layer lookups return a fresh wrapper each
# call (layers are not in PythonObjectCache), so compare identity via the shared data,
# not `is`.
layer = grid_data.layer("color")
if layer is not None and layer.z_index == -1 and layer.at(5, 5).b == color_layer.at(5, 5).b:
print(" PASS: grid_data.layer('color') returns the ColorLayer")
else:
print(" FAIL: Could not get ColorLayer by name")
sys.exit(1)
layer = grid_data.layer("tile")
if layer is not None and layer.z_index == -2 and layer.at(3, 3) == tile_layer.at(3, 3):
print(" PASS: grid_data.layer('tile') returns the TileLayer")
else:
print(" FAIL: Could not get TileLayer by name")
sys.exit(1)
layer = grid_data.layer("nonexistent")
if layer is None:
print(" PASS: grid_data.layer('nonexistent') returns None for non-existent layer")
else:
print(" FAIL: Should return None for non-existent layer")
sys.exit(1)
print("\n--- Test 8: Layer above entities (z_index >= 0) ---")
fog_layer = grid_data.add_layer(mcrfpy.ColorLayer(name="fog", z_index=1))
if fog_layer.z_index == 1:
print(" PASS: Created layer with z_index=1 (above entities)")
else:
print(" FAIL: Layer z_index incorrect")
sys.exit(1)
# Set fog
fog_layer.fill(mcrfpy.Color(0, 0, 0, 128))
print(" PASS: Fog layer filled")
print("\n--- Test 9: Remove layer ---")
initial_count = len(grid.layers)
grid_data.remove_layer(fog_layer)
final_count = len(grid.layers)
if final_count == initial_count - 1:
print(f" PASS: Layer removed ({initial_count} -> {final_count})")
else:
print(f" FAIL: Layer count didn't decrease ({initial_count} -> {final_count})")
sys.exit(1)
print("\n--- Test 10: Layer visibility toggle ---")
color_layer.visible = False
if not color_layer.visible:
print(" PASS: Layer visibility can be toggled")
else:
print(" FAIL: Layer visibility toggle failed")
sys.exit(1)
color_layer.visible = True
print("\n" + "=" * 60)
print("All tests PASSED")
print("=" * 60)
sys.exit(0)