McRogueFace/tests/unit/test_animation_removal.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

95 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""
Test if the crash is related to removing animated objects.
Uses mcrfpy.step() for synchronous test execution.
"""
import mcrfpy
import sys
print("Animation Removal Test")
print("=" * 40)
# Create initial scene
print("Creating scene...")
test = mcrfpy.Scene("test")
test.activate()
ui = test.children
# Add title and subtitle (to preserve during clearing)
title = mcrfpy.Caption(pos=(400, 20), text="Test Title")
subtitle = mcrfpy.Caption(pos=(400, 50), text="Test Subtitle")
ui.extend([title, subtitle])
# Initialize scene
mcrfpy.step(0.1)
# Create initial animated objects
print("Creating initial animated objects...")
initial_frames = []
for i in range(10):
f = mcrfpy.Frame(pos=(50 + i*30, 100), size=(25, 25))
f.fill_color = mcrfpy.Color(255, 100, 100)
ui.append(f)
initial_frames.append(f)
# Animate them (mcrfpy.Animation is gone; animations are built by the target)
f.animate("y", 300.0, 2.0, mcrfpy.Easing.EASE_OUT_BOUNCE)
print(f"Initial scene has {len(ui)} elements")
# Let animations run a bit
mcrfpy.step(0.5)
# Clear and recreate - mimics demo switching
print("\nClearing and recreating...")
print(f"Scene has {len(ui)} elements before clearing")
# Remove all but first 2 items (like clear_demo_objects)
# Use reverse iteration to remove by element
while len(ui) > 2:
ui.remove(ui[-1])
print(f"Scene has {len(ui)} elements after clearing")
# Create new animated objects
print("Creating new animated objects...")
new_frames = []
for i in range(5):
f = mcrfpy.Frame(pos=(100 + i*50, 200), size=(40, 40))
f.fill_color = mcrfpy.Color(100 + i*30, 50, 200)
ui.append(f)
# Start animation on the new frame
target_x = 300 + i * 50
f.animate("x", float(target_x), 1.0, mcrfpy.Easing.EASE_IN_OUT)
new_frames.append((f, float(target_x)))
print("New objects created and animated")
print(f"Scene now has {len(ui)} elements")
# Let new animations run to completion (duration 1.0s)
for _ in range(20):
mcrfpy.step(0.1)
failures = []
# Final check: element count survived the clear/recreate cycle
print(f"\nFinal scene has {len(ui)} elements")
if len(ui) != 7: # 2 captions + 5 new frames
failures.append(f"Expected 7 elements, got {len(ui)}")
# The removed frames' animations must not have kept running (or crashed);
# the new frames' animations must have actually completed.
for i, (f, target_x) in enumerate(new_frames):
if abs(f.x - target_x) > 0.5:
failures.append(f"new frame {i}: x={f.x}, expected {target_x}")
if failures:
for msg in failures:
print(f"FAIL: {msg}")
sys.exit(1)
print("SUCCESS: Animation removal test passed!")
print("PASS")
sys.exit(0)