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_timer_callback.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
These tests still use Timer-based async patterns:
- WORKING_automation_test_example.py
- benchmark_logging_test.py
- keypress_scene_validation_test.py
- test_empty_animation_manager.py
- test_simple_callback.py
**Headless mode tests:**
- test_headless_detection.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
```bash

View file

@ -1,47 +1,11 @@
#!/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
from mcrfpy import automation
from datetime import datetime
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)
import sys
# This code runs during --exec script execution
print("=== Setting Up Test Scene ===")
@ -49,6 +13,8 @@ print("=== Setting Up Test Scene ===")
# Create scene with visible content
timer_test_scene = mcrfpy.Scene("timer_test_scene")
timer_test_scene.activate()
mcrfpy.step(0.01) # Initialize scene
ui = timer_test_scene.children
# Add a bright red frame that should be visible
@ -60,23 +26,57 @@ ui.append(frame)
# Add text
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))
caption.font_size = 24
frame.children.append(caption)
# Add click handler to demonstrate interaction
click_received = False
def frame_clicked(x, y, button):
global click_received
click_received = True
print(f"Frame clicked at ({x}, {y}) with button {button}")
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
automation_test_timer = mcrfpy.Timer("automation_test", run_automation_tests, 1000, once=True)
# Step to render the scene
mcrfpy.step(0.1)
print("Timer set. Game loop will start after this script completes.")
print("Automation tests will run 1 second later when content is visible.")
print("\n=== Automation Test Running ===")
# 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,182 +1,181 @@
#!/usr/bin/env python3
"""
Test #94: Color helper methods - from_hex, to_hex, lerp
Refactored to use mcrfpy.step() for synchronous execution.
"""
import mcrfpy
import sys
def test_color_helpers(timer, runtime):
"""Test Color helper methods"""
all_pass = True
# Test 1: from_hex with # prefix
try:
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}"
print("+ Color.from_hex('#FF0000') works")
except Exception as e:
print(f"x Color.from_hex('#FF0000') failed: {e}")
all_pass = False
# Test 2: from_hex without # prefix
try:
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}"
print("+ Color.from_hex('00FF00') works")
except Exception as e:
print(f"x Color.from_hex('00FF00') failed: {e}")
all_pass = False
# Test 3: from_hex with alpha
try:
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}"
print("+ Color.from_hex('#0000FF80') with alpha works")
except Exception as e:
print(f"x Color.from_hex('#0000FF80') failed: {e}")
all_pass = False
# Test 4: from_hex error handling
try:
c4 = mcrfpy.Color.from_hex("GGGGGG")
print("x from_hex should fail on invalid hex")
all_pass = False
except ValueError as e:
print("+ Color.from_hex() correctly rejects invalid hex")
# Test 5: from_hex wrong length
try:
c5 = mcrfpy.Color.from_hex("FF00")
print("x from_hex should fail on wrong length")
all_pass = False
except ValueError as e:
print("+ Color.from_hex() correctly rejects wrong length")
# Test 6: to_hex without alpha
try:
c6 = mcrfpy.Color(255, 128, 64)
hex_str = c6.to_hex()
assert hex_str == "#FF8040", f"to_hex() failed: {hex_str}"
print("+ Color.to_hex() works")
except Exception as e:
print(f"x Color.to_hex() failed: {e}")
all_pass = False
# Test 7: to_hex with alpha
try:
c7 = mcrfpy.Color(255, 128, 64, 127)
hex_str = c7.to_hex()
assert hex_str == "#FF80407F", f"to_hex() with alpha failed: {hex_str}"
print("+ Color.to_hex() with alpha works")
except Exception as e:
print(f"x Color.to_hex() with alpha failed: {e}")
all_pass = False
# Test 8: Round-trip hex conversion
try:
original_hex = "#ABCDEF"
c8 = mcrfpy.Color.from_hex(original_hex)
result_hex = c8.to_hex()
assert result_hex == original_hex, f"Round-trip failed: {original_hex} -> {result_hex}"
print("+ Hex round-trip conversion works")
except Exception as e:
print(f"x Hex round-trip failed: {e}")
all_pass = False
# Test 9: lerp at t=0
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 0.0)
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")
except Exception as e:
print(f"x Color.lerp(t=0) failed: {e}")
all_pass = False
# Test 10: lerp at t=1
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 1.0)
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")
except Exception as e:
print(f"x Color.lerp(t=1) failed: {e}")
all_pass = False
# Test 11: lerp at t=0.5
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 0.5)
# 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}"
print("+ Color.lerp(t=0.5) returns midpoint")
except Exception as e:
print(f"x Color.lerp(t=0.5) failed: {e}")
all_pass = False
# Test 12: lerp with alpha
try:
c1 = mcrfpy.Color(255, 0, 0, 255)
c2 = mcrfpy.Color(0, 255, 0, 0)
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.a <= 128, f"lerp alpha failed: {result.a}"
print("+ Color.lerp() with alpha works")
except Exception as e:
print(f"x Color.lerp() with alpha failed: {e}")
all_pass = False
# Test 13: lerp clamps t < 0
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
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"
print("+ Color.lerp() clamps t < 0")
except Exception as e:
print(f"x Color.lerp(t<0) failed: {e}")
all_pass = False
# Test 14: lerp clamps t > 1
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
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"
print("+ Color.lerp() clamps t > 1")
except Exception as e:
print(f"x Color.lerp(t>1) failed: {e}")
all_pass = False
# Test 15: Practical use case - gradient
try:
start = mcrfpy.Color.from_hex("#FF0000") # Red
end = mcrfpy.Color.from_hex("#0000FF") # Blue
# Create 5-step gradient
steps = []
for i in range(5):
t = i / 4.0
color = start.lerp(end, t)
steps.append(color.to_hex())
assert steps[0] == "#FF0000", "Gradient start should be red"
assert steps[4] == "#0000FF", "Gradient end should be blue"
assert len(set(steps)) == 5, "All gradient steps should be unique"
print("+ Gradient generation works correctly")
except Exception as e:
print(f"x Gradient generation failed: {e}")
all_pass = False
print(f"\n{'PASS' if all_pass else 'FAIL'}")
sys.exit(0 if all_pass else 1)
# Run test
# Initialize scene
test = mcrfpy.Scene("test")
test_timer = mcrfpy.Timer("test", test_color_helpers, 100, once=True)
test.activate()
mcrfpy.step(0.01)
all_pass = True
# Test 1: from_hex with # prefix
try:
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}"
print("+ Color.from_hex('#FF0000') works")
except Exception as e:
print(f"x Color.from_hex('#FF0000') failed: {e}")
all_pass = False
# Test 2: from_hex without # prefix
try:
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}"
print("+ Color.from_hex('00FF00') works")
except Exception as e:
print(f"x Color.from_hex('00FF00') failed: {e}")
all_pass = False
# Test 3: from_hex with alpha
try:
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}"
print("+ Color.from_hex('#0000FF80') with alpha works")
except Exception as e:
print(f"x Color.from_hex('#0000FF80') failed: {e}")
all_pass = False
# Test 4: from_hex error handling
try:
c4 = mcrfpy.Color.from_hex("GGGGGG")
print("x from_hex should fail on invalid hex")
all_pass = False
except ValueError as e:
print("+ Color.from_hex() correctly rejects invalid hex")
# Test 5: from_hex wrong length
try:
c5 = mcrfpy.Color.from_hex("FF00")
print("x from_hex should fail on wrong length")
all_pass = False
except ValueError as e:
print("+ Color.from_hex() correctly rejects wrong length")
# Test 6: to_hex without alpha
try:
c6 = mcrfpy.Color(255, 128, 64)
hex_str = c6.to_hex()
assert hex_str == "#FF8040", f"to_hex() failed: {hex_str}"
print("+ Color.to_hex() works")
except Exception as e:
print(f"x Color.to_hex() failed: {e}")
all_pass = False
# Test 7: to_hex with alpha
try:
c7 = mcrfpy.Color(255, 128, 64, 127)
hex_str = c7.to_hex()
assert hex_str == "#FF80407F", f"to_hex() with alpha failed: {hex_str}"
print("+ Color.to_hex() with alpha works")
except Exception as e:
print(f"x Color.to_hex() with alpha failed: {e}")
all_pass = False
# Test 8: Round-trip hex conversion
try:
original_hex = "#ABCDEF"
c8 = mcrfpy.Color.from_hex(original_hex)
result_hex = c8.to_hex()
assert result_hex == original_hex, f"Round-trip failed: {original_hex} -> {result_hex}"
print("+ Hex round-trip conversion works")
except Exception as e:
print(f"x Hex round-trip failed: {e}")
all_pass = False
# Test 9: lerp at t=0
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 0.0)
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")
except Exception as e:
print(f"x Color.lerp(t=0) failed: {e}")
all_pass = False
# Test 10: lerp at t=1
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 1.0)
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")
except Exception as e:
print(f"x Color.lerp(t=1) failed: {e}")
all_pass = False
# Test 11: lerp at t=0.5
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
result = red.lerp(blue, 0.5)
# 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}"
print("+ Color.lerp(t=0.5) returns midpoint")
except Exception as e:
print(f"x Color.lerp(t=0.5) failed: {e}")
all_pass = False
# Test 12: lerp with alpha
try:
c1 = mcrfpy.Color(255, 0, 0, 255)
c2 = mcrfpy.Color(0, 255, 0, 0)
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.a <= 128, f"lerp alpha failed: {result.a}"
print("+ Color.lerp() with alpha works")
except Exception as e:
print(f"x Color.lerp() with alpha failed: {e}")
all_pass = False
# Test 13: lerp clamps t < 0
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
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"
print("+ Color.lerp() clamps t < 0")
except Exception as e:
print(f"x Color.lerp(t<0) failed: {e}")
all_pass = False
# Test 14: lerp clamps t > 1
try:
red = mcrfpy.Color(255, 0, 0)
blue = mcrfpy.Color(0, 0, 255)
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"
print("+ Color.lerp() clamps t > 1")
except Exception as e:
print(f"x Color.lerp(t>1) failed: {e}")
all_pass = False
# Test 15: Practical use case - gradient
try:
start = mcrfpy.Color.from_hex("#FF0000") # Red
end = mcrfpy.Color.from_hex("#0000FF") # Blue
# Create 5-step gradient
steps = []
for i in range(5):
t = i / 4.0
color = start.lerp(end, t)
steps.append(color.to_hex())
assert steps[0] == "#FF0000", "Gradient start should be red"
assert steps[4] == "#0000FF", "Gradient end should be blue"
assert len(set(steps)) == 5, "All gradient steps should be unique"
print("+ Gradient generation works correctly")
except Exception as e:
print(f"x Gradient generation failed: {e}")
all_pass = False
print(f"\n{'PASS' if all_pass else 'FAIL'}")
sys.exit(0 if all_pass else 1)

View file

@ -1,20 +1,28 @@
#!/usr/bin/env python3
"""
Test if AnimationManager crashes with no animations
Refactored to use mcrfpy.step() for synchronous execution.
"""
import mcrfpy
import sys
print("Creating empty scene...")
test = mcrfpy.Scene("test")
test.activate()
print("Scene created, no animations added")
print("Starting game loop in 100ms...")
print("Advancing simulation with step()...")
def check_alive(timer, runtime):
print(f"Timer fired at {runtime}ms - AnimationManager survived!")
mcrfpy.Timer("exit", lambda t, r: mcrfpy.exit(), 100, once=True)
# Step multiple times to simulate game loop running
# If AnimationManager crashes with empty state, this will fail
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("If this crashes immediately, AnimationManager has an issue with empty state")
print("AnimationManager survived 10 steps with no animations!")
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
"""Test UIFrame clipping functionality"""
"""Test UIFrame clipping functionality
Refactored to use mcrfpy.step() for synchronous execution.
"""
import mcrfpy
from mcrfpy import Color, Frame, Caption
from mcrfpy import Color, Frame, Caption, automation
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...")
test = mcrfpy.Scene("test")
test.activate()
test.on_key = handle_keypress
test_clipping_timer = mcrfpy.Timer("test_clipping", test_clipping, 100, once=True)
print("Test scheduled, running...")
mcrfpy.step(0.01) # Initialize
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
"""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
from mcrfpy import Color, Frame, Caption, Vector
from mcrfpy import Color, Frame, Caption, Vector, automation
import sys
def test_nested_clipping(timer, runtime):
"""Test nested frames with clipping"""
timer.stop()
print("Testing advanced UIFrame clipping with nested frames...")
# Create test scene
scene = test.children
# Create outer frame with clipping enabled
outer = Frame(pos=(50, 50), size=(400, 300),
fill_color=Color(50, 50, 150),
outline_color=Color(255, 255, 255),
outline=3)
outer.name = "outer"
outer.clip_children = True
scene.append(outer)
# Create inner frame that extends beyond outer bounds
inner = Frame(pos=(200, 150), size=(300, 200),
fill_color=Color(150, 50, 50),
outline_color=Color(255, 255, 0),
outline=2)
inner.name = "inner"
inner.clip_children = True # Also enable clipping on inner frame
outer.children.append(inner)
# Add content to inner frame that extends beyond its bounds
for i in range(5):
caption = Caption(text=f"Line {i+1}: This text should be double-clipped", pos=(10, 30 * i))
caption.font_size = 14
caption.fill_color = Color(255, 255, 255)
inner.children.append(caption)
# Add a child frame to inner that extends way out
deeply_nested = Frame(pos=(250, 100), size=(200, 150),
fill_color=Color(50, 150, 50),
outline_color=Color(255, 0, 255),
outline=2)
deeply_nested.name = "deeply_nested"
inner.children.append(deeply_nested)
# Add status text
status = Caption(text="Nested clipping test:\n"
"- Blue outer frame clips red inner frame\n"
"- Red inner frame clips green deeply nested frame\n"
"- All text should be clipped to frame bounds",
pos=(50, 380))
status.font_size = 12
status.fill_color = Color(200, 200, 200)
scene.append(status)
# Test render texture size handling
print(f"Outer frame size: {outer.w}x{outer.h}")
print(f"Inner frame size: {inner.w}x{inner.h}")
# Dynamically resize frames to test RenderTexture recreation
def resize_test(timer, runtime):
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
global screenshot_resize_timer
screenshot_resize_timer = mcrfpy.Timer("screenshot_resize", take_resize_screenshot, 500, once=True)
def take_resize_screenshot(timer, runtime):
timer.stop()
from mcrfpy import automation
automation.screenshot("frame_clipping_resized.png")
print("\nAdvanced test completed!")
print("Screenshots saved:")
print(" - frame_clipping_resized.png (after resize)")
sys.exit(0)
# Take initial screenshot
from mcrfpy import automation
automation.screenshot("frame_clipping_nested.png")
print("Initial screenshot saved: frame_clipping_nested.png")
# Schedule resize test
global resize_test_timer
resize_test_timer = mcrfpy.Timer("resize_test", resize_test, 1000, once=True)
# Main execution
print("Creating advanced test scene...")
test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
# Schedule the test
test_nested_clipping_timer = mcrfpy.Timer("test_nested_clipping", test_nested_clipping, 100, once=True)
print("Testing advanced UIFrame clipping with nested frames...")
print("Advanced test scheduled, running...")
# Create test scene
scene = test.children
# Create outer frame with clipping enabled
outer = Frame(pos=(50, 50), size=(400, 300),
fill_color=Color(50, 50, 150),
outline_color=Color(255, 255, 255),
outline=3)
outer.name = "outer"
outer.clip_children = True
scene.append(outer)
# Create inner frame that extends beyond outer bounds
inner = Frame(pos=(200, 150), size=(300, 200),
fill_color=Color(150, 50, 50),
outline_color=Color(255, 255, 0),
outline=2)
inner.name = "inner"
inner.clip_children = True # Also enable clipping on inner frame
outer.children.append(inner)
# Add content to inner frame that extends beyond its bounds
for i in range(5):
caption = Caption(text=f"Line {i+1}: This text should be double-clipped", pos=(10, 30 * i))
caption.font_size = 14
caption.fill_color = Color(255, 255, 255)
inner.children.append(caption)
# Add a child frame to inner that extends way out
deeply_nested = Frame(pos=(250, 100), size=(200, 150),
fill_color=Color(50, 150, 50),
outline_color=Color(255, 0, 255),
outline=2)
deeply_nested.name = "deeply_nested"
inner.children.append(deeply_nested)
# Add status text
status = Caption(text="Nested clipping test:\n"
"- Blue outer frame clips red inner frame\n"
"- Red inner frame clips green deeply nested frame\n"
"- All text should be clipped to frame bounds",
pos=(50, 380))
status.font_size = 12
status.fill_color = Color(200, 200, 200)
scene.append(status)
# Test render texture size handling
print(f"Outer frame size: {outer.w}x{outer.h}")
print(f"Inner frame size: {inner.w}x{inner.h}")
# Step to render
mcrfpy.step(0.1)
# Take initial screenshot
automation.screenshot("frame_clipping_nested.png")
print("Initial screenshot saved: frame_clipping_nested.png")
# Dynamically resize frames to test RenderTexture recreation
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}")
# Step to render resize
mcrfpy.step(0.1)
# Take screenshot after resize
automation.screenshot("frame_clipping_resized.png")
print("\nAdvanced test completed!")
print("Screenshots saved:")
print(" - frame_clipping_nested.png (initial)")
print(" - frame_clipping_resized.png (after resize)")
print("PASS")
sys.exit(0)

View file

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

View file

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

View file

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

View file

@ -1,14 +1,32 @@
#!/usr/bin/env python3
"""Very simple callback test"""
"""Very simple callback test - refactored to use mcrfpy.step()"""
import mcrfpy
import sys
callback_fired = False
def cb(a, t):
global callback_fired
callback_fired = True
print("CB")
# Setup scene
test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01) # Initialize
# Create entity and animation
e = mcrfpy.Entity((0, 0), texture=None, sprite_index=0)
a = mcrfpy.Animation("x", 1.0, 0.1, "linear", callback=cb)
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,30 +1,32 @@
#!/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 sys
def simple_test(timer, runtime):
timer.stop()
try:
# Test basic functionality
frame = mcrfpy.Frame(pos=(10, 10), size=(100, 100))
print(f"Frame created: visible={frame.visible}, opacity={frame.opacity}")
bounds = frame.get_bounds()
print(f"Bounds: {bounds}")
frame.move(5, 5)
print("Move completed")
frame.resize(150, 150)
print("Resize completed")
print("PASS - No crash!")
except Exception as e:
print(f"ERROR: {e}")
sys.exit(0)
# Initialize scene
test = mcrfpy.Scene("test")
simple_test_timer = mcrfpy.Timer("simple_test", simple_test, 100, once=True)
test.activate()
mcrfpy.step(0.01)
try:
# Test basic functionality
frame = mcrfpy.Frame(pos=(10, 10), size=(100, 100))
print(f"Frame created: visible={frame.visible}, opacity={frame.opacity}")
bounds = frame.get_bounds()
print(f"Bounds: {bounds}")
frame.move(5, 5)
print("Move completed")
frame.resize(150, 150)
print("Resize completed")
print("PASS - No crash!")
sys.exit(0)
except Exception as e:
print(f"ERROR: {e}")
print("FAIL")
sys.exit(1)