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,12 +1,51 @@
#!/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
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) ===")
# This code runs during --exec script execution
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
frame = mcrfpy.Frame(pos=(100, 100), size=(400, 300),
fill_color=mcrfpy.Color(255, 0, 0), # Bright red
outline_color=mcrfpy.Color(255, 255, 255), # White outline
outline=5.0)
ui.append(frame)
# Add text
caption = mcrfpy.Caption(pos=(150, 150),
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.")
# Step to render the scene
mcrfpy.step(0.1)
print("\n=== Automation Test Running ===")
# NOW we can take screenshots that will show content!
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
@ -19,8 +58,14 @@ def run_automation_tests(timer, runtime):
# 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 timer callback!")
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"
@ -29,54 +74,9 @@ def run_automation_tests(timer, runtime):
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")
print("1. mcrfpy.step() advances simulation synchronously")
print("2. The scene renders during step() calls")
print("3. The RenderTexture 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
print("=== Setting Up Test Scene ===")
# Create scene with visible content
timer_test_scene = mcrfpy.Scene("timer_test_scene")
timer_test_scene.activate()
ui = timer_test_scene.children
# Add a bright red frame that should be visible
frame = mcrfpy.Frame(pos=(100, 100), size=(400, 300),
fill_color=mcrfpy.Color(255, 0, 0), # Bright red
outline_color=mcrfpy.Color(255, 255, 255), # White outline
outline=5.0)
ui.append(frame)
# Add text
caption = mcrfpy.Caption(pos=(150, 150),
text="TIMER 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
def frame_clicked(x, y, button):
print(f"Frame clicked at ({x}, {y}) with button {button}")
frame.on_click = frame_clicked
print("Scene setup complete. Setting timer for automation tests...")
# 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)
print("Timer set. Game loop will start after this script completes.")
print("Automation tests will run 1 second later when content is visible.")
# Script ends here - game loop starts next
print("PASS")
sys.exit(0)

View file

@ -1,13 +1,16 @@
#!/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"""
# Initialize scene
test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
all_pass = True
@ -176,7 +179,3 @@ def test_color_helpers(timer, runtime):
print(f"\n{'PASS' if all_pass else 'FAIL'}")
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
"""
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,39 +1,16 @@
#!/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("Creating test scene...")
test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01) # Initialize
print("Testing UIFrame clipping functionality...")
@ -90,15 +67,16 @@ def test_clipping(timer, runtime):
# 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",
"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
from mcrfpy import automation
automation.screenshot("frame_clipping_test.png")
print(f"Parent1 clip_children: {parent1.clip_children}")
@ -109,27 +87,32 @@ def test_clipping(timer, runtime):
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}")
# Start animation after a short delay
global animate_timer
animate_timer = mcrfpy.Timer("animate", animate_frames, 100, once=True)
# Animate frames (move children)
parent1.children[1].x = 50
parent2.children[1].x = 50
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}")
# Step to render animation
mcrfpy.step(0.1)
# 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...")
# 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,13 +1,16 @@
#!/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("Creating advanced test scene...")
test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
print("Testing advanced UIFrame clipping with nested frames...")
@ -61,9 +64,14 @@ def test_nested_clipping(timer, runtime):
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
def resize_test(timer, runtime):
timer.stop()
print("Resizing frames to test RenderTexture recreation...")
outer.w = 450
outer.h = 350
@ -72,34 +80,16 @@ def test_nested_clipping(timer, runtime):
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)
# Step to render resize
mcrfpy.step(0.1)
def take_resize_screenshot(timer, runtime):
timer.stop()
from mcrfpy import automation
# 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)
# 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()
# Schedule the test
test_nested_clipping_timer = mcrfpy.Timer("test_nested_clipping", test_nested_clipping, 100, once=True)
print("Advanced test scheduled, running...")

View file

@ -1,26 +1,20 @@
#!/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()
print("Creating test scene...")
test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01) # Initialize
# Get the scene UI
ui = test.children
# Create a grid without texture (uses default 16x16 cells)
# 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)
@ -67,7 +61,9 @@ def run_test(timer, runtime):
# 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)}"
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...")
@ -118,12 +114,12 @@ def run_test(timer, runtime):
print(f"\nFinal children count: {len(grid.children)}")
# Schedule screenshot for next frame
mcrfpy.Timer("screenshot", take_screenshot, 100, once=True)
# Step to render everything
mcrfpy.step(0.1)
# Create a test scene
test = mcrfpy.Scene("test")
test.activate()
# Take screenshot
automation.screenshot("test_grid_children_result.png")
print("Screenshot saved to test_grid_children_result.png")
# Schedule test to run after game loop starts
mcrfpy.Timer("test", run_test, 50, once=True)
print("PASS - Grid.children test completed")
sys.exit(0)

View file

@ -2,16 +2,22 @@
"""
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"""
# Initialize scene
test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
print("Testing UI class instantiation without arguments...")
all_pass = True
# Test UICaption with no arguments
try:
caption = mcrfpy.Caption()
@ -23,8 +29,8 @@ def test_ui_constructors(timer, runtime):
assert caption.text == ""
except Exception as e:
print(f"FAIL: Caption() - {e}")
import traceback
traceback.print_exc()
all_pass = False
# Test UIFrame with no arguments
try:
@ -38,8 +44,8 @@ def test_ui_constructors(timer, runtime):
assert frame.h == 0.0
except Exception as e:
print(f"FAIL: Frame() - {e}")
import traceback
traceback.print_exc()
all_pass = False
# Test UIGrid with no arguments
try:
@ -53,8 +59,8 @@ def test_ui_constructors(timer, runtime):
assert grid.y == 0.0
except Exception as e:
print(f"FAIL: Grid() - {e}")
import traceback
traceback.print_exc()
all_pass = False
# Test UIEntity with no arguments
try:
@ -65,8 +71,8 @@ def test_ui_constructors(timer, runtime):
assert entity.y == 0.0
except Exception as e:
print(f"FAIL: Entity() - {e}")
import traceback
traceback.print_exc()
all_pass = False
# Test UISprite with no arguments (if it has a default constructor)
try:
@ -81,11 +87,9 @@ def test_ui_constructors(timer, runtime):
print("\nAll tests complete!")
# Exit cleanly
if all_pass:
print("PASS")
sys.exit(0)
# Create a basic scene so the game can start
test = mcrfpy.Scene("test")
# Schedule the test to run after game initialization
test_timer = mcrfpy.Timer("test", test_ui_constructors, 100, once=True)
else:
print("FAIL")
sys.exit(1)

View file

@ -1,13 +1,19 @@
#!/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()
# Initialize scene
test = mcrfpy.Scene("test")
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))
@ -26,9 +32,10 @@ def test_properties(timer, runtime):
bounds2 = frame.get_bounds()
print(f"Frame bounds after move(5,5): {bounds2}")
print(" Frame properties work!")
print("+ Frame properties work!")
except Exception as e:
print(f"✗ Frame failed: {e}")
print(f"x Frame failed: {e}")
all_pass = False
# Test Entity
try:
@ -47,11 +54,14 @@ def test_properties(timer, runtime):
entity.move(3, 3)
print(f"Entity position after move(3,3): ({entity.x}, {entity.y})")
print(" Entity properties work!")
print("+ Entity properties work!")
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)
test = mcrfpy.Scene("test")
test_properties_timer = mcrfpy.Timer("test_properties", test_properties, 100, once=True)
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,14 +17,15 @@ 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
# Create test scene
test_scene = mcrfpy.Scene("test_scene")
test_scene.activate()
mcrfpy.step(0.01)
print("\n=== Testing Python Object Cache ===")
@ -137,15 +139,6 @@ def run_tests(timer, runtime):
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")
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()
# 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
"""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,10 +1,14 @@
#!/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()
# Initialize scene
test = mcrfpy.Scene("test")
test.activate()
mcrfpy.step(0.01)
try:
# Test basic functionality
@ -21,10 +25,8 @@ def simple_test(timer, runtime):
print("Resize completed")
print("PASS - No crash!")
sys.exit(0)
except Exception as e:
print(f"ERROR: {e}")
sys.exit(0)
test = mcrfpy.Scene("test")
simple_test_timer = mcrfpy.Timer("simple_test", simple_test, 100, once=True)
print("FAIL")
sys.exit(1)