McRogueFace/tests/unit/test_callback_enums.py

250 lines
10 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Test unified callback signatures with enum types (#184)
This tests that all callbacks now use consistent typed arguments:
- Drawable callbacks: (pos: Vector, button: MouseButton, action: InputState)
- Grid cell callbacks: (cell_pos: Vector, button: MouseButton, action: InputState)
- Scene on_key: (key: Key, action: InputState)
"""
import mcrfpy
import sys
# Test results tracking
results = []
def test_passed(name):
results.append((name, True, None))
print(f" PASS: {name}")
def test_failed(name, error):
results.append((name, False, str(error)))
print(f" FAIL: {name}: {error}")
# ==============================================================================
# Test 1: Verify enum types exist and are accessible
# ==============================================================================
print("\n=== Callback Enum Signature Tests ===\n")
try:
# Check MouseButton enum
assert hasattr(mcrfpy, 'MouseButton'), "MouseButton enum should exist"
assert hasattr(mcrfpy.MouseButton, 'LEFT'), "MouseButton.LEFT should exist"
assert hasattr(mcrfpy.MouseButton, 'RIGHT'), "MouseButton.RIGHT should exist"
# Check InputState enum
assert hasattr(mcrfpy, 'InputState'), "InputState enum should exist"
assert hasattr(mcrfpy.InputState, 'PRESSED'), "InputState.PRESSED should exist"
assert hasattr(mcrfpy.InputState, 'RELEASED'), "InputState.RELEASED should exist"
# Check Key enum
assert hasattr(mcrfpy, 'Key'), "Key enum should exist"
assert hasattr(mcrfpy.Key, 'A'), "Key.A should exist"
assert hasattr(mcrfpy.Key, 'ESCAPE'), "Key.ESCAPE should exist"
test_passed("Enum types exist and are accessible")
except Exception as e:
test_failed("Enum types exist and are accessible", e)
# ==============================================================================
# Test 2: Grid cell callback with enum signature (subclass method)
# ==============================================================================
try:
class GridWithCellCallbacks(mcrfpy.Grid):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.cell_events = []
def on_cell_click(self, cell_pos, button, action):
# Verify types
assert isinstance(cell_pos, mcrfpy.Vector), f"cell_pos should be Vector, got {type(cell_pos)}"
self.cell_events.append(('click', cell_pos.x, cell_pos.y, button, action))
# #230 - Cell hover callbacks now only receive (cell_pos)
def on_cell_enter(self, cell_pos):
assert isinstance(cell_pos, mcrfpy.Vector), f"cell_pos should be Vector, got {type(cell_pos)}"
self.cell_events.append(('enter', cell_pos.x, cell_pos.y))
def on_cell_exit(self, cell_pos):
assert isinstance(cell_pos, mcrfpy.Vector), f"cell_pos should be Vector, got {type(cell_pos)}"
self.cell_events.append(('exit', cell_pos.x, cell_pos.y))
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
grid = GridWithCellCallbacks(grid_size=(5, 5), texture=texture, pos=(0, 0), size=(100, 100))
# Verify grid is subclass
assert isinstance(grid, mcrfpy.Grid), "Should be Grid instance"
assert type(grid) is not mcrfpy.Grid, "Should be subclass"
# Manually call methods to verify signature works
grid.on_cell_click(mcrfpy.Vector(1.0, 2.0), mcrfpy.MouseButton.LEFT, mcrfpy.InputState.PRESSED)
# #230 - Cell hover callbacks now only receive (cell_pos)
grid.on_cell_enter(mcrfpy.Vector(3.0, 4.0))
grid.on_cell_exit(mcrfpy.Vector(5.0, 6.0))
assert len(grid.cell_events) == 3, f"Should have 3 events, got {len(grid.cell_events)}"
assert grid.cell_events[0][0] == 'click', "First event should be click"
assert grid.cell_events[1][0] == 'enter', "Second event should be enter"
assert grid.cell_events[2][0] == 'exit', "Third event should be exit"
test_passed("Grid subclass cell callbacks with enum signature")
except Exception as e:
test_failed("Grid subclass cell callbacks with enum signature", e)
# ==============================================================================
# Test 3: Grid cell callback with property-assigned callable
# ==============================================================================
try:
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
grid = mcrfpy.Grid(grid_size=(5, 5), texture=texture, pos=(0, 0), size=(100, 100))
cell_events = []
def on_cell_click_handler(cell_pos, button, action):
assert isinstance(cell_pos, mcrfpy.Vector), f"cell_pos should be Vector, got {type(cell_pos)}"
cell_events.append(('click', cell_pos.x, cell_pos.y, button, action))
grid.on_cell_click = on_cell_click_handler
# Manually call to test (normally engine would call this)
grid.on_cell_click(mcrfpy.Vector(1.0, 2.0), mcrfpy.MouseButton.LEFT, mcrfpy.InputState.PRESSED)
assert len(cell_events) == 1, f"Should have 1 event, got {len(cell_events)}"
assert cell_events[0][3] == mcrfpy.MouseButton.LEFT, "Button should be MouseButton.LEFT"
assert cell_events[0][4] == mcrfpy.InputState.PRESSED, "Action should be InputState.PRESSED"
test_passed("Grid property cell callback with enum signature")
except Exception as e:
test_failed("Grid property cell callback with enum signature", e)
# ==============================================================================
# Test 4: Scene on_key callback with enum signature (subclass method)
# ==============================================================================
try:
class MyScene(mcrfpy.Scene):
def __init__(self, name):
super().__init__(name)
self.key_events = []
def on_key(self, key, action):
# Verify types - key should be Key enum, action should be InputState enum
self.key_events.append((key, action))
scene = MyScene("test_key_enum_scene")
# Manually call to test signature (normally engine would call this)
scene.on_key(mcrfpy.Key.A, mcrfpy.InputState.PRESSED)
scene.on_key(mcrfpy.Key.ESCAPE, mcrfpy.InputState.RELEASED)
assert len(scene.key_events) == 2, f"Should have 2 events, got {len(scene.key_events)}"
assert scene.key_events[0][0] == mcrfpy.Key.A, f"First key should be Key.A, got {scene.key_events[0][0]}"
assert scene.key_events[0][1] == mcrfpy.InputState.PRESSED, f"First action should be PRESSED"
assert scene.key_events[1][0] == mcrfpy.Key.ESCAPE, f"Second key should be Key.ESCAPE"
assert scene.key_events[1][1] == mcrfpy.InputState.RELEASED, f"Second action should be RELEASED"
test_passed("Scene subclass on_key with enum signature")
except Exception as e:
test_failed("Scene subclass on_key with enum signature", e)
# ==============================================================================
# Test 5: Scene on_key callback with property-assigned callable
# ==============================================================================
try:
scene = mcrfpy.Scene("test_key_prop_scene")
key_events = []
def on_key_handler(key, action):
key_events.append((key, action))
scene.on_key = on_key_handler
# Manually call to test (normally engine would call this via the callable)
scene.on_key(mcrfpy.Key.SPACE, mcrfpy.InputState.PRESSED)
assert len(key_events) == 1, f"Should have 1 event, got {len(key_events)}"
# Note: When calling directly on Python side, we pass enums directly
# The engine conversion happens internally when calling through C++
test_passed("Scene property on_key accepts enum args")
except Exception as e:
test_failed("Scene property on_key accepts enum args", e)
# ==============================================================================
# Test 6: Verify MouseButton enum values
# ==============================================================================
try:
# Verify the enum values are usable in comparisons
left = mcrfpy.MouseButton.LEFT
right = mcrfpy.MouseButton.RIGHT
assert left != right, "LEFT should not equal RIGHT"
assert left == mcrfpy.MouseButton.LEFT, "LEFT should equal itself"
# Verify we can use in conditions
def check_button(button):
if button == mcrfpy.MouseButton.LEFT:
return "left"
elif button == mcrfpy.MouseButton.RIGHT:
return "right"
return "other"
assert check_button(mcrfpy.MouseButton.LEFT) == "left"
assert check_button(mcrfpy.MouseButton.RIGHT) == "right"
test_passed("MouseButton enum values work correctly")
except Exception as e:
test_failed("MouseButton enum values work correctly", e)
# ==============================================================================
# Test 7: Verify InputState enum values
# ==============================================================================
try:
pressed = mcrfpy.InputState.PRESSED
released = mcrfpy.InputState.RELEASED
assert pressed != released, "PRESSED should not equal RELEASED"
assert pressed == mcrfpy.InputState.PRESSED, "PRESSED should equal itself"
test_passed("InputState enum values work correctly")
except Exception as e:
test_failed("InputState enum values work correctly", e)
# ==============================================================================
# Test 8: Verify Key enum values
# ==============================================================================
try:
a_key = mcrfpy.Key.A
esc_key = mcrfpy.Key.ESCAPE
assert a_key != esc_key, "A should not equal ESCAPE"
assert a_key == mcrfpy.Key.A, "A should equal itself"
# Verify various keys exist
assert hasattr(mcrfpy.Key, 'SPACE'), "SPACE should exist"
assert hasattr(mcrfpy.Key, 'ENTER'), "ENTER should exist"
assert hasattr(mcrfpy.Key, 'UP'), "UP should exist"
assert hasattr(mcrfpy.Key, 'DOWN'), "DOWN should exist"
test_passed("Key enum values work correctly")
except Exception as e:
test_failed("Key enum values work correctly", e)
# ==============================================================================
# Summary
# ==============================================================================
print("\n=== Test Summary ===")
passed = sum(1 for _, p, _ in results if p)
total = len(results)
print(f"Passed: {passed}/{total}")
if passed == total:
print("\nAll tests passed!")
sys.exit(0)
else:
print("\nSome tests failed:")
for name, p, err in results:
if not p:
print(f" - {name}: {err}")
sys.exit(1)