Test suite modernization
This commit is contained in:
parent
0969f7c2f6
commit
52fdfd0347
141 changed files with 9947 additions and 4665 deletions
|
|
@ -158,10 +158,16 @@ def test_edge_cases():
|
|||
print(" Edge cases: PASS")
|
||||
return True
|
||||
|
||||
def run_test(timer, runtime):
|
||||
"""Timer callback to run tests after scene is active"""
|
||||
results = []
|
||||
# Main
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Issue #123: Grid Sub-grid Chunk System Test")
|
||||
print("=" * 60)
|
||||
|
||||
test = mcrfpy.Scene("test")
|
||||
mcrfpy.current_scene = test
|
||||
|
||||
results = []
|
||||
results.append(test_small_grid())
|
||||
results.append(test_large_grid())
|
||||
results.append(test_very_large_grid())
|
||||
|
|
@ -174,15 +180,3 @@ def run_test(timer, runtime):
|
|||
else:
|
||||
print("\n=== SOME TESTS FAILED ===")
|
||||
sys.exit(1)
|
||||
|
||||
# Main
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Issue #123: Grid Sub-grid Chunk System Test")
|
||||
print("=" * 60)
|
||||
|
||||
test = mcrfpy.Scene("test")
|
||||
test.activate()
|
||||
|
||||
# Run tests after scene is active
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"""
|
||||
Regression test for issue #146: compute_fov() returns None
|
||||
|
||||
The compute_fov() method had O(n²) performance because it built a Python list
|
||||
The compute_fov() method had O(n^2) performance because it built a Python list
|
||||
of all visible cells by iterating the entire grid. The fix removes this
|
||||
list-building and returns None instead. Users should use is_in_fov() to query
|
||||
visibility.
|
||||
|
|
@ -14,101 +14,96 @@ import mcrfpy
|
|||
import sys
|
||||
import time
|
||||
|
||||
def run_test(timer, runtime):
|
||||
print("=" * 60)
|
||||
print("Issue #146 Regression Test: compute_fov() returns None")
|
||||
print("=" * 60)
|
||||
print("=" * 60)
|
||||
print("Issue #146 Regression Test: compute_fov() returns None")
|
||||
print("=" * 60)
|
||||
|
||||
# Create a test grid
|
||||
test = mcrfpy.Scene("test")
|
||||
ui = test.children
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
# Create a test scene and grid
|
||||
test = mcrfpy.Scene("test")
|
||||
mcrfpy.current_scene = test
|
||||
ui = test.children
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
|
||||
grid = mcrfpy.Grid(pos=(0,0), size=(400,300), grid_size=(50, 50), texture=texture)
|
||||
ui.append(grid)
|
||||
grid = mcrfpy.Grid(pos=(0,0), size=(400,300), grid_size=(50, 50), texture=texture)
|
||||
ui.append(grid)
|
||||
|
||||
# Set walkability for center area
|
||||
for y in range(50):
|
||||
for x in range(50):
|
||||
cell = grid.at(x, y)
|
||||
cell.walkable = True
|
||||
cell.transparent = True
|
||||
# Set walkability for center area
|
||||
for y in range(50):
|
||||
for x in range(50):
|
||||
cell = grid.at(x, y)
|
||||
cell.walkable = True
|
||||
cell.transparent = True
|
||||
|
||||
# Add some walls to test blocking
|
||||
for i in range(10, 20):
|
||||
grid.at(i, 25).transparent = False
|
||||
grid.at(i, 25).walkable = False
|
||||
# Add some walls to test blocking
|
||||
for i in range(10, 20):
|
||||
grid.at(i, 25).transparent = False
|
||||
grid.at(i, 25).walkable = False
|
||||
|
||||
print("\n--- Test 1: compute_fov() returns None ---")
|
||||
result = grid.compute_fov(25, 25, radius=10)
|
||||
if result is None:
|
||||
print(" PASS: compute_fov() returned None")
|
||||
else:
|
||||
print(f" FAIL: compute_fov() returned {type(result).__name__} instead of None")
|
||||
sys.exit(1)
|
||||
print("\n--- Test 1: compute_fov() returns None ---")
|
||||
result = grid.compute_fov(25, 25, radius=10)
|
||||
if result is None:
|
||||
print(" PASS: compute_fov() returned None")
|
||||
else:
|
||||
print(f" FAIL: compute_fov() returned {type(result).__name__} instead of None")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n--- Test 2: is_in_fov() works after compute_fov() ---")
|
||||
# Center should be visible
|
||||
if grid.is_in_fov(25, 25):
|
||||
print(" PASS: Center (25,25) is in FOV")
|
||||
else:
|
||||
print(" FAIL: Center should be in FOV")
|
||||
sys.exit(1)
|
||||
print("\n--- Test 2: is_in_fov() works after compute_fov() ---")
|
||||
# Center should be visible
|
||||
if grid.is_in_fov(25, 25):
|
||||
print(" PASS: Center (25,25) is in FOV")
|
||||
else:
|
||||
print(" FAIL: Center should be in FOV")
|
||||
sys.exit(1)
|
||||
|
||||
# Cell within radius should be visible
|
||||
if grid.is_in_fov(20, 25):
|
||||
print(" PASS: Cell (20,25) within radius is in FOV")
|
||||
else:
|
||||
print(" FAIL: Cell (20,25) should be in FOV")
|
||||
sys.exit(1)
|
||||
# Cell within radius should be visible
|
||||
if grid.is_in_fov(20, 25):
|
||||
print(" PASS: Cell (20,25) within radius is in FOV")
|
||||
else:
|
||||
print(" FAIL: Cell (20,25) should be in FOV")
|
||||
sys.exit(1)
|
||||
|
||||
# Cell behind wall should NOT be visible
|
||||
if not grid.is_in_fov(15, 30):
|
||||
print(" PASS: Cell (15,30) behind wall is NOT in FOV")
|
||||
else:
|
||||
print(" FAIL: Cell behind wall should not be in FOV")
|
||||
sys.exit(1)
|
||||
# Cell behind wall should NOT be visible
|
||||
if not grid.is_in_fov(15, 30):
|
||||
print(" PASS: Cell (15,30) behind wall is NOT in FOV")
|
||||
else:
|
||||
print(" FAIL: Cell behind wall should not be in FOV")
|
||||
sys.exit(1)
|
||||
|
||||
# Cell outside radius should NOT be visible
|
||||
if not grid.is_in_fov(0, 0):
|
||||
print(" PASS: Cell (0,0) outside radius is NOT in FOV")
|
||||
else:
|
||||
print(" FAIL: Cell outside radius should not be in FOV")
|
||||
sys.exit(1)
|
||||
# Cell outside radius should NOT be visible
|
||||
if not grid.is_in_fov(0, 0):
|
||||
print(" PASS: Cell (0,0) outside radius is NOT in FOV")
|
||||
else:
|
||||
print(" FAIL: Cell outside radius should not be in FOV")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n--- Test 3: Performance sanity check ---")
|
||||
# Create larger grid for timing
|
||||
grid_large = mcrfpy.Grid(pos=(0,0), size=(400,300), grid_size=(200, 200), texture=texture)
|
||||
for y in range(0, 200, 5): # Sample for speed
|
||||
for x in range(200):
|
||||
cell = grid_large.at(x, y)
|
||||
cell.walkable = True
|
||||
cell.transparent = True
|
||||
print("\n--- Test 3: Performance sanity check ---")
|
||||
# Create larger grid for timing
|
||||
grid_large = mcrfpy.Grid(pos=(0,0), size=(400,300), grid_size=(200, 200), texture=texture)
|
||||
for y in range(0, 200, 5): # Sample for speed
|
||||
for x in range(200):
|
||||
cell = grid_large.at(x, y)
|
||||
cell.walkable = True
|
||||
cell.transparent = True
|
||||
|
||||
# Time compute_fov (should be fast now - no list building)
|
||||
times = []
|
||||
for i in range(5):
|
||||
t0 = time.perf_counter()
|
||||
grid_large.compute_fov(100, 100, radius=15)
|
||||
elapsed = (time.perf_counter() - t0) * 1000
|
||||
times.append(elapsed)
|
||||
# Time compute_fov (should be fast now - no list building)
|
||||
times = []
|
||||
for i in range(5):
|
||||
t0 = time.perf_counter()
|
||||
grid_large.compute_fov(100, 100, radius=15)
|
||||
elapsed = (time.perf_counter() - t0) * 1000
|
||||
times.append(elapsed)
|
||||
|
||||
avg_time = sum(times) / len(times)
|
||||
print(f" compute_fov() on 200x200 grid: {avg_time:.3f}ms avg")
|
||||
avg_time = sum(times) / len(times)
|
||||
print(f" compute_fov() on 200x200 grid: {avg_time:.3f}ms avg")
|
||||
|
||||
# Should be under 1ms without list building (was ~4ms with list on 200x200)
|
||||
if avg_time < 2.0:
|
||||
print(f" PASS: compute_fov() is fast (<2ms)")
|
||||
else:
|
||||
print(f" WARNING: compute_fov() took {avg_time:.3f}ms (expected <2ms)")
|
||||
# Not a hard failure, just a warning
|
||||
# Should be under 1ms without list building (was ~4ms with list on 200x200)
|
||||
if avg_time < 2.0:
|
||||
print(f" PASS: compute_fov() is fast (<2ms)")
|
||||
else:
|
||||
print(f" WARNING: compute_fov() took {avg_time:.3f}ms (expected <2ms)")
|
||||
# Not a hard failure, just a warning
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("All tests PASSED")
|
||||
print("=" * 60)
|
||||
sys.exit(0)
|
||||
|
||||
# Initialize and run
|
||||
init = mcrfpy.Scene("init")
|
||||
init.activate()
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
print("\n" + "=" * 60)
|
||||
print("All tests PASSED")
|
||||
print("=" * 60)
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -11,183 +11,178 @@ Tests:
|
|||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def run_test(timer, runtime):
|
||||
print("=" * 60)
|
||||
print("Issue #147 Regression Test: Dynamic Layer System for Grid")
|
||||
print("=" * 60)
|
||||
print("=" * 60)
|
||||
print("Issue #147 Regression Test: Dynamic Layer System for Grid")
|
||||
print("=" * 60)
|
||||
|
||||
# Create test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
ui = test.children
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
# Create test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
mcrfpy.current_scene = test
|
||||
ui = test.children
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.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)
|
||||
# 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)
|
||||
|
||||
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 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.add_layer("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)
|
||||
print("\n--- Test 2: Add ColorLayer ---")
|
||||
color_layer = grid.add_layer("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)
|
||||
# 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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
# 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.add_layer("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)
|
||||
print("\n--- Test 4: Add TileLayer ---")
|
||||
tile_layer = grid.add_layer("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)
|
||||
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)
|
||||
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)
|
||||
# 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)
|
||||
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)
|
||||
# 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 z_index ---")
|
||||
layer = grid.layer(-1)
|
||||
if layer is not None and layer.z_index == -1:
|
||||
print(" PASS: grid.layer(-1) returns ColorLayer")
|
||||
else:
|
||||
print(" FAIL: Could not get layer by z_index")
|
||||
sys.exit(1)
|
||||
print("\n--- Test 7: Get layer by z_index ---")
|
||||
layer = grid.layer(-1)
|
||||
if layer is not None and layer.z_index == -1:
|
||||
print(" PASS: grid.layer(-1) returns ColorLayer")
|
||||
else:
|
||||
print(" FAIL: Could not get layer by z_index")
|
||||
sys.exit(1)
|
||||
|
||||
layer = grid.layer(-2)
|
||||
if layer is not None and layer.z_index == -2:
|
||||
print(" PASS: grid.layer(-2) returns TileLayer")
|
||||
else:
|
||||
print(" FAIL: Could not get layer by z_index")
|
||||
sys.exit(1)
|
||||
layer = grid.layer(-2)
|
||||
if layer is not None and layer.z_index == -2:
|
||||
print(" PASS: grid.layer(-2) returns TileLayer")
|
||||
else:
|
||||
print(" FAIL: Could not get layer by z_index")
|
||||
sys.exit(1)
|
||||
|
||||
layer = grid.layer(999)
|
||||
if layer is None:
|
||||
print(" PASS: grid.layer(999) returns None for non-existent layer")
|
||||
else:
|
||||
print(" FAIL: Should return None for non-existent layer")
|
||||
sys.exit(1)
|
||||
layer = grid.layer(999)
|
||||
if layer is None:
|
||||
print(" PASS: grid.layer(999) 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.add_layer("color", 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)
|
||||
print("\n--- Test 8: Layer above entities (z_index >= 0) ---")
|
||||
fog_layer = grid.add_layer("color", 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")
|
||||
# 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.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 9: Remove layer ---")
|
||||
initial_count = len(grid.layers)
|
||||
grid.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--- 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)
|
||||
|
||||
# Initialize and run
|
||||
init = mcrfpy.Scene("init")
|
||||
init.activate()
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
print("\n" + "=" * 60)
|
||||
print("All tests PASSED")
|
||||
print("=" * 60)
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -14,144 +14,134 @@ import mcrfpy
|
|||
import sys
|
||||
import time
|
||||
|
||||
def run_test(timer, runtime):
|
||||
print("=" * 60)
|
||||
print("Issue #148 Regression Test: Layer Dirty Flags and Caching")
|
||||
print("=" * 60)
|
||||
print("=" * 60)
|
||||
print("Issue #148 Regression Test: Layer Dirty Flags and Caching")
|
||||
print("=" * 60)
|
||||
|
||||
# Create test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
ui = test.children
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
# Create test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
mcrfpy.current_scene = test
|
||||
ui = test.children
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
|
||||
# Create grid with larger size for performance testing
|
||||
grid = mcrfpy.Grid(pos=(50, 50), size=(500, 400), grid_size=(50, 40), texture=texture)
|
||||
ui.append(grid)
|
||||
test.activate()
|
||||
# Create grid with larger size for performance testing
|
||||
grid = mcrfpy.Grid(pos=(50, 50), size=(500, 400), grid_size=(50, 40), texture=texture)
|
||||
ui.append(grid)
|
||||
|
||||
print("\n--- Test 1: Layer creation (starts dirty) ---")
|
||||
color_layer = grid.add_layer("color", z_index=-1)
|
||||
# The layer should be dirty initially
|
||||
# We can't directly check dirty flag from Python, but we verify the system works
|
||||
print(" ColorLayer created successfully")
|
||||
print("\n--- Test 1: Layer creation (starts dirty) ---")
|
||||
color_layer = grid.add_layer("color", z_index=-1)
|
||||
# The layer should be dirty initially
|
||||
# We can't directly check dirty flag from Python, but we verify the system works
|
||||
print(" ColorLayer created successfully")
|
||||
|
||||
tile_layer = grid.add_layer("tile", z_index=-2, texture=texture)
|
||||
print(" TileLayer created successfully")
|
||||
print(" PASS: Layers created")
|
||||
tile_layer = grid.add_layer("tile", z_index=-2, texture=texture)
|
||||
print(" TileLayer created successfully")
|
||||
print(" PASS: Layers created")
|
||||
|
||||
print("\n--- Test 2: Fill operations work ---")
|
||||
# Fill with some data
|
||||
color_layer.fill(mcrfpy.Color(128, 0, 128, 64))
|
||||
print(" ColorLayer filled with purple overlay")
|
||||
print("\n--- Test 2: Fill operations work ---")
|
||||
# Fill with some data
|
||||
color_layer.fill(mcrfpy.Color(128, 0, 128, 64))
|
||||
print(" ColorLayer filled with purple overlay")
|
||||
|
||||
tile_layer.fill(5) # Fill with tile index 5
|
||||
print(" TileLayer filled with tile index 5")
|
||||
print(" PASS: Fill operations completed")
|
||||
tile_layer.fill(5) # Fill with tile index 5
|
||||
print(" TileLayer filled with tile index 5")
|
||||
print(" PASS: Fill operations completed")
|
||||
|
||||
print("\n--- Test 3: Cell set operations work ---")
|
||||
# Set individual cells
|
||||
color_layer.set(10, 10, mcrfpy.Color(255, 255, 0, 128))
|
||||
color_layer.set(11, 10, mcrfpy.Color(255, 255, 0, 128))
|
||||
color_layer.set(10, 11, mcrfpy.Color(255, 255, 0, 128))
|
||||
color_layer.set(11, 11, mcrfpy.Color(255, 255, 0, 128))
|
||||
print(" Set 4 cells in ColorLayer to yellow")
|
||||
print("\n--- Test 3: Cell set operations work ---")
|
||||
# Set individual cells
|
||||
color_layer.set(10, 10, mcrfpy.Color(255, 255, 0, 128))
|
||||
color_layer.set(11, 10, mcrfpy.Color(255, 255, 0, 128))
|
||||
color_layer.set(10, 11, mcrfpy.Color(255, 255, 0, 128))
|
||||
color_layer.set(11, 11, mcrfpy.Color(255, 255, 0, 128))
|
||||
print(" Set 4 cells in ColorLayer to yellow")
|
||||
|
||||
tile_layer.set(15, 15, 10)
|
||||
tile_layer.set(16, 15, 11)
|
||||
tile_layer.set(15, 16, 10)
|
||||
tile_layer.set(16, 16, 11)
|
||||
print(" Set 4 cells in TileLayer to different tiles")
|
||||
print(" PASS: Cell set operations completed")
|
||||
tile_layer.set(15, 15, 10)
|
||||
tile_layer.set(16, 15, 11)
|
||||
tile_layer.set(15, 16, 10)
|
||||
tile_layer.set(16, 16, 11)
|
||||
print(" Set 4 cells in TileLayer to different tiles")
|
||||
print(" PASS: Cell set operations completed")
|
||||
|
||||
print("\n--- Test 4: Texture change on TileLayer ---")
|
||||
# Create a second texture and assign it
|
||||
texture2 = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
tile_layer.texture = texture2
|
||||
print(" Changed TileLayer texture")
|
||||
print("\n--- Test 4: Texture change on TileLayer ---")
|
||||
# Create a second texture and assign it
|
||||
texture2 = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
tile_layer.texture = texture2
|
||||
print(" Changed TileLayer texture")
|
||||
|
||||
# Set back to original
|
||||
tile_layer.texture = texture
|
||||
print(" Restored original texture")
|
||||
print(" PASS: Texture changes work")
|
||||
# Set back to original
|
||||
tile_layer.texture = texture
|
||||
print(" Restored original texture")
|
||||
print(" PASS: Texture changes work")
|
||||
|
||||
print("\n--- Test 5: Viewport changes (should use cached texture) ---")
|
||||
# Pan around - these should NOT cause layer re-renders (just blit different region)
|
||||
original_center = grid.center
|
||||
print(f" Original center: {original_center}")
|
||||
print("\n--- Test 5: Viewport changes (should use cached texture) ---")
|
||||
# Pan around - these should NOT cause layer re-renders (just blit different region)
|
||||
original_center = grid.center
|
||||
print(f" Original center: {original_center}")
|
||||
|
||||
# Perform multiple viewport changes
|
||||
for i in range(10):
|
||||
grid.center = (100 + i * 20, 80 + i * 10)
|
||||
print(" Performed 10 center changes")
|
||||
# Perform multiple viewport changes
|
||||
for i in range(10):
|
||||
grid.center = (100 + i * 20, 80 + i * 10)
|
||||
print(" Performed 10 center changes")
|
||||
|
||||
# Zoom changes
|
||||
original_zoom = grid.zoom
|
||||
for z in [1.0, 0.8, 1.2, 0.5, 1.5, 1.0]:
|
||||
grid.zoom = z
|
||||
print(" Performed 6 zoom changes")
|
||||
# Zoom changes
|
||||
original_zoom = grid.zoom
|
||||
for z in [1.0, 0.8, 1.2, 0.5, 1.5, 1.0]:
|
||||
grid.zoom = z
|
||||
print(" Performed 6 zoom changes")
|
||||
|
||||
# Restore
|
||||
grid.center = original_center
|
||||
grid.zoom = original_zoom
|
||||
print(" PASS: Viewport changes completed without crashing")
|
||||
# Restore
|
||||
grid.center = original_center
|
||||
grid.zoom = original_zoom
|
||||
print(" PASS: Viewport changes completed without crashing")
|
||||
|
||||
print("\n--- Test 6: Performance benchmark ---")
|
||||
# Create a large layer for performance testing
|
||||
perf_grid = mcrfpy.Grid(pos=(50, 50), size=(600, 500), grid_size=(100, 80), texture=texture)
|
||||
ui.append(perf_grid)
|
||||
perf_layer = perf_grid.add_layer("tile", z_index=-1, texture=texture)
|
||||
print("\n--- Test 6: Performance benchmark ---")
|
||||
# Create a large layer for performance testing
|
||||
perf_grid = mcrfpy.Grid(pos=(50, 50), size=(600, 500), grid_size=(100, 80), texture=texture)
|
||||
ui.append(perf_grid)
|
||||
perf_layer = perf_grid.add_layer("tile", z_index=-1, texture=texture)
|
||||
|
||||
# Fill with data
|
||||
perf_layer.fill(1)
|
||||
# Fill with data
|
||||
perf_layer.fill(1)
|
||||
|
||||
# First render will be slow (cache miss)
|
||||
start = time.time()
|
||||
test.activate() # Force render
|
||||
first_render = time.time() - start
|
||||
print(f" First render (cache build): {first_render*1000:.2f}ms")
|
||||
# Render a frame to build cache
|
||||
mcrfpy.step(0.01)
|
||||
|
||||
# Subsequent viewport changes should be fast (cache hit)
|
||||
# We simulate by changing center multiple times
|
||||
start = time.time()
|
||||
for i in range(5):
|
||||
perf_grid.center = (200 + i * 10, 160 + i * 8)
|
||||
viewport_changes = time.time() - start
|
||||
print(f" 5 viewport changes: {viewport_changes*1000:.2f}ms")
|
||||
# Subsequent viewport changes should be fast (cache hit)
|
||||
start = time.time()
|
||||
for i in range(5):
|
||||
perf_grid.center = (200 + i * 10, 160 + i * 8)
|
||||
viewport_changes = time.time() - start
|
||||
print(f" 5 viewport changes: {viewport_changes*1000:.2f}ms")
|
||||
|
||||
print(" PASS: Performance benchmark completed")
|
||||
print(" PASS: Performance benchmark completed")
|
||||
|
||||
print("\n--- Test 7: Layer visibility toggle ---")
|
||||
color_layer.visible = False
|
||||
print(" ColorLayer hidden")
|
||||
color_layer.visible = True
|
||||
print(" ColorLayer shown")
|
||||
print(" PASS: Visibility toggle works")
|
||||
print("\n--- Test 7: Layer visibility toggle ---")
|
||||
color_layer.visible = False
|
||||
print(" ColorLayer hidden")
|
||||
color_layer.visible = True
|
||||
print(" ColorLayer shown")
|
||||
print(" PASS: Visibility toggle works")
|
||||
|
||||
print("\n--- Test 8: Large grid stress test ---")
|
||||
# Test with maximum size grid to ensure texture caching works
|
||||
stress_grid = mcrfpy.Grid(pos=(10, 10), size=(200, 150), grid_size=(200, 150), texture=texture)
|
||||
ui.append(stress_grid)
|
||||
stress_layer = stress_grid.add_layer("color", z_index=-1)
|
||||
print("\n--- Test 8: Large grid stress test ---")
|
||||
# Test with maximum size grid to ensure texture caching works
|
||||
stress_grid = mcrfpy.Grid(pos=(10, 10), size=(200, 150), grid_size=(200, 150), texture=texture)
|
||||
ui.append(stress_grid)
|
||||
stress_layer = stress_grid.add_layer("color", z_index=-1)
|
||||
|
||||
# This would be 30,000 cells - should handle via caching
|
||||
stress_layer.fill(mcrfpy.Color(0, 100, 200, 100))
|
||||
# This would be 30,000 cells - should handle via caching
|
||||
stress_layer.fill(mcrfpy.Color(0, 100, 200, 100))
|
||||
|
||||
# Set a few specific cells
|
||||
for x in range(10):
|
||||
for y in range(10):
|
||||
stress_layer.set(x, y, mcrfpy.Color(255, 0, 0, 200))
|
||||
# Set a few specific cells
|
||||
for x in range(10):
|
||||
for y in range(10):
|
||||
stress_layer.set(x, y, mcrfpy.Color(255, 0, 0, 200))
|
||||
|
||||
print(" Created 200x150 grid with 30,000 cells")
|
||||
print(" PASS: Large grid handled successfully")
|
||||
print(" Created 200x150 grid with 30,000 cells")
|
||||
print(" PASS: Large grid handled successfully")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("All tests PASSED")
|
||||
print("=" * 60)
|
||||
print("\nNote: Dirty flag behavior is internal - tests verify API works")
|
||||
print("Actual caching benefits are measured by render performance.")
|
||||
sys.exit(0)
|
||||
|
||||
# Initialize and run
|
||||
init = mcrfpy.Scene("init")
|
||||
init.activate()
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
print("\n" + "=" * 60)
|
||||
print("All tests PASSED")
|
||||
print("=" * 60)
|
||||
print("\nNote: Dirty flag behavior is internal - tests verify API works")
|
||||
print("Actual caching benefits are measured by render performance.")
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -66,10 +66,10 @@ def test_entity_positions():
|
|||
if entity.grid_x != 4 or entity.grid_y != 6:
|
||||
errors.append(f"After setting pos, grid_x/y: expected (4, 6), got ({entity.grid_x}, {entity.grid_y})")
|
||||
|
||||
# Test 8: repr should show grid_x/grid_y
|
||||
# Test 8: repr should show position info
|
||||
repr_str = repr(entity)
|
||||
if "grid_x=" not in repr_str or "grid_y=" not in repr_str:
|
||||
errors.append(f"repr should contain grid_x/grid_y: {repr_str}")
|
||||
if "draw_pos=" not in repr_str:
|
||||
errors.append(f"repr should contain draw_pos: {repr_str}")
|
||||
|
||||
return errors
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import sys
|
|||
print("Starting test...")
|
||||
|
||||
# Create a simple grid without texture (should work in headless mode)
|
||||
grid = mcrfpy.Grid(grid_x=10, grid_y=8)
|
||||
grid = mcrfpy.Grid(grid_w=10, grid_h=8)
|
||||
print(f"Created grid: {grid}")
|
||||
|
||||
# Test various grid positions
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ def test_colorlayer_docs():
|
|||
"at(x, y)",
|
||||
"set(x, y",
|
||||
"fill(",
|
||||
"Grid.add_layer",
|
||||
"visible",
|
||||
"add_layer",
|
||||
"Example",
|
||||
]
|
||||
|
||||
|
|
@ -66,8 +65,7 @@ def test_tilelayer_docs():
|
|||
"fill(",
|
||||
"-1", # Special value for no tile
|
||||
"sprite",
|
||||
"Grid.add_layer",
|
||||
"visible",
|
||||
"add_layer",
|
||||
"Example",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #37: Windows scripts subdirectory not checked for .py files
|
||||
|
||||
This test checks if the game can find and load scripts/game.py from different working directories.
|
||||
On Windows, this often fails because fopen uses relative paths without resolving them.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
def test_script_loading():
|
||||
# Create a temporary directory to test from
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
print(f"Testing from directory: {tmpdir}")
|
||||
|
||||
# Get the build directory (assuming we're running from the repo root)
|
||||
build_dir = os.path.abspath("build")
|
||||
mcrogueface_exe = os.path.join(build_dir, "mcrogueface")
|
||||
if os.name == "nt": # Windows
|
||||
mcrogueface_exe += ".exe"
|
||||
|
||||
# Create a simple test script that the game should load
|
||||
test_script = """
|
||||
import mcrfpy
|
||||
print("TEST SCRIPT LOADED SUCCESSFULLY")
|
||||
test_scene = mcrfpy.Scene("test_scene")
|
||||
"""
|
||||
|
||||
# Save the original game.py
|
||||
game_py_path = os.path.join(build_dir, "scripts", "game.py")
|
||||
game_py_backup = game_py_path + ".backup"
|
||||
if os.path.exists(game_py_path):
|
||||
shutil.copy(game_py_path, game_py_backup)
|
||||
|
||||
try:
|
||||
# Replace game.py with our test script
|
||||
os.makedirs(os.path.dirname(game_py_path), exist_ok=True)
|
||||
with open(game_py_path, "w") as f:
|
||||
f.write(test_script)
|
||||
|
||||
# Test 1: Run from build directory (should work)
|
||||
print("\nTest 1: Running from build directory...")
|
||||
result = subprocess.run(
|
||||
[mcrogueface_exe, "--headless", "-c", "print('Test 1 complete')"],
|
||||
cwd=build_dir,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
if "TEST SCRIPT LOADED SUCCESSFULLY" in result.stdout:
|
||||
print("✓ Test 1 PASSED: Script loaded from build directory")
|
||||
else:
|
||||
print("✗ Test 1 FAILED: Script not loaded from build directory")
|
||||
print(f"stdout: {result.stdout}")
|
||||
print(f"stderr: {result.stderr}")
|
||||
|
||||
# Test 2: Run from temporary directory (often fails on Windows)
|
||||
print("\nTest 2: Running from different working directory...")
|
||||
result = subprocess.run(
|
||||
[mcrogueface_exe, "--headless", "-c", "print('Test 2 complete')"],
|
||||
cwd=tmpdir,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
if "TEST SCRIPT LOADED SUCCESSFULLY" in result.stdout:
|
||||
print("✓ Test 2 PASSED: Script loaded from different directory")
|
||||
else:
|
||||
print("✗ Test 2 FAILED: Script not loaded from different directory")
|
||||
print(f"stdout: {result.stdout}")
|
||||
print(f"stderr: {result.stderr}")
|
||||
print("\nThis is the bug described in Issue #37!")
|
||||
|
||||
finally:
|
||||
# Restore original game.py
|
||||
if os.path.exists(game_py_backup):
|
||||
shutil.move(game_py_backup, game_py_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_script_loading()
|
||||
|
|
@ -17,72 +17,68 @@ class CustomEntity(mcrfpy.Entity):
|
|||
def custom_method(self):
|
||||
return "Custom method called"
|
||||
|
||||
def run_test(timer, runtime):
|
||||
"""Test that derived entity classes maintain their type in collections"""
|
||||
try:
|
||||
# Create a grid
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||||
|
||||
# Create instances of base and derived entities
|
||||
base_entity = mcrfpy.Entity((1, 1))
|
||||
custom_entity = CustomEntity((2, 2))
|
||||
|
||||
# Add them to the grid's entity collection
|
||||
grid.entities.append(base_entity)
|
||||
grid.entities.append(custom_entity)
|
||||
|
||||
# Retrieve them back
|
||||
retrieved_base = grid.entities[0]
|
||||
retrieved_custom = grid.entities[1]
|
||||
|
||||
print(f"Base entity type: {type(retrieved_base)}")
|
||||
print(f"Custom entity type: {type(retrieved_custom)}")
|
||||
|
||||
# Test 1: Check if base entity is correct type
|
||||
if type(retrieved_base).__name__ == "Entity":
|
||||
print("✓ Test 1 PASSED: Base entity maintains correct type")
|
||||
else:
|
||||
print("✗ Test 1 FAILED: Base entity has wrong type")
|
||||
|
||||
# Test 2: Check if custom entity maintains its derived type
|
||||
if type(retrieved_custom).__name__ == "CustomEntity":
|
||||
print("✓ Test 2 PASSED: Derived entity maintains correct type")
|
||||
|
||||
# Test 3: Check if custom attributes are preserved
|
||||
try:
|
||||
attr = retrieved_custom.custom_attribute
|
||||
method_result = retrieved_custom.custom_method()
|
||||
print(f"✓ Test 3 PASSED: Custom attributes preserved - {attr}, {method_result}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ Test 3 FAILED: Custom attributes lost - {e}")
|
||||
else:
|
||||
print("✗ Test 2 FAILED: Derived entity type lost!")
|
||||
print("This is the bug described in Issue #76!")
|
||||
|
||||
# Try to access custom attributes anyway
|
||||
try:
|
||||
attr = retrieved_custom.custom_attribute
|
||||
print(f" - Has custom_attribute: {attr} (but wrong type)")
|
||||
except AttributeError:
|
||||
print(" - Lost custom_attribute")
|
||||
|
||||
# Test 4: Check iteration
|
||||
print("\nTesting iteration:")
|
||||
for i, entity in enumerate(grid.entities):
|
||||
print(f" Entity {i}: {type(entity).__name__}")
|
||||
|
||||
print("\nTest complete")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Test error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
test.activate()
|
||||
mcrfpy.current_scene = test
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
# Run the test
|
||||
try:
|
||||
# Create a grid
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||||
|
||||
# Create instances of base and derived entities
|
||||
base_entity = mcrfpy.Entity((1, 1))
|
||||
custom_entity = CustomEntity((2, 2))
|
||||
|
||||
# Add them to the grid's entity collection
|
||||
grid.entities.append(base_entity)
|
||||
grid.entities.append(custom_entity)
|
||||
|
||||
# Retrieve them back
|
||||
retrieved_base = grid.entities[0]
|
||||
retrieved_custom = grid.entities[1]
|
||||
|
||||
print(f"Base entity type: {type(retrieved_base)}")
|
||||
print(f"Custom entity type: {type(retrieved_custom)}")
|
||||
|
||||
# Test 1: Check if base entity is correct type
|
||||
if type(retrieved_base).__name__ == "Entity":
|
||||
print("PASS: Test 1 - Base entity maintains correct type")
|
||||
else:
|
||||
print("FAIL: Test 1 - Base entity has wrong type")
|
||||
|
||||
# Test 2: Check if custom entity maintains its derived type
|
||||
if type(retrieved_custom).__name__ == "CustomEntity":
|
||||
print("PASS: Test 2 - Derived entity maintains correct type")
|
||||
|
||||
# Test 3: Check if custom attributes are preserved
|
||||
try:
|
||||
attr = retrieved_custom.custom_attribute
|
||||
method_result = retrieved_custom.custom_method()
|
||||
print(f"PASS: Test 3 - Custom attributes preserved - {attr}, {method_result}")
|
||||
except AttributeError as e:
|
||||
print(f"FAIL: Test 3 - Custom attributes lost - {e}")
|
||||
else:
|
||||
print("FAIL: Test 2 - Derived entity type lost!")
|
||||
print("This is the bug described in Issue #76!")
|
||||
|
||||
# Try to access custom attributes anyway
|
||||
try:
|
||||
attr = retrieved_custom.custom_attribute
|
||||
print(f" - Has custom_attribute: {attr} (but wrong type)")
|
||||
except AttributeError:
|
||||
print(" - Lost custom_attribute")
|
||||
|
||||
# Test 4: Check iteration
|
||||
print("\nTesting iteration:")
|
||||
for i, entity in enumerate(grid.entities):
|
||||
print(f" Entity {i}: {type(entity).__name__}")
|
||||
|
||||
print("\nTest complete")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Test error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -11,160 +11,155 @@ import sys
|
|||
def test_color_properties():
|
||||
"""Test Color r, g, b, a property access and modification"""
|
||||
print("=== Testing Color r, g, b, a Properties (Issue #79) ===\n")
|
||||
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
|
||||
# Test 1: Create color and check properties
|
||||
print("--- Test 1: Basic property access ---")
|
||||
color1 = mcrfpy.Color(255, 128, 64, 32)
|
||||
|
||||
|
||||
tests_total += 1
|
||||
if color1.r == 255:
|
||||
print("✓ PASS: color.r returns correct value (255)")
|
||||
print("PASS: color.r returns correct value (255)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.r returned {color1.r} instead of 255")
|
||||
|
||||
print(f"FAIL: color.r returned {color1.r} instead of 255")
|
||||
|
||||
tests_total += 1
|
||||
if color1.g == 128:
|
||||
print("✓ PASS: color.g returns correct value (128)")
|
||||
print("PASS: color.g returns correct value (128)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.g returned {color1.g} instead of 128")
|
||||
|
||||
print(f"FAIL: color.g returned {color1.g} instead of 128")
|
||||
|
||||
tests_total += 1
|
||||
if color1.b == 64:
|
||||
print("✓ PASS: color.b returns correct value (64)")
|
||||
print("PASS: color.b returns correct value (64)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.b returned {color1.b} instead of 64")
|
||||
|
||||
print(f"FAIL: color.b returned {color1.b} instead of 64")
|
||||
|
||||
tests_total += 1
|
||||
if color1.a == 32:
|
||||
print("✓ PASS: color.a returns correct value (32)")
|
||||
print("PASS: color.a returns correct value (32)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.a returned {color1.a} instead of 32")
|
||||
|
||||
print(f"FAIL: color.a returned {color1.a} instead of 32")
|
||||
|
||||
# Test 2: Modify properties
|
||||
print("\n--- Test 2: Property modification ---")
|
||||
color1.r = 200
|
||||
color1.g = 100
|
||||
color1.b = 50
|
||||
color1.a = 25
|
||||
|
||||
|
||||
tests_total += 1
|
||||
if color1.r == 200:
|
||||
print("✓ PASS: color.r set successfully")
|
||||
print("PASS: color.r set successfully")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.r is {color1.r} after setting to 200")
|
||||
|
||||
print(f"FAIL: color.r is {color1.r} after setting to 200")
|
||||
|
||||
tests_total += 1
|
||||
if color1.g == 100:
|
||||
print("✓ PASS: color.g set successfully")
|
||||
print("PASS: color.g set successfully")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.g is {color1.g} after setting to 100")
|
||||
|
||||
print(f"FAIL: color.g is {color1.g} after setting to 100")
|
||||
|
||||
tests_total += 1
|
||||
if color1.b == 50:
|
||||
print("✓ PASS: color.b set successfully")
|
||||
print("PASS: color.b set successfully")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.b is {color1.b} after setting to 50")
|
||||
|
||||
print(f"FAIL: color.b is {color1.b} after setting to 50")
|
||||
|
||||
tests_total += 1
|
||||
if color1.a == 25:
|
||||
print("✓ PASS: color.a set successfully")
|
||||
print("PASS: color.a set successfully")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.a is {color1.a} after setting to 25")
|
||||
|
||||
print(f"FAIL: color.a is {color1.a} after setting to 25")
|
||||
|
||||
# Test 3: Boundary values
|
||||
print("\n--- Test 3: Boundary value tests ---")
|
||||
color2 = mcrfpy.Color(0, 0, 0, 0)
|
||||
|
||||
|
||||
tests_total += 1
|
||||
if color2.r == 0 and color2.g == 0 and color2.b == 0 and color2.a == 0:
|
||||
print("✓ PASS: Minimum values (0) work correctly")
|
||||
print("PASS: Minimum values (0) work correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Minimum values not working")
|
||||
|
||||
print("FAIL: Minimum values not working")
|
||||
|
||||
color3 = mcrfpy.Color(255, 255, 255, 255)
|
||||
tests_total += 1
|
||||
if color3.r == 255 and color3.g == 255 and color3.b == 255 and color3.a == 255:
|
||||
print("✓ PASS: Maximum values (255) work correctly")
|
||||
print("PASS: Maximum values (255) work correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Maximum values not working")
|
||||
|
||||
print("FAIL: Maximum values not working")
|
||||
|
||||
# Test 4: Invalid value handling
|
||||
print("\n--- Test 4: Invalid value handling ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
color3.r = 256 # Out of range
|
||||
print("✗ FAIL: Should have raised ValueError for value > 255")
|
||||
print("FAIL: Should have raised ValueError for value > 255")
|
||||
except ValueError as e:
|
||||
print(f"✓ PASS: Correctly raised ValueError: {e}")
|
||||
print(f"PASS: Correctly raised ValueError: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
|
||||
tests_total += 1
|
||||
try:
|
||||
color3.g = -1 # Out of range
|
||||
print("✗ FAIL: Should have raised ValueError for value < 0")
|
||||
print("FAIL: Should have raised ValueError for value < 0")
|
||||
except ValueError as e:
|
||||
print(f"✓ PASS: Correctly raised ValueError: {e}")
|
||||
print(f"PASS: Correctly raised ValueError: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
|
||||
tests_total += 1
|
||||
try:
|
||||
color3.b = "red" # Wrong type
|
||||
print("✗ FAIL: Should have raised TypeError for string value")
|
||||
print("FAIL: Should have raised TypeError for string value")
|
||||
except TypeError as e:
|
||||
print(f"✓ PASS: Correctly raised TypeError: {e}")
|
||||
print(f"PASS: Correctly raised TypeError: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
|
||||
# Test 5: Verify __repr__ shows correct values
|
||||
print("\n--- Test 5: String representation ---")
|
||||
color4 = mcrfpy.Color(10, 20, 30, 40)
|
||||
repr_str = repr(color4)
|
||||
tests_total += 1
|
||||
if "(10, 20, 30, 40)" in repr_str:
|
||||
print(f"✓ PASS: __repr__ shows correct values: {repr_str}")
|
||||
print(f"PASS: __repr__ shows correct values: {repr_str}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: __repr__ incorrect: {repr_str}")
|
||||
|
||||
print(f"FAIL: __repr__ incorrect: {repr_str}")
|
||||
|
||||
# Summary
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Tests passed: {tests_passed}/{tests_total}")
|
||||
|
||||
|
||||
if tests_passed == tests_total:
|
||||
print("\nIssue #79 FIXED: Color properties now work correctly!")
|
||||
else:
|
||||
print("\nIssue #79: Some tests failed")
|
||||
|
||||
return tests_passed == tests_total
|
||||
|
||||
def run_test(timer, runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
success = test_color_properties()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
return tests_passed == tests_total
|
||||
|
||||
# Set up the test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
test.activate()
|
||||
mcrfpy.current_scene = test
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
try:
|
||||
success = test_color_properties()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -12,213 +12,208 @@ import sys
|
|||
def test_texture_properties():
|
||||
"""Test Texture properties"""
|
||||
print("=== Testing Texture Properties ===")
|
||||
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
|
||||
# Create a texture
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
|
||||
|
||||
# Test 1: sprite_width property
|
||||
tests_total += 1
|
||||
try:
|
||||
width = texture.sprite_width
|
||||
if width == 16:
|
||||
print(f"✓ PASS: sprite_width = {width}")
|
||||
print(f"PASS: sprite_width = {width}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite_width = {width}, expected 16")
|
||||
print(f"FAIL: sprite_width = {width}, expected 16")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sprite_width not accessible: {e}")
|
||||
|
||||
print(f"FAIL: sprite_width not accessible: {e}")
|
||||
|
||||
# Test 2: sprite_height property
|
||||
tests_total += 1
|
||||
try:
|
||||
height = texture.sprite_height
|
||||
if height == 16:
|
||||
print(f"✓ PASS: sprite_height = {height}")
|
||||
print(f"PASS: sprite_height = {height}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite_height = {height}, expected 16")
|
||||
print(f"FAIL: sprite_height = {height}, expected 16")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sprite_height not accessible: {e}")
|
||||
|
||||
print(f"FAIL: sprite_height not accessible: {e}")
|
||||
|
||||
# Test 3: sheet_width property
|
||||
tests_total += 1
|
||||
try:
|
||||
sheet_w = texture.sheet_width
|
||||
if isinstance(sheet_w, int) and sheet_w > 0:
|
||||
print(f"✓ PASS: sheet_width = {sheet_w}")
|
||||
print(f"PASS: sheet_width = {sheet_w}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sheet_width invalid: {sheet_w}")
|
||||
print(f"FAIL: sheet_width invalid: {sheet_w}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sheet_width not accessible: {e}")
|
||||
|
||||
print(f"FAIL: sheet_width not accessible: {e}")
|
||||
|
||||
# Test 4: sheet_height property
|
||||
tests_total += 1
|
||||
try:
|
||||
sheet_h = texture.sheet_height
|
||||
if isinstance(sheet_h, int) and sheet_h > 0:
|
||||
print(f"✓ PASS: sheet_height = {sheet_h}")
|
||||
print(f"PASS: sheet_height = {sheet_h}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sheet_height invalid: {sheet_h}")
|
||||
print(f"FAIL: sheet_height invalid: {sheet_h}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sheet_height not accessible: {e}")
|
||||
|
||||
print(f"FAIL: sheet_height not accessible: {e}")
|
||||
|
||||
# Test 5: sprite_count property
|
||||
tests_total += 1
|
||||
try:
|
||||
count = texture.sprite_count
|
||||
expected = texture.sheet_width * texture.sheet_height
|
||||
if count == expected:
|
||||
print(f"✓ PASS: sprite_count = {count} (sheet_width * sheet_height)")
|
||||
print(f"PASS: sprite_count = {count} (sheet_width * sheet_height)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite_count = {count}, expected {expected}")
|
||||
print(f"FAIL: sprite_count = {count}, expected {expected}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sprite_count not accessible: {e}")
|
||||
|
||||
print(f"FAIL: sprite_count not accessible: {e}")
|
||||
|
||||
# Test 6: source property
|
||||
tests_total += 1
|
||||
try:
|
||||
source = texture.source
|
||||
if "kenney_tinydungeon.png" in source:
|
||||
print(f"✓ PASS: source = '{source}'")
|
||||
print(f"PASS: source = '{source}'")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: source unexpected: '{source}'")
|
||||
print(f"FAIL: source unexpected: '{source}'")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: source not accessible: {e}")
|
||||
|
||||
print(f"FAIL: source not accessible: {e}")
|
||||
|
||||
# Test 7: Properties are read-only
|
||||
tests_total += 1
|
||||
try:
|
||||
texture.sprite_width = 32 # Should fail
|
||||
print("✗ FAIL: sprite_width should be read-only")
|
||||
print("FAIL: sprite_width should be read-only")
|
||||
except AttributeError as e:
|
||||
print(f"✓ PASS: sprite_width is read-only: {e}")
|
||||
print(f"PASS: sprite_width is read-only: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_font_properties():
|
||||
"""Test Font properties"""
|
||||
print("\n=== Testing Font Properties ===")
|
||||
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
|
||||
# Create a font
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
|
||||
|
||||
# Test 1: family property
|
||||
tests_total += 1
|
||||
try:
|
||||
family = font.family
|
||||
if isinstance(family, str) and len(family) > 0:
|
||||
print(f"✓ PASS: family = '{family}'")
|
||||
print(f"PASS: family = '{family}'")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: family invalid: '{family}'")
|
||||
print(f"FAIL: family invalid: '{family}'")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: family not accessible: {e}")
|
||||
|
||||
print(f"FAIL: family not accessible: {e}")
|
||||
|
||||
# Test 2: source property
|
||||
tests_total += 1
|
||||
try:
|
||||
source = font.source
|
||||
if "JetbrainsMono.ttf" in source:
|
||||
print(f"✓ PASS: source = '{source}'")
|
||||
print(f"PASS: source = '{source}'")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: source unexpected: '{source}'")
|
||||
print(f"FAIL: source unexpected: '{source}'")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: source not accessible: {e}")
|
||||
|
||||
print(f"FAIL: source not accessible: {e}")
|
||||
|
||||
# Test 3: Properties are read-only
|
||||
tests_total += 1
|
||||
try:
|
||||
font.family = "Arial" # Should fail
|
||||
print("✗ FAIL: family should be read-only")
|
||||
print("FAIL: family should be read-only")
|
||||
except AttributeError as e:
|
||||
print(f"✓ PASS: family is read-only: {e}")
|
||||
print(f"PASS: family is read-only: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_property_introspection():
|
||||
"""Test that properties appear in dir()"""
|
||||
print("\n=== Testing Property Introspection ===")
|
||||
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
|
||||
# Test Texture properties in dir()
|
||||
tests_total += 1
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
texture_props = dir(texture)
|
||||
expected_texture_props = ['sprite_width', 'sprite_height', 'sheet_width', 'sheet_height', 'sprite_count', 'source']
|
||||
|
||||
|
||||
missing = [p for p in expected_texture_props if p not in texture_props]
|
||||
if not missing:
|
||||
print("✓ PASS: All Texture properties appear in dir()")
|
||||
print("PASS: All Texture properties appear in dir()")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Missing Texture properties in dir(): {missing}")
|
||||
|
||||
print(f"FAIL: Missing Texture properties in dir(): {missing}")
|
||||
|
||||
# Test Font properties in dir()
|
||||
tests_total += 1
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
font_props = dir(font)
|
||||
expected_font_props = ['family', 'source']
|
||||
|
||||
|
||||
missing = [p for p in expected_font_props if p not in font_props]
|
||||
if not missing:
|
||||
print("✓ PASS: All Font properties appear in dir()")
|
||||
print("PASS: All Font properties appear in dir()")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Missing Font properties in dir(): {missing}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
print(f"FAIL: Missing Font properties in dir(): {missing}")
|
||||
|
||||
def run_test(timer, runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
print("=== Testing Texture and Font Properties (Issue #99) ===\n")
|
||||
|
||||
texture_passed, texture_total = test_texture_properties()
|
||||
font_passed, font_total = test_font_properties()
|
||||
intro_passed, intro_total = test_property_introspection()
|
||||
|
||||
total_passed = texture_passed + font_passed + intro_passed
|
||||
total_tests = texture_total + font_total + intro_total
|
||||
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Texture tests: {texture_passed}/{texture_total}")
|
||||
print(f"Font tests: {font_passed}/{font_total}")
|
||||
print(f"Introspection tests: {intro_passed}/{intro_total}")
|
||||
print(f"Total tests passed: {total_passed}/{total_tests}")
|
||||
|
||||
if total_passed == total_tests:
|
||||
print("\nIssue #99 FIXED: Texture and Font properties exposed successfully!")
|
||||
print("\nOverall result: PASS")
|
||||
else:
|
||||
print("\nIssue #99: Some tests failed")
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
return tests_passed, tests_total
|
||||
|
||||
# Set up the test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
test.activate()
|
||||
mcrfpy.current_scene = test
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
try:
|
||||
print("=== Testing Texture and Font Properties (Issue #99) ===\n")
|
||||
|
||||
texture_passed, texture_total = test_texture_properties()
|
||||
font_passed, font_total = test_font_properties()
|
||||
intro_passed, intro_total = test_property_introspection()
|
||||
|
||||
total_passed = texture_passed + font_passed + intro_passed
|
||||
total_tests = texture_total + font_total + intro_total
|
||||
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Texture tests: {texture_passed}/{texture_total}")
|
||||
print(f"Font tests: {font_passed}/{font_total}")
|
||||
print(f"Introspection tests: {intro_passed}/{intro_total}")
|
||||
print(f"Total tests passed: {total_passed}/{total_tests}")
|
||||
|
||||
if total_passed == total_tests:
|
||||
print("\nIssue #99 FIXED: Texture and Font properties exposed successfully!")
|
||||
print("\nOverall result: PASS")
|
||||
else:
|
||||
print("\nIssue #99: Some tests failed")
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Minimal test for Issue #9: RenderTexture resize
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
|
||||
def run_test(timer, runtime):
|
||||
"""Test RenderTexture resizing"""
|
||||
print("Testing Issue #9: RenderTexture resize (minimal)")
|
||||
|
||||
try:
|
||||
# Create a grid
|
||||
print("Creating grid...")
|
||||
grid = mcrfpy.Grid(30, 30)
|
||||
grid.x = 10
|
||||
grid.y = 10
|
||||
grid.w = 300
|
||||
grid.h = 300
|
||||
|
||||
# Add to scene
|
||||
scene_ui = test.children
|
||||
scene_ui.append(grid)
|
||||
|
||||
# Test accessing grid points
|
||||
print("Testing grid.at()...")
|
||||
point = grid.at(5, 5)
|
||||
print(f"Got grid point: {point}")
|
||||
|
||||
# Test color creation
|
||||
print("Testing Color creation...")
|
||||
red = mcrfpy.Color(255, 0, 0, 255)
|
||||
print(f"Created color: {red}")
|
||||
|
||||
# Set color
|
||||
print("Setting grid point color...")
|
||||
point.color = red
|
||||
|
||||
print("Taking screenshot before resize...")
|
||||
automation.screenshot("/tmp/issue_9_minimal_before.png")
|
||||
|
||||
# Resize grid
|
||||
print("Resizing grid to 2500x2500...")
|
||||
grid.w = 2500
|
||||
grid.h = 2500
|
||||
|
||||
print("Taking screenshot after resize...")
|
||||
automation.screenshot("/tmp/issue_9_minimal_after.png")
|
||||
|
||||
print("\nTest complete - check screenshots")
|
||||
print("If RenderTexture is recreated properly, grid should render correctly at large size")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Create and set scene
|
||||
test = mcrfpy.Scene("test")
|
||||
test.activate()
|
||||
|
||||
# Schedule test
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
|
|
@ -28,202 +28,193 @@ def add_border_markers(grid, grid_width, grid_height):
|
|||
# Red border on top
|
||||
for x in range(grid_width):
|
||||
grid.at(x, 0).color = mcrfpy.Color(255, 0, 0, 255)
|
||||
|
||||
|
||||
# Green border on right
|
||||
for y in range(grid_height):
|
||||
grid.at(grid_width-1, y).color = mcrfpy.Color(0, 255, 0, 255)
|
||||
|
||||
|
||||
# Blue border on bottom
|
||||
for x in range(grid_width):
|
||||
grid.at(x, grid_height-1).color = mcrfpy.Color(0, 0, 255, 255)
|
||||
|
||||
|
||||
# Yellow border on left
|
||||
for y in range(grid_height):
|
||||
grid.at(0, y).color = mcrfpy.Color(255, 255, 0, 255)
|
||||
|
||||
def test_rendertexture_resize():
|
||||
"""Test RenderTexture behavior with various grid sizes"""
|
||||
print("=== Testing UIGrid RenderTexture Resize (Issue #9) ===\n")
|
||||
|
||||
scene_ui = test.children
|
||||
|
||||
# Test 1: Small grid (should work fine)
|
||||
print("--- Test 1: Small Grid (400x300) ---")
|
||||
grid1 = mcrfpy.Grid(20, 15) # 20x15 tiles
|
||||
grid1.x = 10
|
||||
grid1.y = 10
|
||||
grid1.w = 400
|
||||
grid1.h = 300
|
||||
scene_ui.append(grid1)
|
||||
|
||||
create_checkerboard_pattern(grid1, 20, 15)
|
||||
add_border_markers(grid1, 20, 15)
|
||||
|
||||
automation.screenshot("/tmp/issue_9_small_grid.png")
|
||||
print("✓ Small grid created and rendered")
|
||||
|
||||
# Test 2: Medium grid at 1920x1080 limit
|
||||
print("\n--- Test 2: Medium Grid at 1920x1080 Limit ---")
|
||||
grid2 = mcrfpy.Grid(64, 36) # 64x36 tiles at 30px each = 1920x1080
|
||||
grid2.x = 10
|
||||
grid2.y = 320
|
||||
grid2.w = 1920
|
||||
grid2.h = 1080
|
||||
scene_ui.append(grid2)
|
||||
|
||||
create_checkerboard_pattern(grid2, 64, 36, 4)
|
||||
add_border_markers(grid2, 64, 36)
|
||||
|
||||
automation.screenshot("/tmp/issue_9_limit_grid.png")
|
||||
print("✓ Grid at RenderTexture limit created")
|
||||
|
||||
# Test 3: Resize grid1 beyond limits
|
||||
print("\n--- Test 3: Resizing Small Grid Beyond 1920x1080 ---")
|
||||
print("Original size: 400x300")
|
||||
grid1.w = 2400
|
||||
grid1.h = 1400
|
||||
print(f"Resized to: {grid1.w}x{grid1.h}")
|
||||
|
||||
# The content should still be visible but may be clipped
|
||||
automation.screenshot("/tmp/issue_9_resized_beyond_limit.png")
|
||||
print("✗ EXPECTED ISSUE: Grid resized beyond RenderTexture limits")
|
||||
print(" Content beyond 1920x1080 will be clipped!")
|
||||
|
||||
# Test 4: Create large grid from start
|
||||
print("\n--- Test 4: Large Grid from Start (2400x1400) ---")
|
||||
# Clear previous grids
|
||||
while len(scene_ui) > 0:
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid3 = mcrfpy.Grid(80, 50) # Large tile count
|
||||
grid3.x = 10
|
||||
grid3.y = 10
|
||||
grid3.w = 2400
|
||||
grid3.h = 1400
|
||||
scene_ui.append(grid3)
|
||||
|
||||
create_checkerboard_pattern(grid3, 80, 50, 5)
|
||||
add_border_markers(grid3, 80, 50)
|
||||
|
||||
# Add markers at specific positions to test rendering
|
||||
# Mark the center
|
||||
center_x, center_y = 40, 25
|
||||
for dx in range(-2, 3):
|
||||
for dy in range(-2, 3):
|
||||
grid3.at(center_x + dx, center_y + dy).color = mcrfpy.Color(255, 0, 255, 255) # Magenta
|
||||
|
||||
# Mark position at 1920 pixel boundary (64 tiles * 30 pixels/tile = 1920)
|
||||
if 64 < 80: # Only if within grid bounds
|
||||
for y in range(min(50, 10)):
|
||||
grid3.at(64, y).color = mcrfpy.Color(255, 128, 0, 255) # Orange
|
||||
|
||||
automation.screenshot("/tmp/issue_9_large_grid.png")
|
||||
print("✗ EXPECTED ISSUE: Large grid created")
|
||||
print(" Content beyond 1920x1080 will not render!")
|
||||
print(" Look for missing orange line at x=1920 boundary")
|
||||
|
||||
# Test 5: Dynamic resize test
|
||||
print("\n--- Test 5: Dynamic Resize Test ---")
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid4 = mcrfpy.Grid(100, 100)
|
||||
grid4.x = 10
|
||||
grid4.y = 10
|
||||
scene_ui.append(grid4)
|
||||
|
||||
sizes = [(500, 500), (1000, 1000), (1500, 1500), (2000, 2000), (2500, 2500)]
|
||||
|
||||
for i, (w, h) in enumerate(sizes):
|
||||
grid4.w = w
|
||||
grid4.h = h
|
||||
|
||||
# Add pattern at current size
|
||||
visible_tiles_x = min(100, w // 30)
|
||||
visible_tiles_y = min(100, h // 30)
|
||||
|
||||
# Clear and create new pattern
|
||||
for x in range(visible_tiles_x):
|
||||
for y in range(visible_tiles_y):
|
||||
if x == visible_tiles_x - 1 or y == visible_tiles_y - 1:
|
||||
# Edge markers
|
||||
grid4.at(x, y).color = mcrfpy.Color(255, 255, 0, 255)
|
||||
elif (x + y) % 10 == 0:
|
||||
# Diagonal lines
|
||||
grid4.at(x, y).color = mcrfpy.Color(0, 255, 255, 255)
|
||||
|
||||
automation.screenshot(f"/tmp/issue_9_resize_{w}x{h}.png")
|
||||
|
||||
if w > 1920 or h > 1080:
|
||||
print(f"✗ Size {w}x{h}: Content clipped at 1920x1080")
|
||||
else:
|
||||
print(f"✓ Size {w}x{h}: Rendered correctly")
|
||||
|
||||
# Test 6: Verify exact clipping boundary
|
||||
print("\n--- Test 6: Exact Clipping Boundary Test ---")
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid5 = mcrfpy.Grid(70, 40)
|
||||
grid5.x = 0
|
||||
grid5.y = 0
|
||||
grid5.w = 2100 # 70 * 30 = 2100 pixels
|
||||
grid5.h = 1200 # 40 * 30 = 1200 pixels
|
||||
scene_ui.append(grid5)
|
||||
|
||||
# Create a pattern that shows the boundary clearly
|
||||
for x in range(70):
|
||||
for y in range(40):
|
||||
pixel_x = x * 30
|
||||
pixel_y = y * 30
|
||||
|
||||
if pixel_x == 1920 - 30: # Last tile before boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(255, 0, 0, 255) # Red
|
||||
elif pixel_x == 1920: # First tile after boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(0, 255, 0, 255) # Green
|
||||
elif pixel_y == 1080 - 30: # Last row before boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(0, 0, 255, 255) # Blue
|
||||
elif pixel_y == 1080: # First row after boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(255, 255, 0, 255) # Yellow
|
||||
else:
|
||||
# Normal checkerboard
|
||||
if (x + y) % 2 == 0:
|
||||
grid5.at(x, y).color = mcrfpy.Color(200, 200, 200, 255)
|
||||
|
||||
automation.screenshot("/tmp/issue_9_boundary_test.png")
|
||||
print("Screenshot saved showing clipping boundary")
|
||||
print("- Red tiles: Last visible column (x=1890-1919)")
|
||||
print("- Green tiles: First clipped column (x=1920+)")
|
||||
print("- Blue tiles: Last visible row (y=1050-1079)")
|
||||
print("- Yellow tiles: First clipped row (y=1080+)")
|
||||
|
||||
# Summary
|
||||
print("\n=== SUMMARY ===")
|
||||
print("Issue #9: UIGrid uses a hardcoded RenderTexture size of 1920x1080")
|
||||
print("Problems demonstrated:")
|
||||
print("1. Grids larger than 1920x1080 are clipped")
|
||||
print("2. Resizing grids doesn't recreate the RenderTexture")
|
||||
print("3. Content beyond the boundary is not rendered")
|
||||
print("\nThe fix should:")
|
||||
print("1. Recreate RenderTexture when grid size changes")
|
||||
print("2. Use the actual grid dimensions instead of hardcoded values")
|
||||
print("3. Consider memory limits for very large grids")
|
||||
|
||||
print(f"\nScreenshots saved to /tmp/issue_9_*.png")
|
||||
|
||||
def run_test(timer, runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
test_rendertexture_resize()
|
||||
print("\nTest complete - check screenshots for visual verification")
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
test.activate()
|
||||
mcrfpy.current_scene = test
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
print("=== Testing UIGrid RenderTexture Resize (Issue #9) ===\n")
|
||||
|
||||
scene_ui = test.children
|
||||
|
||||
# Test 1: Small grid (should work fine)
|
||||
print("--- Test 1: Small Grid (400x300) ---")
|
||||
grid1 = mcrfpy.Grid(20, 15) # 20x15 tiles
|
||||
grid1.x = 10
|
||||
grid1.y = 10
|
||||
grid1.w = 400
|
||||
grid1.h = 300
|
||||
scene_ui.append(grid1)
|
||||
|
||||
create_checkerboard_pattern(grid1, 20, 15)
|
||||
add_border_markers(grid1, 20, 15)
|
||||
|
||||
mcrfpy.step(0.01)
|
||||
automation.screenshot("/tmp/issue_9_small_grid.png")
|
||||
print("PASS: Small grid created and rendered")
|
||||
|
||||
# Test 2: Medium grid at 1920x1080 limit
|
||||
print("\n--- Test 2: Medium Grid at 1920x1080 Limit ---")
|
||||
grid2 = mcrfpy.Grid(64, 36) # 64x36 tiles at 30px each = 1920x1080
|
||||
grid2.x = 10
|
||||
grid2.y = 320
|
||||
grid2.w = 1920
|
||||
grid2.h = 1080
|
||||
scene_ui.append(grid2)
|
||||
|
||||
create_checkerboard_pattern(grid2, 64, 36, 4)
|
||||
add_border_markers(grid2, 64, 36)
|
||||
|
||||
mcrfpy.step(0.01)
|
||||
automation.screenshot("/tmp/issue_9_limit_grid.png")
|
||||
print("PASS: Grid at RenderTexture limit created")
|
||||
|
||||
# Test 3: Resize grid1 beyond limits
|
||||
print("\n--- Test 3: Resizing Small Grid Beyond 1920x1080 ---")
|
||||
print("Original size: 400x300")
|
||||
grid1.w = 2400
|
||||
grid1.h = 1400
|
||||
print(f"Resized to: {grid1.w}x{grid1.h}")
|
||||
|
||||
# The content should still be visible but may be clipped
|
||||
mcrfpy.step(0.01)
|
||||
automation.screenshot("/tmp/issue_9_resized_beyond_limit.png")
|
||||
print("EXPECTED ISSUE: Grid resized beyond RenderTexture limits")
|
||||
print(" Content beyond 1920x1080 will be clipped!")
|
||||
|
||||
# Test 4: Create large grid from start
|
||||
print("\n--- Test 4: Large Grid from Start (2400x1400) ---")
|
||||
# Clear previous grids
|
||||
while len(scene_ui) > 0:
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid3 = mcrfpy.Grid(80, 50) # Large tile count
|
||||
grid3.x = 10
|
||||
grid3.y = 10
|
||||
grid3.w = 2400
|
||||
grid3.h = 1400
|
||||
scene_ui.append(grid3)
|
||||
|
||||
create_checkerboard_pattern(grid3, 80, 50, 5)
|
||||
add_border_markers(grid3, 80, 50)
|
||||
|
||||
# Add markers at specific positions to test rendering
|
||||
# Mark the center
|
||||
center_x, center_y = 40, 25
|
||||
for dx in range(-2, 3):
|
||||
for dy in range(-2, 3):
|
||||
grid3.at(center_x + dx, center_y + dy).color = mcrfpy.Color(255, 0, 255, 255) # Magenta
|
||||
|
||||
# Mark position at 1920 pixel boundary (64 tiles * 30 pixels/tile = 1920)
|
||||
if 64 < 80: # Only if within grid bounds
|
||||
for y in range(min(50, 10)):
|
||||
grid3.at(64, y).color = mcrfpy.Color(255, 128, 0, 255) # Orange
|
||||
|
||||
mcrfpy.step(0.01)
|
||||
automation.screenshot("/tmp/issue_9_large_grid.png")
|
||||
print("EXPECTED ISSUE: Large grid created")
|
||||
print(" Content beyond 1920x1080 will not render!")
|
||||
print(" Look for missing orange line at x=1920 boundary")
|
||||
|
||||
# Test 5: Dynamic resize test
|
||||
print("\n--- Test 5: Dynamic Resize Test ---")
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid4 = mcrfpy.Grid(100, 100)
|
||||
grid4.x = 10
|
||||
grid4.y = 10
|
||||
scene_ui.append(grid4)
|
||||
|
||||
sizes = [(500, 500), (1000, 1000), (1500, 1500), (2000, 2000), (2500, 2500)]
|
||||
|
||||
for i, (w, h) in enumerate(sizes):
|
||||
grid4.w = w
|
||||
grid4.h = h
|
||||
|
||||
# Add pattern at current size
|
||||
visible_tiles_x = min(100, w // 30)
|
||||
visible_tiles_y = min(100, h // 30)
|
||||
|
||||
# Clear and create new pattern
|
||||
for x in range(visible_tiles_x):
|
||||
for y in range(visible_tiles_y):
|
||||
if x == visible_tiles_x - 1 or y == visible_tiles_y - 1:
|
||||
# Edge markers
|
||||
grid4.at(x, y).color = mcrfpy.Color(255, 255, 0, 255)
|
||||
elif (x + y) % 10 == 0:
|
||||
# Diagonal lines
|
||||
grid4.at(x, y).color = mcrfpy.Color(0, 255, 255, 255)
|
||||
|
||||
mcrfpy.step(0.01)
|
||||
automation.screenshot(f"/tmp/issue_9_resize_{w}x{h}.png")
|
||||
|
||||
if w > 1920 or h > 1080:
|
||||
print(f"FAIL: Size {w}x{h}: Content clipped at 1920x1080")
|
||||
else:
|
||||
print(f"PASS: Size {w}x{h}: Rendered correctly")
|
||||
|
||||
# Test 6: Verify exact clipping boundary
|
||||
print("\n--- Test 6: Exact Clipping Boundary Test ---")
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid5 = mcrfpy.Grid(70, 40)
|
||||
grid5.x = 0
|
||||
grid5.y = 0
|
||||
grid5.w = 2100 # 70 * 30 = 2100 pixels
|
||||
grid5.h = 1200 # 40 * 30 = 1200 pixels
|
||||
scene_ui.append(grid5)
|
||||
|
||||
# Create a pattern that shows the boundary clearly
|
||||
for x in range(70):
|
||||
for y in range(40):
|
||||
pixel_x = x * 30
|
||||
pixel_y = y * 30
|
||||
|
||||
if pixel_x == 1920 - 30: # Last tile before boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(255, 0, 0, 255) # Red
|
||||
elif pixel_x == 1920: # First tile after boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(0, 255, 0, 255) # Green
|
||||
elif pixel_y == 1080 - 30: # Last row before boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(0, 0, 255, 255) # Blue
|
||||
elif pixel_y == 1080: # First row after boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(255, 255, 0, 255) # Yellow
|
||||
else:
|
||||
# Normal checkerboard
|
||||
if (x + y) % 2 == 0:
|
||||
grid5.at(x, y).color = mcrfpy.Color(200, 200, 200, 255)
|
||||
|
||||
mcrfpy.step(0.01)
|
||||
automation.screenshot("/tmp/issue_9_boundary_test.png")
|
||||
print("Screenshot saved showing clipping boundary")
|
||||
print("- Red tiles: Last visible column (x=1890-1919)")
|
||||
print("- Green tiles: First clipped column (x=1920+)")
|
||||
print("- Blue tiles: Last visible row (y=1050-1079)")
|
||||
print("- Yellow tiles: First clipped row (y=1080+)")
|
||||
|
||||
# Summary
|
||||
print("\n=== SUMMARY ===")
|
||||
print("Issue #9: UIGrid uses a hardcoded RenderTexture size of 1920x1080")
|
||||
print("Problems demonstrated:")
|
||||
print("1. Grids larger than 1920x1080 are clipped")
|
||||
print("2. Resizing grids doesn't recreate the RenderTexture")
|
||||
print("3. Content beyond the boundary is not rendered")
|
||||
print("\nThe fix should:")
|
||||
print("1. Recreate RenderTexture when grid size changes")
|
||||
print("2. Use the actual grid dimensions instead of hardcoded values")
|
||||
print("3. Consider memory limits for very large grids")
|
||||
|
||||
print(f"\nScreenshots saved to /tmp/issue_9_*.png")
|
||||
print("\nTest complete - check screenshots for visual verification")
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #9: Recreate RenderTexture when UIGrid is resized
|
||||
|
||||
This test checks if resizing a UIGrid properly recreates its RenderTexture.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
|
||||
def run_test(timer, runtime):
|
||||
"""Test that UIGrid properly handles resizing"""
|
||||
try:
|
||||
# Create a grid with initial size
|
||||
grid = mcrfpy.Grid(20, 20)
|
||||
grid.x = 50
|
||||
grid.y = 50
|
||||
grid.w = 200
|
||||
grid.h = 200
|
||||
|
||||
# Add grid to scene
|
||||
scene_ui = test.children
|
||||
scene_ui.append(grid)
|
||||
|
||||
# Take initial screenshot
|
||||
automation.screenshot("/tmp/grid_initial.png")
|
||||
print("Initial grid created at 200x200")
|
||||
|
||||
# Add some visible content to the grid
|
||||
for x in range(5):
|
||||
for y in range(5):
|
||||
grid.at(x, y).color = mcrfpy.Color(255, 0, 0, 255) # Red squares
|
||||
|
||||
automation.screenshot("/tmp/grid_with_content.png")
|
||||
print("Added red squares to grid")
|
||||
|
||||
# Test 1: Resize the grid smaller
|
||||
print("\nTest 1: Resizing grid to 100x100...")
|
||||
grid.w = 100
|
||||
grid.h = 100
|
||||
|
||||
automation.screenshot("/tmp/grid_resized_small.png")
|
||||
|
||||
# The grid should still render correctly
|
||||
print("✓ Test 1: Grid resized to 100x100")
|
||||
|
||||
# Test 2: Resize the grid larger than initial
|
||||
print("\nTest 2: Resizing grid to 400x400...")
|
||||
grid.w = 400
|
||||
grid.h = 400
|
||||
|
||||
automation.screenshot("/tmp/grid_resized_large.png")
|
||||
|
||||
# Add content at the edges to test if render texture is big enough
|
||||
for x in range(15, 20):
|
||||
for y in range(15, 20):
|
||||
grid.at(x, y).color = mcrfpy.Color(0, 255, 0, 255) # Green squares
|
||||
|
||||
automation.screenshot("/tmp/grid_resized_with_edge_content.png")
|
||||
print("✓ Test 2: Grid resized to 400x400 with edge content")
|
||||
|
||||
# Test 3: Resize beyond the hardcoded 1920x1080 limit
|
||||
print("\nTest 3: Resizing grid beyond 1920x1080...")
|
||||
grid.w = 2000
|
||||
grid.h = 1200
|
||||
|
||||
automation.screenshot("/tmp/grid_resized_huge.png")
|
||||
|
||||
# This should fail with the current implementation
|
||||
print("✗ Test 3: This likely shows rendering errors due to fixed RenderTexture size")
|
||||
print("This is the bug described in Issue #9!")
|
||||
|
||||
print("\nScreenshots saved to /tmp/grid_*.png")
|
||||
print("Check grid_resized_huge.png for rendering artifacts")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Test error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
test = mcrfpy.Scene("test")
|
||||
test.activate()
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
|
|
@ -15,69 +15,45 @@ import sys
|
|||
def demonstrate_solution():
|
||||
"""Demonstrate how the solution should work"""
|
||||
print("=== Type Preservation Solution Demonstration ===\n")
|
||||
|
||||
|
||||
print("Current behavior (broken):")
|
||||
print("1. Python creates derived object (e.g., MyFrame extends Frame)")
|
||||
print("2. C++ stores only the shared_ptr<UIFrame>")
|
||||
print("3. When retrieved, C++ creates a NEW PyUIFrameObject with type 'Frame'")
|
||||
print("4. Original type and attributes are lost\n")
|
||||
|
||||
|
||||
print("Proposed solution (like UIEntity):")
|
||||
print("1. Add PyObject* self member to UIDrawable base class")
|
||||
print("2. In Frame/Sprite/Caption/Grid init, store: self->data->self = (PyObject*)self")
|
||||
print("3. In convertDrawableToPython, check if drawable->self exists")
|
||||
print("4. If it exists, return the stored Python object (with INCREF)")
|
||||
print("5. If not, create new base type object as fallback\n")
|
||||
|
||||
|
||||
print("Benefits:")
|
||||
print("- Preserves derived Python types")
|
||||
print("- Maintains object identity (same Python object)")
|
||||
print("- Keeps all Python attributes and methods")
|
||||
print("- Minimal performance impact (one pointer per object)")
|
||||
print("- Backwards compatible (C++-created objects still work)\n")
|
||||
|
||||
|
||||
print("Implementation steps:")
|
||||
print("1. Add 'PyObject* self = nullptr;' to UIDrawable class")
|
||||
print("2. Update Frame/Sprite/Caption/Grid init methods to store self")
|
||||
print("3. Update convertDrawableToPython in UICollection.cpp")
|
||||
print("4. Handle reference counting properly (INCREF/DECREF)")
|
||||
print("5. Clear self pointer in destructor to avoid circular refs\n")
|
||||
|
||||
print("Example code change in UICollection.cpp:")
|
||||
print("""
|
||||
static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
|
||||
if (!drawable) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// Check if we have a stored Python object reference
|
||||
if (drawable->self != nullptr) {
|
||||
// Return the original Python object, preserving its type
|
||||
Py_INCREF(drawable->self);
|
||||
return drawable->self;
|
||||
}
|
||||
|
||||
// Otherwise, create new object as before (fallback for C++-created objects)
|
||||
PyTypeObject* type = nullptr;
|
||||
PyObject* obj = nullptr;
|
||||
// ... existing switch statement ...
|
||||
}
|
||||
""")
|
||||
|
||||
def run_test(timer, runtime):
|
||||
"""Timer callback"""
|
||||
try:
|
||||
demonstrate_solution()
|
||||
print("\nThis solution approach is proven to work in UIEntityCollection.")
|
||||
print("It should be applied to UICollection for consistency.")
|
||||
except Exception as e:
|
||||
print(f"\nError: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up scene and run
|
||||
test = mcrfpy.Scene("test")
|
||||
test.activate()
|
||||
test_timer = mcrfpy.Timer("test", run_test, 100, once=True)
|
||||
mcrfpy.current_scene = test
|
||||
|
||||
try:
|
||||
demonstrate_solution()
|
||||
print("\nThis solution approach is proven to work in UIEntityCollection.")
|
||||
print("It should be applied to UICollection for consistency.")
|
||||
except Exception as e:
|
||||
print(f"\nError: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue