Refactor 11 more tests to mcrfpy.step() pattern

Converted from Timer-based async to step()-based sync:
- test_simple_callback.py
- test_empty_animation_manager.py
- test_frame_clipping.py
- test_frame_clipping_advanced.py
- test_grid_children.py
- test_color_helpers.py
- test_no_arg_constructors.py
- test_properties_quick.py
- test_simple_drawable.py
- test_python_object_cache.py
- WORKING_automation_test_example.py

Only 4 tests remain with Timer-based patterns (2 are headless detection
tests that may require special handling).

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Frack <frack@goblincorps.dev>
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Frick 2026-01-14 03:09:47 +00:00
commit be450286f8
12 changed files with 867 additions and 866 deletions

View file

@ -89,30 +89,28 @@ The following tests have been converted to use `mcrfpy.step()`:
- test_animation_removal.py - test_animation_removal.py
- test_timer_callback.py - test_timer_callback.py
- test_timer_once.py - test_timer_once.py
- test_simple_callback.py
- test_empty_animation_manager.py
- test_frame_clipping.py
- test_frame_clipping_advanced.py
- test_grid_children.py
- test_color_helpers.py
- test_no_arg_constructors.py
- test_properties_quick.py
- test_simple_drawable.py
- test_python_object_cache.py
- WORKING_automation_test_example.py
## Remaining Timeout Failures ## Remaining Timeout Failures
These tests still use Timer-based async patterns: These tests still use Timer-based async patterns:
- WORKING_automation_test_example.py
- benchmark_logging_test.py - benchmark_logging_test.py
- keypress_scene_validation_test.py - keypress_scene_validation_test.py
- test_empty_animation_manager.py
- test_simple_callback.py
**Headless mode tests:** **Headless mode tests:**
- test_headless_detection.py - test_headless_detection.py
- test_headless_modes.py - test_headless_modes.py
**Other timing-dependent:**
- test_color_helpers.py
- test_frame_clipping.py
- test_frame_clipping_advanced.py
- test_grid_children.py
- test_no_arg_constructors.py
- test_properties_quick.py
- test_python_object_cache.py
- test_simple_drawable.py
## Running Tests ## Running Tests
```bash ```bash

View file

