Add regression test for entity.die() during iteration, refs #273
Verifies that die() during grid.entities iteration raises RuntimeError (iterator invalidation protection), that the safe collect-then-die pattern works, and that die() properly removes from spatial hash. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8780f287bd
commit
de2dbe0b48
1 changed files with 66 additions and 0 deletions
66
tests/regression/issue_273_die_during_iteration_test.py
Normal file
66
tests/regression/issue_273_die_during_iteration_test.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
"""Regression test: entity.die() during iteration raises RuntimeError (#273).
|
||||
|
||||
Bug: Calling entity.die() inside a for-loop over grid.entities would
|
||||
invalidate the C++ list iterator, causing undefined behavior.
|
||||
|
||||
Fix: The EntityCollection iterator captures the collection size at start
|
||||
and checks it before each yield. If die() changes the size, RuntimeError
|
||||
is raised instead of silently corrupting memory.
|
||||
"""
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_die_during_iteration_raises():
|
||||
"""entity.die() during iteration raises RuntimeError"""
|
||||
grid = mcrfpy.Grid(grid_size=(20, 20))
|
||||
for i in range(5):
|
||||
mcrfpy.Entity(grid_pos=(i, 0), grid=grid)
|
||||
|
||||
caught = False
|
||||
try:
|
||||
for entity in grid.entities:
|
||||
entity.die()
|
||||
except RuntimeError as e:
|
||||
caught = True
|
||||
assert "changed size during iteration" in str(e), f"Wrong error: {e}"
|
||||
|
||||
assert caught, "Expected RuntimeError when calling die() during iteration"
|
||||
print(" PASS: die_during_iteration_raises")
|
||||
|
||||
def test_safe_die_pattern():
|
||||
"""Collecting entities first, then dying, works correctly"""
|
||||
grid = mcrfpy.Grid(grid_size=(20, 20))
|
||||
for i in range(5):
|
||||
mcrfpy.Entity(grid_pos=(i, 0), grid=grid)
|
||||
|
||||
assert len(grid.entities) == 5
|
||||
|
||||
# Safe pattern: collect references first
|
||||
to_die = list(grid.entities)
|
||||
for entity in to_die:
|
||||
entity.die()
|
||||
|
||||
assert len(grid.entities) == 0
|
||||
print(" PASS: safe_die_pattern")
|
||||
|
||||
def test_die_removes_from_spatial_hash():
|
||||
"""entity.die() properly removes from spatial hash"""
|
||||
grid = mcrfpy.Grid(grid_size=(20, 20))
|
||||
entity = mcrfpy.Entity(grid_pos=(5, 5), grid=grid)
|
||||
|
||||
found = grid.entities_in_radius((5.0, 5.0), 1.0)
|
||||
assert len(found) == 1
|
||||
|
||||
entity.die()
|
||||
|
||||
found = grid.entities_in_radius((5.0, 5.0), 1.0)
|
||||
assert len(found) == 0, f"Expected 0 after die(), found {len(found)}"
|
||||
print(" PASS: die_removes_from_spatial_hash")
|
||||
|
||||
|
||||
print("Testing entity.die() during iteration (#273)...")
|
||||
test_die_during_iteration_raises()
|
||||
test_safe_die_pattern()
|
||||
test_die_removes_from_spatial_hash()
|
||||
print("PASS: all die-during-iteration tests passed")
|
||||
sys.exit(0)
|
||||
Loading…
Add table
Add a link
Reference in a new issue