@ -1,47 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Example of CORRECT test pattern using timer callbacks for automation""" """Example of CORRECT test pattern using mcrfpy.step() for automation
Refactored from timer-based approach to synchronous step() pattern.
"""
import mcrfpy import mcrfpy
from mcrfpy import automation from mcrfpy import automation
from datetime import datetime from datetime import datetime
import sys
def run_automation_tests(timer, runtime):
"""This runs AFTER the game loop has started and rendered frames"""
print("\n=== Automation Test Running (1 second after start) ===")
# NOW we can take screenshots that will show content!
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"WORKING_screenshot_{timestamp}.png"
# Take screenshot - this should now show our red frame
result = automation.screenshot(filename)
print(f"Screenshot taken: {filename} - Result: {result}")
# Test clicking on the frame
automation.click(200, 200) # Click in center of red frame
# Test keyboard input
automation.typewrite("Hello from timer callback!")
# Take another screenshot to show any changes
filename2 = f"WORKING_screenshot_after_click_{timestamp}.png"
automation.screenshot(filename2)
print(f"Second screenshot: {filename2}")
print("Test completed successfully!")
print("\nThis works because:")
print("1. The game loop has been running for 1 second")
print("2. The scene has been rendered multiple times")
print("3. The RenderTexture now contains actual rendered content")
# Cancel this timer so it doesn't repeat
timer.stop()
# Optional: exit after a moment
def exit_game(t, r):
print("Exiting...")
mcrfpy.exit()
global exit_timer
exit_timer = mcrfpy.Timer("exit", exit_game, 500, once=True)
# This code runs during --exec script execution # This code runs during --exec script execution
print("=== Setting Up Test Scene ===") print("=== Setting Up Test Scene ===")
@ -49,6 +13,8 @@ print("=== Setting Up Test Scene ===")
# Create scene with visible content # Create scene with visible content
timer_test_scene = mcrfpy.Scene("timer_test_scene") timer_test_scene = mcrfpy.Scene("timer_test_scene")
timer_test_scene.activate() timer_test_scene.activate()
mcrfpy.step(0.01) # Initialize scene
ui = timer_test_scene.children ui = timer_test_scene.children
# Add a bright red frame that should be visible # Add a bright red frame that should be visible
@ -60,23 +26,57 @@ ui.append(frame)
# Add text # Add text
caption = mcrfpy.Caption(pos=(150, 150), caption = mcrfpy.Caption(pos=(150, 150),
text="TIMER TEST - SHOULD BE VISIBLE", text="STEP TEST - SHOULD BE VISIBLE",
fill_color=mcrfpy.Color(255, 255, 255)) fill_color=mcrfpy.Color(255, 255, 255))
caption.font_size = 24 caption.font_size = 24
frame.children.append(caption) frame.children.append(caption)
# Add click handler to demonstrate interaction # Add click handler to demonstrate interaction
click_received = False
def frame_clicked(x, y, button): def frame_clicked(x, y, button):
global click_received
click_received = True
print(f"Frame clicked at ({x}, {y}) with button {button}") print(f"Frame clicked at ({x}, {y}) with button {button}")
frame.on_click = frame_clicked frame.on_click = frame_clicked
print("Scene setup complete. Setting timer for automation tests...") print("Scene setup complete.")
# THIS IS THE KEY: Set timer to run AFTER the game loop starts # Step to render the scene
automation_test_timer = mcrfpy.Timer("automation_test", run_automation_tests, 1000, once=True) mcrfpy.step(0.1)
print("Timer set. Game loop will start after this script completes.") print("\n=== Automation Test Running ===")
print("Automation tests will run 1 second later when content is visible.")
# Script ends here - game loop starts next # NOW we can take screenshots that will show content!
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"WORKING_screenshot_{timestamp}.png"
# Take screenshot - this should now show our red frame
result = automation.screenshot(filename)
print(f"Screenshot taken: {filename} - Result: {result}")
# Test clicking on the frame
automation.click(200, 200) # Click in center of red frame
# Step to process the click
mcrfpy.step(0.1)
# Test keyboard input
automation.typewrite("Hello from step-based test!")
# Step to process keyboard input
mcrfpy.step(0.1)
# Take another screenshot to show any changes
filename2 = f"WORKING_screenshot_after_click_{timestamp}.png"
automation.screenshot(filename2)
print(f"Second screenshot: {filename2}")
print("Test completed successfully!")
print("\nThis works because:")
print("1. mcrfpy.step() advances simulation synchronously")
print("2. The scene renders during step() calls")
print("3. The RenderTexture contains actual rendered content")
print("PASS")
sys.exit(0)

View file

@ -1,160 +1,163 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Test #94: Color helper methods - from_hex, to_hex, lerp Test #94: Color helper methods - from_hex, to_hex, lerp
Refactored to use mcrfpy.step() for synchronous execution.
""" """
import mcrfpy import mcrfpy
import sys import sys
def test_color_helpers(timer, runtime): # Initialize scene
"""Test Color helper methods""" test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
all_pass = True all_pass = True
# Test 1: from_hex with # prefix # Test 1: from_hex with # prefix
try: try:
c1 = mcrfpy.Color.from_hex("#FF0000") c1 = mcrfpy.Color.from_hex("#FF0000")
assert c1.r == 255 and c1.g == 0 and c1.b == 0 and c1.a == 255, f"from_hex('#FF0000') failed: {c1}" assert c1.r == 255 and c1.g == 0 and c1.b == 0 and c1.a == 255, f"from_hex('#FF0000') failed: {c1}"
print("+ Color.from_hex('#FF0000') works") print("+ Color.from_hex('#FF0000') works")
except Exception as e: except Exception as e:
print(f"x Color.from_hex('#FF0000') failed: {e}") print(f"x Color.from_hex('#FF0000') failed: {e}")
all_pass = False all_pass = False
# Test 2: from_hex without # prefix # Test 2: from_hex without # prefix
try: try:
c2 = mcrfpy.Color.from_hex("00FF00") c2 = mcrfpy.Color.from_hex("00FF00")
assert c2.r == 0 and c2.g == 255 and c2.b == 0 and c2.a == 255, f"from_hex('00FF00') failed: {c2}" assert c2.r == 0 and c2.g == 255 and c2.b == 0 and c2.a == 255, f"from_hex('00FF00') failed: {c2}"
print("+ Color.from_hex('00FF00') works") print("+ Color.from_hex('00FF00') works")
except Exception as e: except Exception as e:
print(f"x Color.from_hex('00FF00') failed: {e}") print(f"x Color.from_hex('00FF00') failed: {e}")
all_pass = False all_pass = False
# Test 3: from_hex with alpha # Test 3: from_hex with alpha
try: try:
c3 = mcrfpy.Color.from_hex("#0000FF80") c3 = mcrfpy.Color.from_hex("#0000FF80")
assert c3.r == 0 and c3.g == 0 and c3.b == 255 and c3.a == 128, f"from_hex('#0000FF80') failed: {c3}" assert c3.r == 0 and c3.g == 0 and c3.b == 255 and c3.a == 128, f"from_hex('#0000FF80') failed: {c3}"
print("+ Color.from_hex('#0000FF80') with alpha works") print("+ Color.from_hex('#0000FF80') with alpha works")
except Exception as e: except Exception as e:
print(f"x Color.from_hex('#0000FF80') failed: {e}") print(f"x Color.from_hex('#0000FF80') failed: {e}")
all_pass = False all_pass = False
# Test 4: from_hex error handling # Test 4: from_hex error handling
try: try:
c4 = mcrfpy.Color.from_hex("GGGGGG") c4 = mcrfpy.Color.from_hex("GGGGGG")
print("x from_hex should fail on invalid hex") print("x from_hex should fail on invalid hex")
all_pass = False all_pass = False
except ValueError as e: except ValueError as e:
print("+ Color.from_hex() correctly rejects invalid hex") print("+ Color.from_hex() correctly rejects invalid hex")
# Test 5: from_hex wrong length # Test 5: from_hex wrong length
try: try:
c5 = mcrfpy.Color.from_hex("FF00") c5 = mcrfpy.Color.from_hex("FF00")
print("x from_hex should fail on wrong length") print("x from_hex should fail on wrong length")
all_pass = False all_pass = False
except ValueError as e: except ValueError as e:
print("+ Color.from_hex() correctly rejects wrong length") print("+ Color.from_hex() correctly rejects wrong length")
# Test 6: to_hex without alpha # Test 6: to_hex without alpha
try: try:
c6 = mcrfpy.Color(255, 128, 64) c6 = mcrfpy.Color(255, 128, 64)
hex_str = c6.to_hex() hex_str = c6.to_hex()
assert hex_str == "#FF8040", f"to_hex() failed: {hex_str}" assert hex_str == "#FF8040", f"to_hex() failed: {hex_str}"
print("+ Color.to_hex() works") print("+ Color.to_hex() works")
except Exception as e: except Exception as e:
print(f"x Color.to_hex() failed: {e}") print(f"x Color.to_hex() failed: {e}")
all_pass = False all_pass = False
# Test 7: to_hex with alpha # Test 7: to_hex with alpha
try: try:
c7 = mcrfpy.Color(255, 128, 64, 127) c7 = mcrfpy.Color(255, 128, 64, 127)
hex_str = c7.to_hex() hex_str = c7.to_hex()
assert hex_str == "#FF80407F", f"to_hex() with alpha failed: {hex_str}" assert hex_str == "#FF80407F", f"to_hex() with alpha failed: {hex_str}"
print("+ Color.to_hex() with alpha works") print("+ Color.to_hex() with alpha works")
except Exception as e: except Exception as e:
print(f"x Color.to_hex() with alpha failed: {e}") print(f"x Color.to_hex() with alpha failed: {e}")
all_pass = False all_pass = False
# Test 8: Round-trip hex conversion # Test 8: Round-trip hex conversion
try: try:
original_hex = "#ABCDEF" original_hex = "#ABCDEF"
c8 = mcrfpy.Color.from_hex(original_hex) c8 = mcrfpy.Color.from_hex(original_hex)
result_hex = c8.to_hex() result_hex = c8.to_hex()
assert result_hex == original_hex, f"Round-trip failed: {original_hex} -> {result_hex}" assert result_hex == original_hex, f"Round-trip failed: {original_hex} -> {result_hex}"
print("+ Hex round-trip conversion works") print("+ Hex round-trip conversion works")
except Exception as e: except Exception as e:
print(f"x Hex round-trip failed: {e}") print(f"x Hex round-trip failed: {e}")
all_pass = False all_pass = False
# Test 9: lerp at t=0 # Test 9: lerp at t=0
try: try:
red = mcrfpy.Color(255, 0, 0) red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255) blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 0.0) result = red.lerp(blue, 0.0)
assert result.r == 255 and result.g == 0 and result.b == 0, f"lerp(t=0) failed: {result}" assert result.r == 255 and result.g == 0 and result.b == 0, f"lerp(t=0) failed: {result}"
print("+ Color.lerp(t=0) returns start color") print("+ Color.lerp(t=0) returns start color")
except Exception as e: except Exception as e:
print(f"x Color.lerp(t=0) failed: {e}") print(f"x Color.lerp(t=0) failed: {e}")
all_pass = False all_pass = False
# Test 10: lerp at t=1 # Test 10: lerp at t=1
try: try:
red = mcrfpy.Color(255, 0, 0) red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255) blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 1.0) result = red.lerp(blue, 1.0)
assert result.r == 0 and result.g == 0 and result.b == 255, f"lerp(t=1) failed: {result}" assert result.r == 0 and result.g == 0 and result.b == 255, f"lerp(t=1) failed: {result}"
print("+ Color.lerp(t=1) returns end color") print("+ Color.lerp(t=1) returns end color")
except Exception as e: except Exception as e:
print(f"x Color.lerp(t=1) failed: {e}") print(f"x Color.lerp(t=1) failed: {e}")
all_pass = False all_pass = False
# Test 11: lerp at t=0.5 # Test 11: lerp at t=0.5
try: try:
red = mcrfpy.Color(255, 0, 0) red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255) blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 0.5) result = red.lerp(blue, 0.5)
# Expect roughly (127, 0, 127) # Expect roughly (127, 0, 127)
assert 126 <= result.r <= 128 and result.g == 0 and 126 <= result.b <= 128, f"lerp(t=0.5) failed: {result}" assert 126 <= result.r <= 128 and result.g == 0 and 126 <= result.b <= 128, f"lerp(t=0.5) failed: {result}"
print("+ Color.lerp(t=0.5) returns midpoint") print("+ Color.lerp(t=0.5) returns midpoint")
except Exception as e: except Exception as e:
print(f"x Color.lerp(t=0.5) failed: {e}") print(f"x Color.lerp(t=0.5) failed: {e}")
all_pass = False all_pass = False
# Test 12: lerp with alpha # Test 12: lerp with alpha
try: try:
c1 = mcrfpy.Color(255, 0, 0, 255) c1 = mcrfpy.Color(255, 0, 0, 255)
c2 = mcrfpy.Color(0, 255, 0, 0) c2 = mcrfpy.Color(0, 255, 0, 0)
result = c1.lerp(c2, 0.5) result = c1.lerp(c2, 0.5)
assert 126 <= result.r <= 128 and 126 <= result.g <= 128 and result.b == 0, f"lerp color components failed" assert 126 <= result.r <= 128 and 126 <= result.g <= 128 and result.b == 0, f"lerp color components failed"
assert 126 <= result.a <= 128, f"lerp alpha failed: {result.a}" assert 126 <= result.a <= 128, f"lerp alpha failed: {result.a}"
print("+ Color.lerp() with alpha works") print("+ Color.lerp() with alpha works")
except Exception as e: except Exception as e:
print(f"x Color.lerp() with alpha failed: {e}") print(f"x Color.lerp() with alpha failed: {e}")
all_pass = False all_pass = False
# Test 13: lerp clamps t < 0 # Test 13: lerp clamps t < 0
try: try:
red = mcrfpy.Color(255, 0, 0) red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255) blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, -0.5) result = red.lerp(blue, -0.5)
assert result.r == 255 and result.g == 0 and result.b == 0, f"lerp(t<0) should clamp to 0" assert result.r == 255 and result.g == 0 and result.b == 0, f"lerp(t<0) should clamp to 0"
print("+ Color.lerp() clamps t < 0") print("+ Color.lerp() clamps t < 0")
except Exception as e: except Exception as e:
print(f"x Color.lerp(t<0) failed: {e}") print(f"x Color.lerp(t<0) failed: {e}")
all_pass = False all_pass = False
# Test 14: lerp clamps t > 1 # Test 14: lerp clamps t > 1
try: try:
red = mcrfpy.Color(255, 0, 0) red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255) blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 1.5) result = red.lerp(blue, 1.5)
assert result.r == 0 and result.g == 0 and result.b == 255, f"lerp(t>1) should clamp to 1" assert result.r == 0 and result.g == 0 and result.b == 255, f"lerp(t>1) should clamp to 1"
print("+ Color.lerp() clamps t > 1") print("+ Color.lerp() clamps t > 1")
except Exception as e: except Exception as e:
print(f"x Color.lerp(t>1) failed: {e}") print(f"x Color.lerp(t>1) failed: {e}")
all_pass = False all_pass = False
# Test 15: Practical use case - gradient # Test 15: Practical use case - gradient
try: try:
start = mcrfpy.Color.from_hex("#FF0000") # Red start = mcrfpy.Color.from_hex("#FF0000") # Red
end = mcrfpy.Color.from_hex("#0000FF") # Blue end = mcrfpy.Color.from_hex("#0000FF") # Blue
@ -170,13 +173,9 @@ def test_color_helpers(timer, runtime):
assert len(set(steps)) == 5, "All gradient steps should be unique" assert len(set(steps)) == 5, "All gradient steps should be unique"
print("+ Gradient generation works correctly") print("+ Gradient generation works correctly")
except Exception as e: except Exception as e:
print(f"x Gradient generation failed: {e}") print(f"x Gradient generation failed: {e}")
all_pass = False all_pass = False
print(f"\n{'PASS' if all_pass else 'FAIL'}") print(f"\n{'PASS' if all_pass else 'FAIL'}")
sys.exit(0 if all_pass else 1) sys.exit(0 if all_pass else 1)
# Run test
test = mcrfpy.Scene("test")
test_timer = mcrfpy.Timer("test", test_color_helpers, 100, once=True)

View file

@ -1,20 +1,28 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Test if AnimationManager crashes with no animations Test if AnimationManager crashes with no animations
Refactored to use mcrfpy.step() for synchronous execution.
""" """
import mcrfpy import mcrfpy
import sys
print("Creating empty scene...") print("Creating empty scene...")
test = mcrfpy.Scene("test") test = mcrfpy.Scene("test")
test.activate() test.activate()
print("Scene created, no animations added") print("Scene created, no animations added")
print("Starting game loop in 100ms...") print("Advancing simulation with step()...")
def check_alive(timer, runtime): # Step multiple times to simulate game loop running
print(f"Timer fired at {runtime}ms - AnimationManager survived!") # If AnimationManager crashes with empty state, this will fail
mcrfpy.Timer("exit", lambda t, r: mcrfpy.exit(), 100, once=True) try:
for i in range(10):
mcrfpy.step(0.1) # 10 steps of 0.1s = 1 second simulated
mcrfpy.Timer("check", check_alive, 1000, once=True) print("AnimationManager survived 10 steps with no animations!")
print("If this crashes immediately, AnimationManager has an issue with empty state") print("PASS")
sys.exit(0)
except Exception as e:
print(f"FAIL: AnimationManager crashed: {e}")
sys.exit(1)

View file

@ -1,135 +1,118 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Test UIFrame clipping functionality""" """Test UIFrame clipping functionality
Refactored to use mcrfpy.step() for synchronous execution.
"""
import mcrfpy import mcrfpy
from mcrfpy import Color, Frame, Caption from mcrfpy import Color, Frame, Caption, automation
import sys import sys
# Module-level state to avoid closures
_test_state = {}
def take_second_screenshot(timer, runtime):
"""Take final screenshot and exit"""
timer.stop()
from mcrfpy import automation
automation.screenshot("frame_clipping_animated.png")
print("\nTest completed successfully!")
print("Screenshots saved:")
print(" - frame_clipping_test.png (initial state)")
print(" - frame_clipping_animated.png (with animation)")
sys.exit(0)
def animate_frames(timer, runtime):
"""Animate frames to demonstrate clipping"""
timer.stop()
scene = test.children
# Move child frames
parent1 = scene[0]
parent2 = scene[1]
parent1.children[1].x = 50
parent2.children[1].x = 50
global screenshot2_timer
screenshot2_timer = mcrfpy.Timer("screenshot2", take_second_screenshot, 500, once=True)
def test_clipping(timer, runtime):
"""Test that clip_children property works correctly"""
timer.stop()
print("Testing UIFrame clipping functionality...")
scene = test.children
# Create parent frame with clipping disabled (default)
parent1 = Frame(pos=(50, 50), size=(200, 150),
fill_color=Color(100, 100, 200),
outline_color=Color(255, 255, 255),
outline=2)
parent1.name = "parent1"
scene.append(parent1)
# Create parent frame with clipping enabled
parent2 = Frame(pos=(300, 50), size=(200, 150),
fill_color=Color(200, 100, 100),
outline_color=Color(255, 255, 255),
outline=2)
parent2.name = "parent2"
parent2.clip_children = True
scene.append(parent2)
# Add captions to both frames
caption1 = Caption(text="This text should overflow the frame bounds", pos=(10, 10))
caption1.font_size = 16
caption1.fill_color = Color(255, 255, 255)
parent1.children.append(caption1)
caption2 = Caption(text="This text should be clipped to frame bounds", pos=(10, 10))
caption2.font_size = 16
caption2.fill_color = Color(255, 255, 255)
parent2.children.append(caption2)
# Add child frames that extend beyond parent bounds
child1 = Frame(pos=(150, 100), size=(100, 100),
fill_color=Color(50, 255, 50),
outline_color=Color(0, 0, 0),
outline=1)
parent1.children.append(child1)
child2 = Frame(pos=(150, 100), size=(100, 100),
fill_color=Color(50, 255, 50),
outline_color=Color(0, 0, 0),
outline=1)
parent2.children.append(child2)
# Add caption to show clip state
status = Caption(text=f"Left frame: clip_children={parent1.clip_children}\n"
f"Right frame: clip_children={parent2.clip_children}",
pos=(50, 250))
status.font_size = 14
status.fill_color = Color(255, 255, 255)
scene.append(status)
# Add instructions
instructions = Caption(text="Left: Children should overflow (no clipping)\n"
"Right: Children should be clipped to frame bounds\n"
"Press 'c' to toggle clipping on left frame",
pos=(50, 300))
instructions.font_size = 12
instructions.fill_color = Color(200, 200, 200)
scene.append(instructions)
# Take screenshot
from mcrfpy import automation
automation.screenshot("frame_clipping_test.png")
print(f"Parent1 clip_children: {parent1.clip_children}")
print(f"Parent2 clip_children: {parent2.clip_children}")
# Test toggling clip_children
parent1.clip_children = True
print(f"After toggle - Parent1 clip_children: {parent1.clip_children}")
# Verify the property setter works
try:
parent1.clip_children = "not a bool"
print("ERROR: clip_children accepted non-boolean value")
except TypeError as e:
print(f"PASS: clip_children correctly rejected non-boolean: {e}")
# Start animation after a short delay
global animate_timer
animate_timer = mcrfpy.Timer("animate", animate_frames, 100, once=True)
def handle_keypress(key, modifiers):
if key == "c":
scene = test.children
parent1 = scene[0]
parent1.clip_children = not parent1.clip_children
print(f"Toggled parent1 clip_children to: {parent1.clip_children}")
# Main execution
print("Creating test scene...") print("Creating test scene...")
test = mcrfpy.Scene("test") test = mcrfpy.Scene("test")
test.activate() test.activate()
test.on_key = handle_keypress mcrfpy.step(0.01) # Initialize
test_clipping_timer = mcrfpy.Timer("test_clipping", test_clipping, 100, once=True)
print("Test scheduled, running...") print("Testing UIFrame clipping functionality...")
scene = test.children
# Create parent frame with clipping disabled (default)
parent1 = Frame(pos=(50, 50), size=(200, 150),
fill_color=Color(100, 100, 200),
outline_color=Color(255, 255, 255),
outline=2)
parent1.name = "parent1"
scene.append(parent1)
# Create parent frame with clipping enabled
parent2 = Frame(pos=(300, 50), size=(200, 150),
fill_color=Color(200, 100, 100),
outline_color=Color(255, 255, 255),
outline=2)
parent2.name = "parent2"
parent2.clip_children = True
scene.append(parent2)
# Add captions to both frames
caption1 = Caption(text="This text should overflow the frame bounds", pos=(10, 10))
caption1.font_size = 16
caption1.fill_color = Color(255, 255, 255)
parent1.children.append(caption1)
caption2 = Caption(text="This text should be clipped to frame bounds", pos=(10, 10))
caption2.font_size = 16
caption2.fill_color = Color(255, 255, 255)
parent2.children.append(caption2)
# Add child frames that extend beyond parent bounds
child1 = Frame(pos=(150, 100), size=(100, 100),
fill_color=Color(50, 255, 50),
outline_color=Color(0, 0, 0),
outline=1)
parent1.children.append(child1)
child2 = Frame(pos=(150, 100), size=(100, 100),
fill_color=Color(50, 255, 50),
outline_color=Color(0, 0, 0),
outline=1)
parent2.children.append(child2)
# Add caption to show clip state
status = Caption(text=f"Left frame: clip_children={parent1.clip_children}\n"
f"Right frame: clip_children={parent2.clip_children}",
pos=(50, 250))
status.font_size = 14
status.fill_color = Color(255, 255, 255)
scene.append(status)
# Add instructions
instructions = Caption(text="Left: Children should overflow (no clipping)\n"
"Right: Children should be clipped to frame bounds",
pos=(50, 300))
instructions.font_size = 12
instructions.fill_color = Color(200, 200, 200)
scene.append(instructions)
# Step to render
mcrfpy.step(0.1)
# Take screenshot
automation.screenshot("frame_clipping_test.png")
print(f"Parent1 clip_children: {parent1.clip_children}")
print(f"Parent2 clip_children: {parent2.clip_children}")
# Test toggling clip_children
parent1.clip_children = True
print(f"After toggle - Parent1 clip_children: {parent1.clip_children}")
# Verify the property setter works
test_passed = True
try:
parent1.clip_children = "not a bool"
print("ERROR: clip_children accepted non-boolean value")
test_passed = False
except TypeError as e:
print(f"PASS: clip_children correctly rejected non-boolean: {e}")
# Animate frames (move children)
parent1.children[1].x = 50
parent2.children[1].x = 50
# Step to render animation
mcrfpy.step(0.1)
# Take second screenshot
automation.screenshot("frame_clipping_animated.png")
print("\nTest completed successfully!")
print("Screenshots saved:")
print(" - frame_clipping_test.png (initial state)")
print(" - frame_clipping_animated.png (with animation)")
if test_passed:
print("PASS")
sys.exit(0)
else:
print("FAIL")
sys.exit(1)

View file

@ -1,105 +1,95 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Advanced test for UIFrame clipping with nested frames""" """Advanced test for UIFrame clipping with nested frames
Refactored to use mcrfpy.step() for synchronous execution.
"""
import mcrfpy import mcrfpy
from mcrfpy import Color, Frame, Caption, Vector from mcrfpy import Color, Frame, Caption, Vector, automation
import sys import sys
def test_nested_clipping(timer, runtime): print("Creating advanced test scene...")
"""Test nested frames with clipping""" test = mcrfpy.Scene("test")
timer.stop() test.activate()
mcrfpy.step(0.01)
print("Testing advanced UIFrame clipping with nested frames...") print("Testing advanced UIFrame clipping with nested frames...")
# Create test scene # Create test scene
scene = test.children scene = test.children
# Create outer frame with clipping enabled # Create outer frame with clipping enabled
outer = Frame(pos=(50, 50), size=(400, 300), outer = Frame(pos=(50, 50), size=(400, 300),
fill_color=Color(50, 50, 150), fill_color=Color(50, 50, 150),
outline_color=Color(255, 255, 255), outline_color=Color(255, 255, 255),
outline=3) outline=3)
outer.name = "outer" outer.name = "outer"
outer.clip_children = True outer.clip_children = True
scene.append(outer) scene.append(outer)
# Create inner frame that extends beyond outer bounds # Create inner frame that extends beyond outer bounds
inner = Frame(pos=(200, 150), size=(300, 200), inner = Frame(pos=(200, 150), size=(300, 200),
fill_color=Color(150, 50, 50), fill_color=Color(150, 50, 50),
outline_color=Color(255, 255, 0), outline_color=Color(255, 255, 0),
outline=2) outline=2)
inner.name = "inner" inner.name = "inner"
inner.clip_children = True # Also enable clipping on inner frame inner.clip_children = True # Also enable clipping on inner frame
outer.children.append(inner) outer.children.append(inner)
# Add content to inner frame that extends beyond its bounds # Add content to inner frame that extends beyond its bounds
for i in range(5): for i in range(5):
caption = Caption(text=f"Line {i+1}: This text should be double-clipped", pos=(10, 30 * i)) caption = Caption(text=f"Line {i+1}: This text should be double-clipped", pos=(10, 30 * i))
caption.font_size = 14 caption.font_size = 14
caption.fill_color = Color(255, 255, 255) caption.fill_color = Color(255, 255, 255)
inner.children.append(caption) inner.children.append(caption)
# Add a child frame to inner that extends way out # Add a child frame to inner that extends way out
deeply_nested = Frame(pos=(250, 100), size=(200, 150), deeply_nested = Frame(pos=(250, 100), size=(200, 150),
fill_color=Color(50, 150, 50), fill_color=Color(50, 150, 50),
outline_color=Color(255, 0, 255), outline_color=Color(255, 0, 255),
outline=2) outline=2)
deeply_nested.name = "deeply_nested" deeply_nested.name = "deeply_nested"
inner.children.append(deeply_nested) inner.children.append(deeply_nested)
# Add status text # Add status text
status = Caption(text="Nested clipping test:\n" status = Caption(text="Nested clipping test:\n"
"- Blue outer frame clips red inner frame\n" "- Blue outer frame clips red inner frame\n"
"- Red inner frame clips green deeply nested frame\n" "- Red inner frame clips green deeply nested frame\n"
"- All text should be clipped to frame bounds", "- All text should be clipped to frame bounds",
pos=(50, 380)) pos=(50, 380))
status.font_size = 12 status.font_size = 12
status.fill_color = Color(200, 200, 200) status.fill_color = Color(200, 200, 200)
scene.append(status) scene.append(status)
# Test render texture size handling # Test render texture size handling
print(f"Outer frame size: {outer.w}x{outer.h}") print(f"Outer frame size: {outer.w}x{outer.h}")
print(f"Inner frame size: {inner.w}x{inner.h}") print(f"Inner frame size: {inner.w}x{inner.h}")
# Dynamically resize frames to test RenderTexture recreation # Step to render
def resize_test(timer, runtime): mcrfpy.step(0.1)
timer.stop()
print("Resizing frames to test RenderTexture recreation...")
outer.w = 450
outer.h = 350
inner.w = 350
inner.h = 250
print(f"New outer frame size: {outer.w}x{outer.h}")
print(f"New inner frame size: {inner.w}x{inner.h}")
# Take screenshot after resize # Take initial screenshot
global screenshot_resize_timer automation.screenshot("frame_clipping_nested.png")
screenshot_resize_timer = mcrfpy.Timer("screenshot_resize", take_resize_screenshot, 500, once=True) print("Initial screenshot saved: frame_clipping_nested.png")
def take_resize_screenshot(timer, runtime): # Dynamically resize frames to test RenderTexture recreation
timer.stop() print("Resizing frames to test RenderTexture recreation...")
from mcrfpy import automation outer.w = 450
automation.screenshot("frame_clipping_resized.png") outer.h = 350
print("\nAdvanced test completed!") inner.w = 350
print("Screenshots saved:") inner.h = 250
print(" - frame_clipping_resized.png (after resize)") print(f"New outer frame size: {outer.w}x{outer.h}")
sys.exit(0) print(f"New inner frame size: {inner.w}x{inner.h}")
# Take initial screenshot # Step to render resize
from mcrfpy import automation mcrfpy.step(0.1)
automation.screenshot("frame_clipping_nested.png")
print("Initial screenshot saved: frame_clipping_nested.png")
# Schedule resize test # Take screenshot after resize
global resize_test_timer automation.screenshot("frame_clipping_resized.png")
resize_test_timer = mcrfpy.Timer("resize_test", resize_test, 1000, once=True)
# Main execution print("\nAdvanced test completed!")
print("Creating advanced test scene...") print("Screenshots saved:")
test = mcrfpy.Scene("test") print(" - frame_clipping_nested.png (initial)")
test.activate() print(" - frame_clipping_resized.png (after resize)")
# Schedule the test print("PASS")
test_nested_clipping_timer = mcrfpy.Timer("test_nested_clipping", test_nested_clipping, 100, once=True) sys.exit(0)
print("Advanced test scheduled, running...")

View file

@ -1,129 +1,125 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Test Grid.children collection - Issue #132""" """Test Grid.children collection - Issue #132
Refactored to use mcrfpy.step() for synchronous execution.
"""
import mcrfpy import mcrfpy
from mcrfpy import automation from mcrfpy import automation
import sys import sys
def take_screenshot(timer, runtime): print("Creating test scene...")
"""Take screenshot after render completes""" test = mcrfpy.Scene("test")
timer.stop() test.activate()
automation.screenshot("test_grid_children_result.png") mcrfpy.step(0.01) # Initialize
print("Screenshot saved to test_grid_children_result.png") # Get the scene UI
print("PASS - Grid.children test completed") ui = test.children
sys.exit(0)
def run_test(timer, runtime): # Test 1: Creating Grid with children
"""Main test - runs after scene is set up""" print("Test 1: Creating Grid with children...")
timer.stop() grid = mcrfpy.Grid(grid_size=(20, 15), pos=(50, 50), size=(320, 240))
grid.fill_color = mcrfpy.Color(30, 30, 60)
ui.append(grid)
# Get the scene UI # Verify entities and children properties exist
ui = test.children print(f" grid.entities = {grid.entities}")
print(f" grid.children = {grid.children}")
# Create a grid without texture (uses default 16x16 cells) # Test 2: Add UIDrawable children to the grid
print("Test 1: Creating Grid with children...") print("\nTest 2: Adding UIDrawable children...")
grid = mcrfpy.Grid(grid_size=(20, 15), pos=(50, 50), size=(320, 240))
grid.fill_color = mcrfpy.Color(30, 30, 60)
ui.append(grid)
# Verify entities and children properties exist # Speech bubble style caption - positioned in grid-world pixels
print(f" grid.entities = {grid.entities}") # At cell (5, 3) which is 5*16=80, 3*16=48 in pixels
print(f" grid.children = {grid.children}") caption = mcrfpy.Caption(text="Hello!", pos=(80, 48))
caption.fill_color = mcrfpy.Color(255, 255, 200)
caption.outline = 1
caption.outline_color = mcrfpy.Color(0, 0, 0)
grid.children.append(caption)
print(f" Added caption at (80, 48)")
# Test 2: Add UIDrawable children to the grid # A highlight circle around cell (10, 7) = (160, 112)
print("\nTest 2: Adding UIDrawable children...") # Circle needs center, not pos
circle = mcrfpy.Circle(center=(168, 120), radius=20,
# Speech bubble style caption - positioned in grid-world pixels
# At cell (5, 3) which is 5*16=80, 3*16=48 in pixels
caption = mcrfpy.Caption(text="Hello!", pos=(80, 48))
caption.fill_color = mcrfpy.Color(255, 255, 200)
caption.outline = 1
caption.outline_color = mcrfpy.Color(0, 0, 0)
grid.children.append(caption)
print(f" Added caption at (80, 48)")
# A highlight circle around cell (10, 7) = (160, 112)
# Circle needs center, not pos
circle = mcrfpy.Circle(center=(168, 120), radius=20,
fill_color=mcrfpy.Color(255, 255, 0, 100), fill_color=mcrfpy.Color(255, 255, 0, 100),
outline_color=mcrfpy.Color(255, 255, 0), outline_color=mcrfpy.Color(255, 255, 0),
outline=2) outline=2)
grid.children.append(circle) grid.children.append(circle)
print(f" Added highlight circle at (168, 120)") print(f" Added highlight circle at (168, 120)")
# A line indicating a path from (2,2) to (8,6) # A line indicating a path from (2,2) to (8,6)
# In pixels: (32, 32) to (128, 96) # In pixels: (32, 32) to (128, 96)
line = mcrfpy.Line(start=(32, 32), end=(128, 96), line = mcrfpy.Line(start=(32, 32), end=(128, 96),
color=mcrfpy.Color(0, 255, 0), thickness=3) color=mcrfpy.Color(0, 255, 0), thickness=3)
grid.children.append(line) grid.children.append(line)
print(f" Added path line from (32,32) to (128,96)") print(f" Added path line from (32,32) to (128,96)")
# An arc for range indicator at (15, 10) = (240, 160) # An arc for range indicator at (15, 10) = (240, 160)
arc = mcrfpy.Arc(center=(240, 160), radius=40, start_angle=0, end_angle=270, arc = mcrfpy.Arc(center=(240, 160), radius=40, start_angle=0, end_angle=270,
color=mcrfpy.Color(255, 0, 255), thickness=4) color=mcrfpy.Color(255, 0, 255), thickness=4)
grid.children.append(arc) grid.children.append(arc)
print(f" Added range arc at (240, 160)") print(f" Added range arc at (240, 160)")
# Test 3: Verify children count # Test 3: Verify children count
print(f"\nTest 3: Verifying children count...") print(f"\nTest 3: Verifying children count...")
print(f" grid.children count = {len(grid.children)}") print(f" grid.children count = {len(grid.children)}")
assert len(grid.children) == 4, f"Expected 4 children, got {len(grid.children)}" if len(grid.children) != 4:
print(f"FAIL: Expected 4 children, got {len(grid.children)}")
sys.exit(1)
# Test 4: Children should be accessible by index # Test 4: Children should be accessible by index
print("\nTest 4: Accessing children by index...") print("\nTest 4: Accessing children by index...")
child0 = grid.children[0] child0 = grid.children[0]
print(f" grid.children[0] = {child0}") print(f" grid.children[0] = {child0}")
child1 = grid.children[1] child1 = grid.children[1]
print(f" grid.children[1] = {child1}") print(f" grid.children[1] = {child1}")
# Test 5: Modify a child's position (should update in grid) # Test 5: Modify a child's position (should update in grid)
print("\nTest 5: Modifying child position...") print("\nTest 5: Modifying child position...")
original_pos = (caption.pos.x, caption.pos.y) original_pos = (caption.pos.x, caption.pos.y)
caption.pos = mcrfpy.Vector(90, 58) caption.pos = mcrfpy.Vector(90, 58)
new_pos = (caption.pos.x, caption.pos.y) new_pos = (caption.pos.x, caption.pos.y)
print(f" Moved caption from {original_pos} to {new_pos}") print(f" Moved caption from {original_pos} to {new_pos}")
# Test 6: Test z_index for children # Test 6: Test z_index for children
print("\nTest 6: Testing z_index ordering...") print("\nTest 6: Testing z_index ordering...")
# Add overlapping elements with different z_index # Add overlapping elements with different z_index
frame1 = mcrfpy.Frame(pos=(150, 80), size=(40, 40)) frame1 = mcrfpy.Frame(pos=(150, 80), size=(40, 40))
frame1.fill_color = mcrfpy.Color(255, 0, 0, 200) frame1.fill_color = mcrfpy.Color(255, 0, 0, 200)
frame1.z_index = 10 frame1.z_index = 10
grid.children.append(frame1) grid.children.append(frame1)
frame2 = mcrfpy.Frame(pos=(160, 90), size=(40, 40)) frame2 = mcrfpy.Frame(pos=(160, 90), size=(40, 40))
frame2.fill_color = mcrfpy.Color(0, 255, 0, 200) frame2.fill_color = mcrfpy.Color(0, 255, 0, 200)
frame2.z_index = 5 # Lower z_index, rendered first (behind) frame2.z_index = 5 # Lower z_index, rendered first (behind)
grid.children.append(frame2) grid.children.append(frame2)
print(f" Added overlapping frames: red z=10, green z=5") print(f" Added overlapping frames: red z=10, green z=5")
# Test 7: Test visibility # Test 7: Test visibility
print("\nTest 7: Testing child visibility...") print("\nTest 7: Testing child visibility...")
frame3 = mcrfpy.Frame(pos=(50, 150), size=(30, 30)) frame3 = mcrfpy.Frame(pos=(50, 150), size=(30, 30))
frame3.fill_color = mcrfpy.Color(0, 0, 255) frame3.fill_color = mcrfpy.Color(0, 0, 255)
frame3.visible = False frame3.visible = False
grid.children.append(frame3) grid.children.append(frame3)
print(f" Added invisible blue frame (should not appear)") print(f" Added invisible blue frame (should not appear)")
# Test 8: Pan the grid and verify children move with it # Test 8: Pan the grid and verify children move with it
print("\nTest 8: Testing pan (children should follow grid camera)...") print("\nTest 8: Testing pan (children should follow grid camera)...")
# Center the view on cell (10, 7.5) - default was grid center # Center the view on cell (10, 7.5) - default was grid center
grid.center = (160, 120) # Center on pixel (160, 120) grid.center = (160, 120) # Center on pixel (160, 120)
print(f" Centered grid on (160, 120)") print(f" Centered grid on (160, 120)")
# Test 9: Test zoom # Test 9: Test zoom
print("\nTest 9: Testing zoom...") print("\nTest 9: Testing zoom...")
grid.zoom = 1.5 grid.zoom = 1.5
print(f" Set zoom to 1.5") print(f" Set zoom to 1.5")
print(f"\nFinal children count: {len(grid.children)}") print(f"\nFinal children count: {len(grid.children)}")
# Schedule screenshot for next frame # Step to render everything
mcrfpy.Timer("screenshot", take_screenshot, 100, once=True) mcrfpy.step(0.1)
# Create a test scene # Take screenshot
test = mcrfpy.Scene("test") automation.screenshot("test_grid_children_result.png")
test.activate() print("Screenshot saved to test_grid_children_result.png")
# Schedule test to run after game loop starts print("PASS - Grid.children test completed")
mcrfpy.Timer("test", run_test, 50, once=True) sys.exit(0)

View file

@ -2,18 +2,24 @@
""" """
Test that all UI classes can be instantiated without arguments. Test that all UI classes can be instantiated without arguments.
This verifies the fix for requiring arguments even with safe default constructors. This verifies the fix for requiring arguments even with safe default constructors.
Refactored to use mcrfpy.step() for synchronous execution.
""" """
import mcrfpy import mcrfpy
import sys import sys
import traceback
def test_ui_constructors(timer, runtime): # Initialize scene
"""Test that UI classes can be instantiated without arguments""" test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
print("Testing UI class instantiation without arguments...") print("Testing UI class instantiation without arguments...")
# Test UICaption with no arguments all_pass = True
try:
# Test UICaption with no arguments
try:
caption = mcrfpy.Caption() caption = mcrfpy.Caption()
print("PASS: Caption() - Success") print("PASS: Caption() - Success")
print(f" Position: ({caption.x}, {caption.y})") print(f" Position: ({caption.x}, {caption.y})")
@ -21,13 +27,13 @@ def test_ui_constructors(timer, runtime):
assert caption.x == 0.0 assert caption.x == 0.0
assert caption.y == 0.0 assert caption.y == 0.0
assert caption.text == "" assert caption.text == ""
except Exception as e: except Exception as e:
print(f"FAIL: Caption() - {e}") print(f"FAIL: Caption() - {e}")
import traceback
traceback.print_exc() traceback.print_exc()
all_pass = False
# Test UIFrame with no arguments # Test UIFrame with no arguments
try: try:
frame = mcrfpy.Frame() frame = mcrfpy.Frame()
print("PASS: Frame() - Success") print("PASS: Frame() - Success")
print(f" Position: ({frame.x}, {frame.y})") print(f" Position: ({frame.x}, {frame.y})")
@ -36,13 +42,13 @@ def test_ui_constructors(timer, runtime):
assert frame.y == 0.0 assert frame.y == 0.0
assert frame.w == 0.0 assert frame.w == 0.0
assert frame.h == 0.0 assert frame.h == 0.0
except Exception as e: except Exception as e:
print(f"FAIL: Frame() - {e}") print(f"FAIL: Frame() - {e}")
import traceback
traceback.print_exc() traceback.print_exc()
all_pass = False
# Test UIGrid with no arguments # Test UIGrid with no arguments
try: try:
grid = mcrfpy.Grid() grid = mcrfpy.Grid()
print("PASS: Grid() - Success") print("PASS: Grid() - Success")
print(f" Grid size: {grid.grid_x} x {grid.grid_y}") print(f" Grid size: {grid.grid_x} x {grid.grid_y}")
@ -51,41 +57,39 @@ def test_ui_constructors(timer, runtime):
assert grid.grid_y == 1 assert grid.grid_y == 1
assert grid.x == 0.0 assert grid.x == 0.0
assert grid.y == 0.0 assert grid.y == 0.0
except Exception as e: except Exception as e:
print(f"FAIL: Grid() - {e}") print(f"FAIL: Grid() - {e}")
import traceback
traceback.print_exc() traceback.print_exc()
all_pass = False
# Test UIEntity with no arguments # Test UIEntity with no arguments
try: try:
entity = mcrfpy.Entity() entity = mcrfpy.Entity()
print("PASS: Entity() - Success") print("PASS: Entity() - Success")
print(f" Position: ({entity.x}, {entity.y})") print(f" Position: ({entity.x}, {entity.y})")
assert entity.x == 0.0 assert entity.x == 0.0
assert entity.y == 0.0 assert entity.y == 0.0
except Exception as e: except Exception as e:
print(f"FAIL: Entity() - {e}") print(f"FAIL: Entity() - {e}")
import traceback
traceback.print_exc() traceback.print_exc()
all_pass = False
# Test UISprite with no arguments (if it has a default constructor) # Test UISprite with no arguments (if it has a default constructor)
try: try:
sprite = mcrfpy.Sprite() sprite = mcrfpy.Sprite()
print("PASS: Sprite() - Success") print("PASS: Sprite() - Success")
print(f" Position: ({sprite.x}, {sprite.y})") print(f" Position: ({sprite.x}, {sprite.y})")
assert sprite.x == 0.0 assert sprite.x == 0.0
assert sprite.y == 0.0 assert sprite.y == 0.0
except Exception as e: except Exception as e:
print(f"FAIL: Sprite() - {e}") print(f"FAIL: Sprite() - {e}")
# Sprite might still require arguments, which is okay # Sprite might still require arguments, which is okay
print("\nAll tests complete!") print("\nAll tests complete!")
# Exit cleanly if all_pass:
print("PASS")
sys.exit(0) sys.exit(0)
else:
# Create a basic scene so the game can start print("FAIL")
test = mcrfpy.Scene("test") sys.exit(1)
# Schedule the test to run after game initialization
test_timer = mcrfpy.Timer("test", test_ui_constructors, 100, once=True)

View file

@ -1,15 +1,21 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Quick test of drawable properties""" """Quick test of drawable properties
Refactored to use mcrfpy.step() for synchronous execution.
"""
import mcrfpy import mcrfpy
import sys import sys
def test_properties(timer, runtime): # Initialize scene
timer.stop() test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
print("\n=== Testing Properties ===") print("\n=== Testing Properties ===")
# Test Frame all_pass = True
try:
# Test Frame
try:
frame = mcrfpy.Frame(pos=(10, 10), size=(100, 100)) frame = mcrfpy.Frame(pos=(10, 10), size=(100, 100))
print(f"Frame visible: {frame.visible}") print(f"Frame visible: {frame.visible}")
frame.visible = False frame.visible = False
@ -26,12 +32,13 @@ def test_properties(timer, runtime):
bounds2 = frame.get_bounds() bounds2 = frame.get_bounds()
print(f"Frame bounds after move(5,5): {bounds2}") print(f"Frame bounds after move(5,5): {bounds2}")
print("✓ Frame properties work!") print("+ Frame properties work!")
except Exception as e: except Exception as e:
print(f"✗ Frame failed: {e}") print(f"x Frame failed: {e}")
all_pass = False
# Test Entity # Test Entity
try: try:
entity = mcrfpy.Entity() entity = mcrfpy.Entity()
print(f"\nEntity visible: {entity.visible}") print(f"\nEntity visible: {entity.visible}")
entity.visible = False entity.visible = False
@ -47,11 +54,14 @@ def test_properties(timer, runtime):
entity.move(3, 3) entity.move(3, 3)
print(f"Entity position after move(3,3): ({entity.x}, {entity.y})") print(f"Entity position after move(3,3): ({entity.x}, {entity.y})")
print("✓ Entity properties work!") print("+ Entity properties work!")
except Exception as e: except Exception as e:
print(f"✗ Entity failed: {e}") print(f"x Entity failed: {e}")
all_pass = False
if all_pass:
print("\nPASS")
sys.exit(0) sys.exit(0)
else:
test = mcrfpy.Scene("test") print("\nFAIL")
test_properties_timer = mcrfpy.Timer("test_properties", test_properties, 100, once=True) sys.exit(1)

View file

@ -4,6 +4,7 @@ Test for Python object cache - verifies that derived Python classes
maintain their identity when stored in and retrieved from collections. maintain their identity when stored in and retrieved from collections.
Issue #112: Object Splitting - Preserve Python derived types in collections Issue #112: Object Splitting - Preserve Python derived types in collections
Refactored to use mcrfpy.step() for synchronous execution.
""" """
import mcrfpy import mcrfpy
@ -16,136 +17,128 @@ test_results = []
def test(condition, message): def test(condition, message):
global test_passed global test_passed
if condition: if condition:
test_results.append(f" {message}") test_results.append(f"+ {message}")
else: else:
test_results.append(f" {message}") test_results.append(f"x {message}")
test_passed = False test_passed = False
def run_tests(timer, runtime): # Create test scene
"""Timer callback to run tests after game loop starts""" test_scene = mcrfpy.Scene("test_scene")
global test_passed test_scene.activate()
mcrfpy.step(0.01)
print("\n=== Testing Python Object Cache ===") print("\n=== Testing Python Object Cache ===")
# Test 1: Create derived Frame class # Test 1: Create derived Frame class
class MyFrame(mcrfpy.Frame): class MyFrame(mcrfpy.Frame):
def __init__(self, x=0, y=0): def __init__(self, x=0, y=0):
super().__init__(pos=(x, y), size=(100, 100)) super().__init__(pos=(x, y), size=(100, 100))
self.custom_data = "I am a custom frame" self.custom_data = "I am a custom frame"
self.test_value = 42 self.test_value = 42
# Test 2: Create instance and add to scene # Test 2: Create instance and add to scene
frame = MyFrame(50, 50) frame = MyFrame(50, 50)
scene_ui = test_scene.children scene_ui = test_scene.children
scene_ui.append(frame) scene_ui.append(frame)
# Test 3: Retrieve from collection and check type # Test 3: Retrieve from collection and check type
retrieved = scene_ui[0] retrieved = scene_ui[0]
test(type(retrieved) == MyFrame, "Retrieved object maintains derived type") test(type(retrieved) == MyFrame, "Retrieved object maintains derived type")
test(isinstance(retrieved, MyFrame), "isinstance check passes") test(isinstance(retrieved, MyFrame), "isinstance check passes")
test(hasattr(retrieved, 'custom_data'), "Custom attribute exists") test(hasattr(retrieved, 'custom_data'), "Custom attribute exists")
if hasattr(retrieved, 'custom_data'): if hasattr(retrieved, 'custom_data'):
test(retrieved.custom_data == "I am a custom frame", "Custom attribute value preserved") test(retrieved.custom_data == "I am a custom frame", "Custom attribute value preserved")
if hasattr(retrieved, 'test_value'): if hasattr(retrieved, 'test_value'):
test(retrieved.test_value == 42, "Numeric attribute value preserved") test(retrieved.test_value == 42, "Numeric attribute value preserved")
# Test 4: Check object identity (same Python object) # Test 4: Check object identity (same Python object)
test(retrieved is frame, "Retrieved object is the same Python object") test(retrieved is frame, "Retrieved object is the same Python object")
test(id(retrieved) == id(frame), "Object IDs match") test(id(retrieved) == id(frame), "Object IDs match")
# Test 5: Multiple retrievals return same object # Test 5: Multiple retrievals return same object
retrieved2 = scene_ui[0] retrieved2 = scene_ui[0]
test(retrieved2 is retrieved, "Multiple retrievals return same object") test(retrieved2 is retrieved, "Multiple retrievals return same object")
# Test 6: Test with other UI types # Test 6: Test with other UI types
class MySprite(mcrfpy.Sprite): class MySprite(mcrfpy.Sprite):
def __init__(self): def __init__(self):
# Use default texture by passing None # Use default texture by passing None
super().__init__(texture=None, sprite_index=0) super().__init__(texture=None, sprite_index=0)
self.sprite_data = "custom sprite" self.sprite_data = "custom sprite"
sprite = MySprite() sprite = MySprite()
sprite.x = 200 sprite.x = 200
sprite.y = 200 sprite.y = 200
scene_ui.append(sprite) scene_ui.append(sprite)
retrieved_sprite = scene_ui[1] retrieved_sprite = scene_ui[1]
test(type(retrieved_sprite) == MySprite, "Sprite maintains derived type") test(type(retrieved_sprite) == MySprite, "Sprite maintains derived type")
if hasattr(retrieved_sprite, 'sprite_data'): if hasattr(retrieved_sprite, 'sprite_data'):
test(retrieved_sprite.sprite_data == "custom sprite", "Sprite custom data preserved") test(retrieved_sprite.sprite_data == "custom sprite", "Sprite custom data preserved")
# Test 7: Test with Caption # Test 7: Test with Caption
class MyCaption(mcrfpy.Caption): class MyCaption(mcrfpy.Caption):
def __init__(self, text): def __init__(self, text):
# Use default font by passing None # Use default font by passing None
super().__init__(text=text, font=None) super().__init__(text=text, font=None)
self.caption_id = "test_caption" self.caption_id = "test_caption"
caption = MyCaption("Test Caption") caption = MyCaption("Test Caption")
caption.x = 10 caption.x = 10
caption.y = 10 caption.y = 10
scene_ui.append(caption) scene_ui.append(caption)
retrieved_caption = scene_ui[2] retrieved_caption = scene_ui[2]
test(type(retrieved_caption) == MyCaption, "Caption maintains derived type") test(type(retrieved_caption) == MyCaption, "Caption maintains derived type")
if hasattr(retrieved_caption, 'caption_id'): if hasattr(retrieved_caption, 'caption_id'):
test(retrieved_caption.caption_id == "test_caption", "Caption custom data preserved") test(retrieved_caption.caption_id == "test_caption", "Caption custom data preserved")
# Test 8: Test removal and re-addition # Test 8: Test removal and re-addition
# Use del to remove by index (Python standard), or .remove(element) to remove by value # Use del to remove by index (Python standard), or .remove(element) to remove by value
print(f"before remove: {len(scene_ui)=}") print(f"before remove: {len(scene_ui)=}")
del scene_ui[-1] # Remove last element by index del scene_ui[-1] # Remove last element by index
print(f"after remove: {len(scene_ui)=}") print(f"after remove: {len(scene_ui)=}")
scene_ui.append(frame) scene_ui.append(frame)
retrieved3 = scene_ui[-1] # Get last element retrieved3 = scene_ui[-1] # Get last element
test(retrieved3 is frame, "Object identity preserved after removal/re-addition") test(retrieved3 is frame, "Object identity preserved after removal/re-addition")
# Test 9: Test with Grid # Test 9: Test with Grid
class MyGrid(mcrfpy.Grid): class MyGrid(mcrfpy.Grid):
def __init__(self, w, h): def __init__(self, w, h):
super().__init__(grid_size=(w, h)) super().__init__(grid_size=(w, h))
self.grid_name = "custom_grid" self.grid_name = "custom_grid"
grid = MyGrid(10, 10) grid = MyGrid(10, 10)
grid.x = 300 grid.x = 300
grid.y = 100 grid.y = 100
scene_ui.append(grid) scene_ui.append(grid)
retrieved_grid = scene_ui[-1] retrieved_grid = scene_ui[-1]
test(type(retrieved_grid) == MyGrid, "Grid maintains derived type") test(type(retrieved_grid) == MyGrid, "Grid maintains derived type")
if hasattr(retrieved_grid, 'grid_name'): if hasattr(retrieved_grid, 'grid_name'):
test(retrieved_grid.grid_name == "custom_grid", "Grid custom data preserved") test(retrieved_grid.grid_name == "custom_grid", "Grid custom data preserved")
# Test 10: Test with nested collections (Frame with children) # Test 10: Test with nested collections (Frame with children)
parent = MyFrame(400, 400) parent = MyFrame(400, 400)
child = MyFrame(10, 10) child = MyFrame(10, 10)
child.custom_data = "I am a child" child.custom_data = "I am a child"
parent.children.append(child) parent.children.append(child)
scene_ui.append(parent) scene_ui.append(parent)
retrieved_parent = scene_ui[-1] retrieved_parent = scene_ui[-1]
test(type(retrieved_parent) == MyFrame, "Parent frame maintains type") test(type(retrieved_parent) == MyFrame, "Parent frame maintains type")
if len(retrieved_parent.children) > 0: if len(retrieved_parent.children) > 0:
retrieved_child = retrieved_parent.children[0] retrieved_child = retrieved_parent.children[0]
test(type(retrieved_child) == MyFrame, "Child frame maintains type in nested collection") test(type(retrieved_child) == MyFrame, "Child frame maintains type in nested collection")
if hasattr(retrieved_child, 'custom_data'): if hasattr(retrieved_child, 'custom_data'):
test(retrieved_child.custom_data == "I am a child", "Child custom data preserved") test(retrieved_child.custom_data == "I am a child", "Child custom data preserved")
# Print results # Print results
print("\n=== Test Results ===") print("\n=== Test Results ===")
for result in test_results: for result in test_results:
print(result) print(result)
print(f"\n{'PASS' if test_passed else 'FAIL'}: {sum(1 for r in test_results if r.startswith(''))}/{len(test_results)} tests passed") print(f"\n{'PASS' if test_passed else 'FAIL'}: {sum(1 for r in test_results if r.startswith('+'))}/{len(test_results)} tests passed")
sys.exit(0 if test_passed else 1) sys.exit(0 if test_passed else 1)
# Create test scene
test_scene = mcrfpy.Scene("test_scene")
test_scene.activate()
# Schedule tests to run after game loop starts
test_timer = mcrfpy.Timer("test", run_tests, 100, once=True)
print("Python object cache test initialized. Running tests...")

View file

@ -1,14 +1,32 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Very simple callback test""" """Very simple callback test - refactored to use mcrfpy.step()"""
import mcrfpy import mcrfpy
import sys import sys
callback_fired = False
def cb(a, t): def cb(a, t):
global callback_fired
callback_fired = True
print("CB") print("CB")
# Setup scene
test = mcrfpy.Scene("test") test = mcrfpy.Scene("test")
test.activate() test.activate()
mcrfpy.step(0.01) # Initialize
# Create entity and animation
e = mcrfpy.Entity((0, 0), texture=None, sprite_index=0) e = mcrfpy.Entity((0, 0), texture=None, sprite_index=0)
a = mcrfpy.Animation("x", 1.0, 0.1, "linear", callback=cb) a = mcrfpy.Animation("x", 1.0, 0.1, "linear", callback=cb)
a.start(e) a.start(e)
mcrfpy.Timer("exit", lambda t, r: sys.exit(0), 200, once=True)
# Advance past animation duration (0.1s)
mcrfpy.step(0.15)
# Verify callback fired
if callback_fired:
print("PASS: Callback fired")
sys.exit(0)
else:
print("FAIL: Callback did not fire")
sys.exit(1)

View file

@ -1,12 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Simple test to isolate drawable issue""" """Simple test to isolate drawable issue
Refactored to use mcrfpy.step() for synchronous execution.
"""
import mcrfpy import mcrfpy
import sys import sys
def simple_test(timer, runtime): # Initialize scene
timer.stop() test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
try: try:
# Test basic functionality # Test basic functionality
frame = mcrfpy.Frame(pos=(10, 10), size=(100, 100)) frame = mcrfpy.Frame(pos=(10, 10), size=(100, 100))
print(f"Frame created: visible={frame.visible}, opacity={frame.opacity}") print(f"Frame created: visible={frame.visible}, opacity={frame.opacity}")
@ -21,10 +25,8 @@ def simple_test(timer, runtime):
print("Resize completed") print("Resize completed")
print("PASS - No crash!") print("PASS - No crash!")
except Exception as e:
print(f"ERROR: {e}")
sys.exit(0) sys.exit(0)
except Exception as e:
test = mcrfpy.Scene("test") print(f"ERROR: {e}")
simple_test_timer = mcrfpy.Timer("simple_test", simple_test, 100, once=True) print("FAIL")
sys.exit(1)