Directory structure cleanup and organization overhaul
This commit is contained in:
parent
1a143982e1
commit
98fc49a978
119 changed files with 10483 additions and 4042 deletions
136
tests/bugs/issue_12_gridpoint_instantiation_test.py
Normal file
136
tests/bugs/issue_12_gridpoint_instantiation_test.py
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #12: Forbid GridPoint/GridPointState instantiation
|
||||
|
||||
This test verifies that GridPoint and GridPointState cannot be instantiated
|
||||
directly from Python, as they should only be created internally by the C++ code.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_gridpoint_instantiation():
|
||||
"""Test that GridPoint and GridPointState cannot be instantiated"""
|
||||
print("=== Testing GridPoint/GridPointState Instantiation Prevention (Issue #12) ===\n")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: Try to instantiate GridPoint
|
||||
print("--- Test 1: GridPoint instantiation ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
point = mcrfpy.GridPoint()
|
||||
print("✗ FAIL: GridPoint() should not be allowed")
|
||||
except TypeError as e:
|
||||
print(f"✓ PASS: GridPoint instantiation correctly prevented: {e}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Unexpected error: {e}")
|
||||
|
||||
# Test 2: Try to instantiate GridPointState
|
||||
print("\n--- Test 2: GridPointState instantiation ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
state = mcrfpy.GridPointState()
|
||||
print("✗ FAIL: GridPointState() should not be allowed")
|
||||
except TypeError as e:
|
||||
print(f"✓ PASS: GridPointState instantiation correctly prevented: {e}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Unexpected error: {e}")
|
||||
|
||||
# Test 3: Verify GridPoint can still be obtained from Grid
|
||||
print("\n--- Test 3: GridPoint obtained from Grid.at() ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
grid = mcrfpy.Grid(10, 10)
|
||||
point = grid.at(5, 5)
|
||||
print(f"✓ PASS: GridPoint obtained from Grid.at(): {point}")
|
||||
print(f" Type: {type(point).__name__}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Could not get GridPoint from Grid: {e}")
|
||||
|
||||
# Test 4: Verify GridPointState can still be obtained from GridPoint
|
||||
print("\n--- Test 4: GridPointState obtained from GridPoint ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
# GridPointState is accessed through GridPoint's click handler
|
||||
# Let's check if we can access point properties that would use GridPointState
|
||||
if hasattr(point, 'walkable'):
|
||||
print(f"✓ PASS: GridPoint has expected properties")
|
||||
print(f" walkable: {point.walkable}")
|
||||
print(f" transparent: {point.transparent}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: GridPoint missing expected properties")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Error accessing GridPoint properties: {e}")
|
||||
|
||||
# Test 5: Try to call the types directly (alternative syntax)
|
||||
print("\n--- Test 5: Alternative instantiation attempts ---")
|
||||
tests_total += 1
|
||||
all_prevented = True
|
||||
|
||||
# Try various ways to instantiate
|
||||
attempts = [
|
||||
("mcrfpy.GridPoint.__new__(mcrfpy.GridPoint)",
|
||||
lambda: mcrfpy.GridPoint.__new__(mcrfpy.GridPoint)),
|
||||
("type(point)()",
|
||||
lambda: type(point)() if 'point' in locals() else None),
|
||||
]
|
||||
|
||||
for desc, func in attempts:
|
||||
try:
|
||||
if func:
|
||||
result = func()
|
||||
print(f"✗ FAIL: {desc} should not be allowed")
|
||||
all_prevented = False
|
||||
except (TypeError, AttributeError) as e:
|
||||
print(f" ✓ Correctly prevented: {desc}")
|
||||
except Exception as e:
|
||||
print(f" ? Unexpected error for {desc}: {e}")
|
||||
|
||||
if all_prevented:
|
||||
print("✓ PASS: All alternative instantiation attempts prevented")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Some instantiation attempts succeeded")
|
||||
|
||||
# Summary
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Tests passed: {tests_passed}/{tests_total}")
|
||||
|
||||
if tests_passed == tests_total:
|
||||
print("\nIssue #12 FIXED: GridPoint/GridPointState instantiation properly forbidden!")
|
||||
else:
|
||||
print("\nIssue #12: Some tests failed")
|
||||
|
||||
return tests_passed == tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
# First verify the types exist
|
||||
print("Checking that GridPoint and GridPointState types exist...")
|
||||
print(f"GridPoint type: {mcrfpy.GridPoint}")
|
||||
print(f"GridPointState type: {mcrfpy.GridPointState}")
|
||||
print()
|
||||
|
||||
success = test_gridpoint_instantiation()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
337
tests/bugs/issue_26_28_iterator_comprehensive_test.py
Normal file
337
tests/bugs/issue_26_28_iterator_comprehensive_test.py
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive test for Issues #26 & #28: Iterator implementation for collections
|
||||
|
||||
This test covers both UICollection and UIEntityCollection iterator implementations,
|
||||
testing all aspects of the Python sequence protocol.
|
||||
|
||||
Issues:
|
||||
- #26: Iterator support for UIEntityCollection
|
||||
- #28: Iterator support for UICollection
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
import gc
|
||||
|
||||
def test_sequence_protocol(collection, name, expected_types=None):
|
||||
"""Test all sequence protocol operations on a collection"""
|
||||
print(f"\n=== Testing {name} ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: len()
|
||||
tests_total += 1
|
||||
try:
|
||||
length = len(collection)
|
||||
print(f"✓ len() works: {length} items")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ len() failed: {e}")
|
||||
return tests_passed, tests_total
|
||||
|
||||
# Test 2: Basic iteration
|
||||
tests_total += 1
|
||||
try:
|
||||
items = []
|
||||
types = []
|
||||
for item in collection:
|
||||
items.append(item)
|
||||
types.append(type(item).__name__)
|
||||
print(f"✓ Iteration works: found {len(items)} items")
|
||||
print(f" Types: {types}")
|
||||
if expected_types and types != expected_types:
|
||||
print(f" WARNING: Expected types {expected_types}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ Iteration failed (Issue #26/#28): {e}")
|
||||
|
||||
# Test 3: Indexing (positive)
|
||||
tests_total += 1
|
||||
try:
|
||||
if length > 0:
|
||||
first = collection[0]
|
||||
last = collection[length-1]
|
||||
print(f"✓ Positive indexing works: [0]={type(first).__name__}, [{length-1}]={type(last).__name__}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(" Skipping indexing test - empty collection")
|
||||
except Exception as e:
|
||||
print(f"✗ Positive indexing failed: {e}")
|
||||
|
||||
# Test 4: Negative indexing
|
||||
tests_total += 1
|
||||
try:
|
||||
if length > 0:
|
||||
last = collection[-1]
|
||||
first = collection[-length]
|
||||
print(f"✓ Negative indexing works: [-1]={type(last).__name__}, [-{length}]={type(first).__name__}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(" Skipping negative indexing test - empty collection")
|
||||
except Exception as e:
|
||||
print(f"✗ Negative indexing failed: {e}")
|
||||
|
||||
# Test 5: Out of bounds indexing
|
||||
tests_total += 1
|
||||
try:
|
||||
_ = collection[length + 10]
|
||||
print(f"✗ Out of bounds indexing should raise IndexError but didn't")
|
||||
except IndexError:
|
||||
print(f"✓ Out of bounds indexing correctly raises IndexError")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ Out of bounds indexing raised wrong exception: {type(e).__name__}: {e}")
|
||||
|
||||
# Test 6: Slicing
|
||||
tests_total += 1
|
||||
try:
|
||||
if length >= 2:
|
||||
slice_result = collection[0:2]
|
||||
print(f"✓ Slicing works: [0:2] returned {len(slice_result)} items")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(" Skipping slicing test - not enough items")
|
||||
except NotImplementedError:
|
||||
print(f"✗ Slicing not implemented")
|
||||
except Exception as e:
|
||||
print(f"✗ Slicing failed: {e}")
|
||||
|
||||
# Test 7: Contains operator
|
||||
tests_total += 1
|
||||
try:
|
||||
if length > 0:
|
||||
first_item = collection[0]
|
||||
if first_item in collection:
|
||||
print(f"✓ 'in' operator works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ 'in' operator returned False for existing item")
|
||||
else:
|
||||
print(" Skipping 'in' operator test - empty collection")
|
||||
except NotImplementedError:
|
||||
print(f"✗ 'in' operator not implemented")
|
||||
except Exception as e:
|
||||
print(f"✗ 'in' operator failed: {e}")
|
||||
|
||||
# Test 8: Multiple iterations
|
||||
tests_total += 1
|
||||
try:
|
||||
count1 = sum(1 for _ in collection)
|
||||
count2 = sum(1 for _ in collection)
|
||||
if count1 == count2 == length:
|
||||
print(f"✓ Multiple iterations work correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ Multiple iterations inconsistent: {count1} vs {count2} vs {length}")
|
||||
except Exception as e:
|
||||
print(f"✗ Multiple iterations failed: {e}")
|
||||
|
||||
# Test 9: Iterator state independence
|
||||
tests_total += 1
|
||||
try:
|
||||
iter1 = iter(collection)
|
||||
iter2 = iter(collection)
|
||||
|
||||
# Advance iter1
|
||||
next(iter1)
|
||||
|
||||
# iter2 should still be at the beginning
|
||||
item1_from_iter2 = next(iter2)
|
||||
item1_from_collection = collection[0]
|
||||
|
||||
if type(item1_from_iter2).__name__ == type(item1_from_collection).__name__:
|
||||
print(f"✓ Iterator state independence maintained")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ Iterator states are not independent")
|
||||
except Exception as e:
|
||||
print(f"✗ Iterator state test failed: {e}")
|
||||
|
||||
# Test 10: List conversion
|
||||
tests_total += 1
|
||||
try:
|
||||
as_list = list(collection)
|
||||
if len(as_list) == length:
|
||||
print(f"✓ list() conversion works: {len(as_list)} items")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ list() conversion wrong length: {len(as_list)} vs {length}")
|
||||
except Exception as e:
|
||||
print(f"✗ list() conversion failed: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_modification_during_iteration(collection, name):
|
||||
"""Test collection modification during iteration"""
|
||||
print(f"\n=== Testing {name} Modification During Iteration ===")
|
||||
|
||||
# This is a tricky case - some implementations might crash
|
||||
# or behave unexpectedly when the collection is modified during iteration
|
||||
|
||||
if len(collection) < 2:
|
||||
print(" Skipping - need at least 2 items")
|
||||
return
|
||||
|
||||
try:
|
||||
count = 0
|
||||
for i, item in enumerate(collection):
|
||||
count += 1
|
||||
if i == 0 and hasattr(collection, 'remove'):
|
||||
# Try to remove an item during iteration
|
||||
# This might raise an exception or cause undefined behavior
|
||||
pass # Don't actually modify to avoid breaking the test
|
||||
print(f"✓ Iteration completed without modification: {count} items")
|
||||
except Exception as e:
|
||||
print(f" Note: Iteration with modification would fail: {e}")
|
||||
|
||||
def run_comprehensive_test():
|
||||
"""Run comprehensive iterator tests for both collection types"""
|
||||
print("=== Testing Collection Iterator Implementation (Issues #26 & #28) ===")
|
||||
|
||||
total_passed = 0
|
||||
total_tests = 0
|
||||
|
||||
# Test UICollection
|
||||
print("\n--- Testing UICollection ---")
|
||||
|
||||
# Create UI elements
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
|
||||
# Add various UI elements
|
||||
frame = mcrfpy.Frame(10, 10, 200, 150,
|
||||
fill_color=mcrfpy.Color(100, 100, 200),
|
||||
outline_color=mcrfpy.Color(255, 255, 255))
|
||||
caption = mcrfpy.Caption(mcrfpy.Vector(220, 10),
|
||||
text="Test Caption",
|
||||
fill_color=mcrfpy.Color(255, 255, 0))
|
||||
|
||||
scene_ui.append(frame)
|
||||
scene_ui.append(caption)
|
||||
|
||||
# Test UICollection
|
||||
passed, total = test_sequence_protocol(scene_ui, "UICollection",
|
||||
expected_types=["Frame", "Caption"])
|
||||
total_passed += passed
|
||||
total_tests += total
|
||||
|
||||
test_modification_during_iteration(scene_ui, "UICollection")
|
||||
|
||||
# Test UICollection with children
|
||||
print("\n--- Testing UICollection Children (Nested) ---")
|
||||
child_caption = mcrfpy.Caption(mcrfpy.Vector(10, 10),
|
||||
text="Child",
|
||||
fill_color=mcrfpy.Color(200, 200, 200))
|
||||
frame.children.append(child_caption)
|
||||
|
||||
passed, total = test_sequence_protocol(frame.children, "Frame.children",
|
||||
expected_types=["Caption"])
|
||||
total_passed += passed
|
||||
total_tests += total
|
||||
|
||||
# Test UIEntityCollection
|
||||
print("\n--- Testing UIEntityCollection ---")
|
||||
|
||||
# Create a grid with entities
|
||||
grid = mcrfpy.Grid(30, 30)
|
||||
grid.x = 10
|
||||
grid.y = 200
|
||||
grid.w = 600
|
||||
grid.h = 400
|
||||
scene_ui.append(grid)
|
||||
|
||||
# Add various entities
|
||||
entity1 = mcrfpy.Entity(5, 5)
|
||||
entity2 = mcrfpy.Entity(10, 10)
|
||||
entity3 = mcrfpy.Entity(15, 15)
|
||||
|
||||
grid.entities.append(entity1)
|
||||
grid.entities.append(entity2)
|
||||
grid.entities.append(entity3)
|
||||
|
||||
passed, total = test_sequence_protocol(grid.entities, "UIEntityCollection",
|
||||
expected_types=["Entity", "Entity", "Entity"])
|
||||
total_passed += passed
|
||||
total_tests += total
|
||||
|
||||
test_modification_during_iteration(grid.entities, "UIEntityCollection")
|
||||
|
||||
# Test empty collections
|
||||
print("\n--- Testing Empty Collections ---")
|
||||
empty_grid = mcrfpy.Grid(10, 10)
|
||||
|
||||
passed, total = test_sequence_protocol(empty_grid.entities, "Empty UIEntityCollection")
|
||||
total_passed += passed
|
||||
total_tests += total
|
||||
|
||||
empty_frame = mcrfpy.Frame(0, 0, 50, 50)
|
||||
passed, total = test_sequence_protocol(empty_frame.children, "Empty UICollection")
|
||||
total_passed += passed
|
||||
total_tests += total
|
||||
|
||||
# Test large collection
|
||||
print("\n--- Testing Large Collection ---")
|
||||
large_grid = mcrfpy.Grid(50, 50)
|
||||
for i in range(100):
|
||||
large_grid.entities.append(mcrfpy.Entity(i % 50, i // 50))
|
||||
|
||||
print(f"Created large collection with {len(large_grid.entities)} entities")
|
||||
|
||||
# Just test basic iteration performance
|
||||
import time
|
||||
start = time.time()
|
||||
count = sum(1 for _ in large_grid.entities)
|
||||
elapsed = time.time() - start
|
||||
print(f"✓ Large collection iteration: {count} items in {elapsed:.3f}s")
|
||||
|
||||
# Edge case: Single item collection
|
||||
print("\n--- Testing Single Item Collection ---")
|
||||
single_grid = mcrfpy.Grid(5, 5)
|
||||
single_grid.entities.append(mcrfpy.Entity(1, 1))
|
||||
|
||||
passed, total = test_sequence_protocol(single_grid.entities, "Single Item UIEntityCollection")
|
||||
total_passed += passed
|
||||
total_tests += total
|
||||
|
||||
# Take screenshot
|
||||
automation.screenshot("/tmp/issue_26_28_iterator_test.png")
|
||||
|
||||
# Summary
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Total tests passed: {total_passed}/{total_tests}")
|
||||
|
||||
if total_passed < total_tests:
|
||||
print("\nIssues found:")
|
||||
print("- Issue #26: UIEntityCollection may not fully implement iterator protocol")
|
||||
print("- Issue #28: UICollection may not fully implement iterator protocol")
|
||||
print("\nThe iterator implementation should support:")
|
||||
print("1. Forward iteration with 'for item in collection'")
|
||||
print("2. Multiple independent iterators")
|
||||
print("3. Proper cleanup when iteration completes")
|
||||
print("4. Integration with Python's sequence protocol")
|
||||
else:
|
||||
print("\nAll iterator tests passed!")
|
||||
|
||||
return total_passed == total_tests
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
success = run_comprehensive_test()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
21
tests/bugs/issue_37_simple_test.py
Normal file
21
tests/bugs/issue_37_simple_test.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple test for Issue #37: Verify script loading works from executable directory
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import mcrfpy
|
||||
|
||||
# This script runs as --exec, which means it's loaded after Python initialization
|
||||
# and after game.py. If we got here, script loading is working.
|
||||
|
||||
print("Issue #37 test: Script execution verified")
|
||||
print(f"Current working directory: {os.getcwd()}")
|
||||
print(f"Script location: {__file__}")
|
||||
|
||||
# Create a simple scene to verify everything is working
|
||||
mcrfpy.createScene("issue37_test")
|
||||
|
||||
print("PASS: Issue #37 - Script loading working correctly")
|
||||
sys.exit(0)
|
||||
84
tests/bugs/issue_37_test.py
Normal file
84
tests/bugs/issue_37_test.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #37: Windows scripts subdirectory not checked for .py files
|
||||
|
||||
This test checks if the game can find and load scripts/game.py from different working directories.
|
||||
On Windows, this often fails because fopen uses relative paths without resolving them.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
def test_script_loading():
|
||||
# Create a temporary directory to test from
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
print(f"Testing from directory: {tmpdir}")
|
||||
|
||||
# Get the build directory (assuming we're running from the repo root)
|
||||
build_dir = os.path.abspath("build")
|
||||
mcrogueface_exe = os.path.join(build_dir, "mcrogueface")
|
||||
if os.name == "nt": # Windows
|
||||
mcrogueface_exe += ".exe"
|
||||
|
||||
# Create a simple test script that the game should load
|
||||
test_script = """
|
||||
import mcrfpy
|
||||
print("TEST SCRIPT LOADED SUCCESSFULLY")
|
||||
mcrfpy.createScene("test_scene")
|
||||
"""
|
||||
|
||||
# Save the original game.py
|
||||
game_py_path = os.path.join(build_dir, "scripts", "game.py")
|
||||
game_py_backup = game_py_path + ".backup"
|
||||
if os.path.exists(game_py_path):
|
||||
shutil.copy(game_py_path, game_py_backup)
|
||||
|
||||
try:
|
||||
# Replace game.py with our test script
|
||||
os.makedirs(os.path.dirname(game_py_path), exist_ok=True)
|
||||
with open(game_py_path, "w") as f:
|
||||
f.write(test_script)
|
||||
|
||||
# Test 1: Run from build directory (should work)
|
||||
print("\nTest 1: Running from build directory...")
|
||||
result = subprocess.run(
|
||||
[mcrogueface_exe, "--headless", "-c", "print('Test 1 complete')"],
|
||||
cwd=build_dir,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
if "TEST SCRIPT LOADED SUCCESSFULLY" in result.stdout:
|
||||
print("✓ Test 1 PASSED: Script loaded from build directory")
|
||||
else:
|
||||
print("✗ Test 1 FAILED: Script not loaded from build directory")
|
||||
print(f"stdout: {result.stdout}")
|
||||
print(f"stderr: {result.stderr}")
|
||||
|
||||
# Test 2: Run from temporary directory (often fails on Windows)
|
||||
print("\nTest 2: Running from different working directory...")
|
||||
result = subprocess.run(
|
||||
[mcrogueface_exe, "--headless", "-c", "print('Test 2 complete')"],
|
||||
cwd=tmpdir,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
if "TEST SCRIPT LOADED SUCCESSFULLY" in result.stdout:
|
||||
print("✓ Test 2 PASSED: Script loaded from different directory")
|
||||
else:
|
||||
print("✗ Test 2 FAILED: Script not loaded from different directory")
|
||||
print(f"stdout: {result.stdout}")
|
||||
print(f"stderr: {result.stderr}")
|
||||
print("\nThis is the bug described in Issue #37!")
|
||||
|
||||
finally:
|
||||
# Restore original game.py
|
||||
if os.path.exists(game_py_backup):
|
||||
shutil.move(game_py_backup, game_py_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_script_loading()
|
||||
152
tests/bugs/issue_37_windows_scripts_comprehensive_test.py
Normal file
152
tests/bugs/issue_37_windows_scripts_comprehensive_test.py
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive test for Issue #37: Windows scripts subdirectory bug
|
||||
|
||||
This test comprehensively tests script loading from different working directories,
|
||||
particularly focusing on the Windows issue where relative paths fail.
|
||||
|
||||
The bug: On Windows, when mcrogueface.exe is run from a different directory,
|
||||
it fails to find scripts/game.py because fopen uses relative paths.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
import platform
|
||||
|
||||
def create_test_script(content=""):
|
||||
"""Create a minimal test script"""
|
||||
if not content:
|
||||
content = """
|
||||
import mcrfpy
|
||||
print("TEST_SCRIPT_LOADED_FROM_PATH")
|
||||
mcrfpy.createScene("test_scene")
|
||||
# Exit cleanly to avoid hanging
|
||||
import sys
|
||||
sys.exit(0)
|
||||
"""
|
||||
return content
|
||||
|
||||
def run_mcrogueface(exe_path, cwd, timeout=5):
|
||||
"""Run mcrogueface from a specific directory and capture output"""
|
||||
cmd = [exe_path, "--headless"]
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
cwd=cwd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout
|
||||
)
|
||||
return result.stdout, result.stderr, result.returncode
|
||||
except subprocess.TimeoutExpired:
|
||||
return "", "TIMEOUT", -1
|
||||
except Exception as e:
|
||||
return "", str(e), -1
|
||||
|
||||
def test_script_loading():
|
||||
"""Test script loading from various directories"""
|
||||
# Detect platform
|
||||
is_windows = platform.system() == "Windows"
|
||||
print(f"Platform: {platform.system()}")
|
||||
|
||||
# Get paths
|
||||
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
build_dir = os.path.join(repo_root, "build")
|
||||
exe_name = "mcrogueface.exe" if is_windows else "mcrogueface"
|
||||
exe_path = os.path.join(build_dir, exe_name)
|
||||
|
||||
if not os.path.exists(exe_path):
|
||||
print(f"FAIL: Executable not found at {exe_path}")
|
||||
print("Please build the project first")
|
||||
return
|
||||
|
||||
# Backup original game.py
|
||||
scripts_dir = os.path.join(build_dir, "scripts")
|
||||
game_py_path = os.path.join(scripts_dir, "game.py")
|
||||
game_py_backup = game_py_path + ".backup"
|
||||
|
||||
if os.path.exists(game_py_path):
|
||||
shutil.copy(game_py_path, game_py_backup)
|
||||
|
||||
try:
|
||||
# Create test script
|
||||
os.makedirs(scripts_dir, exist_ok=True)
|
||||
with open(game_py_path, "w") as f:
|
||||
f.write(create_test_script())
|
||||
|
||||
print("\n=== Test 1: Run from build directory (baseline) ===")
|
||||
stdout, stderr, code = run_mcrogueface(exe_path, build_dir)
|
||||
if "TEST_SCRIPT_LOADED_FROM_PATH" in stdout:
|
||||
print("✓ PASS: Script loaded when running from build directory")
|
||||
else:
|
||||
print("✗ FAIL: Script not loaded from build directory")
|
||||
print(f" stdout: {stdout[:200]}")
|
||||
print(f" stderr: {stderr[:200]}")
|
||||
|
||||
print("\n=== Test 2: Run from parent directory ===")
|
||||
stdout, stderr, code = run_mcrogueface(exe_path, repo_root)
|
||||
if "TEST_SCRIPT_LOADED_FROM_PATH" in stdout:
|
||||
print("✓ PASS: Script loaded from parent directory")
|
||||
else:
|
||||
print("✗ FAIL: Script not loaded from parent directory")
|
||||
print(" This might indicate Issue #37")
|
||||
print(f" stdout: {stdout[:200]}")
|
||||
print(f" stderr: {stderr[:200]}")
|
||||
|
||||
print("\n=== Test 3: Run from system temp directory ===")
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
stdout, stderr, code = run_mcrogueface(exe_path, tmpdir)
|
||||
if "TEST_SCRIPT_LOADED_FROM_PATH" in stdout:
|
||||
print("✓ PASS: Script loaded from temp directory")
|
||||
else:
|
||||
print("✗ FAIL: Script not loaded from temp directory")
|
||||
print(" This is the core Issue #37 bug!")
|
||||
print(f" Working directory: {tmpdir}")
|
||||
print(f" stdout: {stdout[:200]}")
|
||||
print(f" stderr: {stderr[:200]}")
|
||||
|
||||
print("\n=== Test 4: Run with absolute path from different directory ===")
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Use absolute path to executable
|
||||
abs_exe = os.path.abspath(exe_path)
|
||||
stdout, stderr, code = run_mcrogueface(abs_exe, tmpdir)
|
||||
if "TEST_SCRIPT_LOADED_FROM_PATH" in stdout:
|
||||
print("✓ PASS: Script loaded with absolute exe path")
|
||||
else:
|
||||
print("✗ FAIL: Script not loaded with absolute exe path")
|
||||
print(f" stdout: {stdout[:200]}")
|
||||
print(f" stderr: {stderr[:200]}")
|
||||
|
||||
# Test 5: Symlink test (Unix only)
|
||||
if not is_windows:
|
||||
print("\n=== Test 5: Run via symlink (Unix only) ===")
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
symlink_path = os.path.join(tmpdir, "mcrogueface_link")
|
||||
os.symlink(exe_path, symlink_path)
|
||||
stdout, stderr, code = run_mcrogueface(symlink_path, tmpdir)
|
||||
if "TEST_SCRIPT_LOADED_FROM_PATH" in stdout:
|
||||
print("✓ PASS: Script loaded via symlink")
|
||||
else:
|
||||
print("✗ FAIL: Script not loaded via symlink")
|
||||
print(f" stdout: {stdout[:200]}")
|
||||
print(f" stderr: {stderr[:200]}")
|
||||
|
||||
# Summary
|
||||
print("\n=== SUMMARY ===")
|
||||
print("Issue #37 is about script loading failing when the executable")
|
||||
print("is run from a different working directory than where it's located.")
|
||||
print("The fix should resolve the script path relative to the executable,")
|
||||
print("not the current working directory.")
|
||||
|
||||
finally:
|
||||
# Restore original game.py
|
||||
if os.path.exists(game_py_backup):
|
||||
shutil.move(game_py_backup, game_py_path)
|
||||
print("\nTest cleanup complete")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_script_loading()
|
||||
88
tests/bugs/issue_76_test.py
Normal file
88
tests/bugs/issue_76_test.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #76: UIEntityCollection::getitem returns wrong type for derived classes
|
||||
|
||||
This test checks if derived Entity classes maintain their type when retrieved from collections.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
# Create a derived Entity class
|
||||
class CustomEntity(mcrfpy.Entity):
|
||||
def __init__(self, x, y):
|
||||
super().__init__(x, y)
|
||||
self.custom_attribute = "I am custom!"
|
||||
|
||||
def custom_method(self):
|
||||
return "Custom method called"
|
||||
|
||||
def run_test(runtime):
|
||||
"""Test that derived entity classes maintain their type in collections"""
|
||||
try:
|
||||
# Create a grid
|
||||
grid = mcrfpy.Grid(10, 10)
|
||||
|
||||
# Create instances of base and derived entities
|
||||
base_entity = mcrfpy.Entity(1, 1)
|
||||
custom_entity = CustomEntity(2, 2)
|
||||
|
||||
# Add them to the grid's entity collection
|
||||
grid.entities.append(base_entity)
|
||||
grid.entities.append(custom_entity)
|
||||
|
||||
# Retrieve them back
|
||||
retrieved_base = grid.entities[0]
|
||||
retrieved_custom = grid.entities[1]
|
||||
|
||||
print(f"Base entity type: {type(retrieved_base)}")
|
||||
print(f"Custom entity type: {type(retrieved_custom)}")
|
||||
|
||||
# Test 1: Check if base entity is correct type
|
||||
if type(retrieved_base).__name__ == "Entity":
|
||||
print("✓ Test 1 PASSED: Base entity maintains correct type")
|
||||
else:
|
||||
print("✗ Test 1 FAILED: Base entity has wrong type")
|
||||
|
||||
# Test 2: Check if custom entity maintains its derived type
|
||||
if type(retrieved_custom).__name__ == "CustomEntity":
|
||||
print("✓ Test 2 PASSED: Derived entity maintains correct type")
|
||||
|
||||
# Test 3: Check if custom attributes are preserved
|
||||
try:
|
||||
attr = retrieved_custom.custom_attribute
|
||||
method_result = retrieved_custom.custom_method()
|
||||
print(f"✓ Test 3 PASSED: Custom attributes preserved - {attr}, {method_result}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ Test 3 FAILED: Custom attributes lost - {e}")
|
||||
else:
|
||||
print("✗ Test 2 FAILED: Derived entity type lost!")
|
||||
print("This is the bug described in Issue #76!")
|
||||
|
||||
# Try to access custom attributes anyway
|
||||
try:
|
||||
attr = retrieved_custom.custom_attribute
|
||||
print(f" - Has custom_attribute: {attr} (but wrong type)")
|
||||
except AttributeError:
|
||||
print(" - Lost custom_attribute")
|
||||
|
||||
# Test 4: Check iteration
|
||||
print("\nTesting iteration:")
|
||||
for i, entity in enumerate(grid.entities):
|
||||
print(f" Entity {i}: {type(entity).__name__}")
|
||||
|
||||
print("\nTest complete")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Test error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
259
tests/bugs/issue_76_uientitycollection_type_test.py
Normal file
259
tests/bugs/issue_76_uientitycollection_type_test.py
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive test for Issue #76: UIEntityCollection returns wrong type for derived classes
|
||||
|
||||
This test demonstrates that when retrieving entities from a UIEntityCollection,
|
||||
derived Entity classes lose their type and are returned as base Entity objects.
|
||||
|
||||
The bug: The C++ implementation of UIEntityCollection::getitem creates a new
|
||||
PyUIEntityObject with type "Entity" instead of preserving the original Python type.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
import gc
|
||||
|
||||
# Define several derived Entity classes with different features
|
||||
class Player(mcrfpy.Entity):
|
||||
def __init__(self, x, y):
|
||||
# Entity expects Vector position and optional texture
|
||||
super().__init__(mcrfpy.Vector(x, y))
|
||||
self.health = 100
|
||||
self.inventory = []
|
||||
self.player_id = "PLAYER_001"
|
||||
|
||||
def take_damage(self, amount):
|
||||
self.health -= amount
|
||||
return self.health > 0
|
||||
|
||||
class Enemy(mcrfpy.Entity):
|
||||
def __init__(self, x, y, enemy_type="goblin"):
|
||||
# Entity expects Vector position and optional texture
|
||||
super().__init__(mcrfpy.Vector(x, y))
|
||||
self.enemy_type = enemy_type
|
||||
self.aggression = 5
|
||||
self.patrol_route = [(x, y), (x+1, y), (x+1, y+1), (x, y+1)]
|
||||
|
||||
def get_next_move(self):
|
||||
return self.patrol_route[0]
|
||||
|
||||
class Treasure(mcrfpy.Entity):
|
||||
def __init__(self, x, y, value=100):
|
||||
# Entity expects Vector position and optional texture
|
||||
super().__init__(mcrfpy.Vector(x, y))
|
||||
self.value = value
|
||||
self.collected = False
|
||||
|
||||
def collect(self):
|
||||
if not self.collected:
|
||||
self.collected = True
|
||||
return self.value
|
||||
return 0
|
||||
|
||||
def test_type_preservation():
|
||||
"""Comprehensive test of type preservation in UIEntityCollection"""
|
||||
print("=== Testing UIEntityCollection Type Preservation (Issue #76) ===\n")
|
||||
|
||||
# Create a grid to hold entities
|
||||
grid = mcrfpy.Grid(30, 30)
|
||||
grid.x = 10
|
||||
grid.y = 10
|
||||
grid.w = 600
|
||||
grid.h = 600
|
||||
|
||||
# Add grid to scene
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
scene_ui.append(grid)
|
||||
|
||||
# Create various entity instances
|
||||
player = Player(5, 5)
|
||||
enemy1 = Enemy(10, 10, "orc")
|
||||
enemy2 = Enemy(15, 15, "skeleton")
|
||||
treasure = Treasure(20, 20, 500)
|
||||
base_entity = mcrfpy.Entity(mcrfpy.Vector(25, 25))
|
||||
|
||||
print("Created entities:")
|
||||
print(f" - Player at (5,5): type={type(player).__name__}, health={player.health}")
|
||||
print(f" - Enemy at (10,10): type={type(enemy1).__name__}, enemy_type={enemy1.enemy_type}")
|
||||
print(f" - Enemy at (15,15): type={type(enemy2).__name__}, enemy_type={enemy2.enemy_type}")
|
||||
print(f" - Treasure at (20,20): type={type(treasure).__name__}, value={treasure.value}")
|
||||
print(f" - Base Entity at (25,25): type={type(base_entity).__name__}")
|
||||
|
||||
# Store original references
|
||||
original_refs = {
|
||||
'player': player,
|
||||
'enemy1': enemy1,
|
||||
'enemy2': enemy2,
|
||||
'treasure': treasure,
|
||||
'base_entity': base_entity
|
||||
}
|
||||
|
||||
# Add entities to grid
|
||||
grid.entities.append(player)
|
||||
grid.entities.append(enemy1)
|
||||
grid.entities.append(enemy2)
|
||||
grid.entities.append(treasure)
|
||||
grid.entities.append(base_entity)
|
||||
|
||||
print(f"\nAdded {len(grid.entities)} entities to grid")
|
||||
|
||||
# Test 1: Direct indexing
|
||||
print("\n--- Test 1: Direct Indexing ---")
|
||||
retrieved_entities = []
|
||||
for i in range(len(grid.entities)):
|
||||
entity = grid.entities[i]
|
||||
retrieved_entities.append(entity)
|
||||
print(f"grid.entities[{i}]: type={type(entity).__name__}, id={id(entity)}")
|
||||
|
||||
# Test 2: Check type preservation
|
||||
print("\n--- Test 2: Type Preservation Check ---")
|
||||
r_player = grid.entities[0]
|
||||
r_enemy1 = grid.entities[1]
|
||||
r_treasure = grid.entities[3]
|
||||
|
||||
# Check types
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
tests_total += 1
|
||||
if type(r_player).__name__ == "Player":
|
||||
print("✓ PASS: Player type preserved")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Player type lost! Got {type(r_player).__name__} instead of Player")
|
||||
print(" This is the core Issue #76 bug!")
|
||||
|
||||
tests_total += 1
|
||||
if type(r_enemy1).__name__ == "Enemy":
|
||||
print("✓ PASS: Enemy type preserved")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Enemy type lost! Got {type(r_enemy1).__name__} instead of Enemy")
|
||||
|
||||
tests_total += 1
|
||||
if type(r_treasure).__name__ == "Treasure":
|
||||
print("✓ PASS: Treasure type preserved")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Treasure type lost! Got {type(r_treasure).__name__} instead of Treasure")
|
||||
|
||||
# Test 3: Check attribute preservation
|
||||
print("\n--- Test 3: Attribute Preservation ---")
|
||||
|
||||
# Test Player attributes
|
||||
try:
|
||||
tests_total += 1
|
||||
health = r_player.health
|
||||
inv = r_player.inventory
|
||||
pid = r_player.player_id
|
||||
print(f"✓ PASS: Player attributes accessible: health={health}, inventory={inv}, id={pid}")
|
||||
tests_passed += 1
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: Player attributes lost: {e}")
|
||||
|
||||
# Test Enemy attributes
|
||||
try:
|
||||
tests_total += 1
|
||||
etype = r_enemy1.enemy_type
|
||||
aggr = r_enemy1.aggression
|
||||
print(f"✓ PASS: Enemy attributes accessible: type={etype}, aggression={aggr}")
|
||||
tests_passed += 1
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: Enemy attributes lost: {e}")
|
||||
|
||||
# Test 4: Method preservation
|
||||
print("\n--- Test 4: Method Preservation ---")
|
||||
|
||||
try:
|
||||
tests_total += 1
|
||||
r_player.take_damage(10)
|
||||
print(f"✓ PASS: Player method callable, health now: {r_player.health}")
|
||||
tests_passed += 1
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: Player methods lost: {e}")
|
||||
|
||||
try:
|
||||
tests_total += 1
|
||||
next_move = r_enemy1.get_next_move()
|
||||
print(f"✓ PASS: Enemy method callable, next move: {next_move}")
|
||||
tests_passed += 1
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: Enemy methods lost: {e}")
|
||||
|
||||
# Test 5: Iteration
|
||||
print("\n--- Test 5: Iteration Test ---")
|
||||
try:
|
||||
tests_total += 1
|
||||
type_list = []
|
||||
for entity in grid.entities:
|
||||
type_list.append(type(entity).__name__)
|
||||
print(f"Types during iteration: {type_list}")
|
||||
if type_list == ["Player", "Enemy", "Enemy", "Treasure", "Entity"]:
|
||||
print("✓ PASS: All types preserved during iteration")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Types lost during iteration")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Iteration error: {e}")
|
||||
|
||||
# Test 6: Identity check
|
||||
print("\n--- Test 6: Object Identity ---")
|
||||
tests_total += 1
|
||||
if r_player is original_refs['player']:
|
||||
print("✓ PASS: Retrieved object is the same Python object")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Retrieved object is a different instance")
|
||||
print(f" Original id: {id(original_refs['player'])}")
|
||||
print(f" Retrieved id: {id(r_player)}")
|
||||
|
||||
# Test 7: Modification persistence
|
||||
print("\n--- Test 7: Modification Persistence ---")
|
||||
tests_total += 1
|
||||
r_player.x = 50
|
||||
r_player.y = 50
|
||||
|
||||
# Retrieve again
|
||||
r_player2 = grid.entities[0]
|
||||
if r_player2.x == 50 and r_player2.y == 50:
|
||||
print("✓ PASS: Modifications persist across retrievals")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Modifications lost: position is ({r_player2.x}, {r_player2.y})")
|
||||
|
||||
# Take screenshot
|
||||
automation.screenshot("/tmp/issue_76_test.png")
|
||||
|
||||
# Summary
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Tests passed: {tests_passed}/{tests_total}")
|
||||
|
||||
if tests_passed < tests_total:
|
||||
print("\nIssue #76: The C++ implementation creates new PyUIEntityObject instances")
|
||||
print("with type 'Entity' instead of preserving the original Python type.")
|
||||
print("This causes derived classes to lose their type, attributes, and methods.")
|
||||
print("\nThe fix requires storing and restoring the original Python type")
|
||||
print("when creating objects in UIEntityCollection::getitem.")
|
||||
|
||||
return tests_passed == tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
success = test_type_preservation()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
170
tests/bugs/issue_79_color_properties_test.py
Normal file
170
tests/bugs/issue_79_color_properties_test.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #79: Color r, g, b, a properties return None
|
||||
|
||||
This test verifies that Color object properties (r, g, b, a) work correctly.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_color_properties():
|
||||
"""Test Color r, g, b, a property access and modification"""
|
||||
print("=== Testing Color r, g, b, a Properties (Issue #79) ===\n")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: Create color and check properties
|
||||
print("--- Test 1: Basic property access ---")
|
||||
color1 = mcrfpy.Color(255, 128, 64, 32)
|
||||
|
||||
tests_total += 1
|
||||
if color1.r == 255:
|
||||
print("✓ PASS: color.r returns correct value (255)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.r returned {color1.r} instead of 255")
|
||||
|
||||
tests_total += 1
|
||||
if color1.g == 128:
|
||||
print("✓ PASS: color.g returns correct value (128)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.g returned {color1.g} instead of 128")
|
||||
|
||||
tests_total += 1
|
||||
if color1.b == 64:
|
||||
print("✓ PASS: color.b returns correct value (64)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.b returned {color1.b} instead of 64")
|
||||
|
||||
tests_total += 1
|
||||
if color1.a == 32:
|
||||
print("✓ PASS: color.a returns correct value (32)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.a returned {color1.a} instead of 32")
|
||||
|
||||
# Test 2: Modify properties
|
||||
print("\n--- Test 2: Property modification ---")
|
||||
color1.r = 200
|
||||
color1.g = 100
|
||||
color1.b = 50
|
||||
color1.a = 25
|
||||
|
||||
tests_total += 1
|
||||
if color1.r == 200:
|
||||
print("✓ PASS: color.r set successfully")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.r is {color1.r} after setting to 200")
|
||||
|
||||
tests_total += 1
|
||||
if color1.g == 100:
|
||||
print("✓ PASS: color.g set successfully")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.g is {color1.g} after setting to 100")
|
||||
|
||||
tests_total += 1
|
||||
if color1.b == 50:
|
||||
print("✓ PASS: color.b set successfully")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.b is {color1.b} after setting to 50")
|
||||
|
||||
tests_total += 1
|
||||
if color1.a == 25:
|
||||
print("✓ PASS: color.a set successfully")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: color.a is {color1.a} after setting to 25")
|
||||
|
||||
# Test 3: Boundary values
|
||||
print("\n--- Test 3: Boundary value tests ---")
|
||||
color2 = mcrfpy.Color(0, 0, 0, 0)
|
||||
|
||||
tests_total += 1
|
||||
if color2.r == 0 and color2.g == 0 and color2.b == 0 and color2.a == 0:
|
||||
print("✓ PASS: Minimum values (0) work correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Minimum values not working")
|
||||
|
||||
color3 = mcrfpy.Color(255, 255, 255, 255)
|
||||
tests_total += 1
|
||||
if color3.r == 255 and color3.g == 255 and color3.b == 255 and color3.a == 255:
|
||||
print("✓ PASS: Maximum values (255) work correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Maximum values not working")
|
||||
|
||||
# Test 4: Invalid value handling
|
||||
print("\n--- Test 4: Invalid value handling ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
color3.r = 256 # Out of range
|
||||
print("✗ FAIL: Should have raised ValueError for value > 255")
|
||||
except ValueError as e:
|
||||
print(f"✓ PASS: Correctly raised ValueError: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
tests_total += 1
|
||||
try:
|
||||
color3.g = -1 # Out of range
|
||||
print("✗ FAIL: Should have raised ValueError for value < 0")
|
||||
except ValueError as e:
|
||||
print(f"✓ PASS: Correctly raised ValueError: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
tests_total += 1
|
||||
try:
|
||||
color3.b = "red" # Wrong type
|
||||
print("✗ FAIL: Should have raised TypeError for string value")
|
||||
except TypeError as e:
|
||||
print(f"✓ PASS: Correctly raised TypeError: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
# Test 5: Verify __repr__ shows correct values
|
||||
print("\n--- Test 5: String representation ---")
|
||||
color4 = mcrfpy.Color(10, 20, 30, 40)
|
||||
repr_str = repr(color4)
|
||||
tests_total += 1
|
||||
if "(10, 20, 30, 40)" in repr_str:
|
||||
print(f"✓ PASS: __repr__ shows correct values: {repr_str}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: __repr__ incorrect: {repr_str}")
|
||||
|
||||
# Summary
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Tests passed: {tests_passed}/{tests_total}")
|
||||
|
||||
if tests_passed == tests_total:
|
||||
print("\nIssue #79 FIXED: Color properties now work correctly!")
|
||||
else:
|
||||
print("\nIssue #79: Some tests failed")
|
||||
|
||||
return tests_passed == tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
success = test_color_properties()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
156
tests/bugs/issue_80_caption_font_size_test.py
Normal file
156
tests/bugs/issue_80_caption_font_size_test.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #80: Rename Caption.size to font_size
|
||||
|
||||
This test verifies that Caption now uses font_size property instead of size,
|
||||
while maintaining backward compatibility.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_caption_font_size():
|
||||
"""Test Caption font_size property"""
|
||||
print("=== Testing Caption font_size Property (Issue #80) ===\n")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Create a caption for testing
|
||||
caption = mcrfpy.Caption((100, 100), "Test Text", mcrfpy.Font("assets/JetbrainsMono.ttf"))
|
||||
|
||||
# Test 1: Check that font_size property exists and works
|
||||
print("--- Test 1: font_size property ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
# Set font size using new property name
|
||||
caption.font_size = 24
|
||||
if caption.font_size == 24:
|
||||
print("✓ PASS: font_size property works correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: font_size is {caption.font_size}, expected 24")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: font_size property not found: {e}")
|
||||
|
||||
# Test 2: Check that old 'size' property is removed
|
||||
print("\n--- Test 2: Old 'size' property removed ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
# Try to access size property - this should fail
|
||||
old_size = caption.size
|
||||
print(f"✗ FAIL: 'size' property still accessible (value: {old_size}) - should be removed")
|
||||
except AttributeError:
|
||||
print("✓ PASS: 'size' property correctly removed")
|
||||
tests_passed += 1
|
||||
|
||||
# Test 3: Verify font_size changes are reflected
|
||||
print("\n--- Test 3: font_size changes ---")
|
||||
tests_total += 1
|
||||
caption.font_size = 36
|
||||
if caption.font_size == 36:
|
||||
print("✓ PASS: font_size changes are reflected correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: font_size is {caption.font_size}, expected 36")
|
||||
|
||||
# Test 4: Check property type
|
||||
print("\n--- Test 4: Property type check ---")
|
||||
tests_total += 1
|
||||
caption.font_size = 18
|
||||
if isinstance(caption.font_size, int):
|
||||
print("✓ PASS: font_size returns integer as expected")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: font_size returns {type(caption.font_size).__name__}, expected int")
|
||||
|
||||
# Test 5: Verify in __dir__
|
||||
print("\n--- Test 5: Property introspection ---")
|
||||
tests_total += 1
|
||||
properties = dir(caption)
|
||||
if 'font_size' in properties:
|
||||
print("✓ PASS: 'font_size' appears in dir(caption)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: 'font_size' not found in dir(caption)")
|
||||
|
||||
# Check if 'size' still appears
|
||||
if 'size' in properties:
|
||||
print(" INFO: 'size' still appears in dir(caption) - backward compatibility maintained")
|
||||
else:
|
||||
print(" INFO: 'size' removed from dir(caption) - breaking change")
|
||||
|
||||
# Test 6: Edge cases
|
||||
print("\n--- Test 6: Edge cases ---")
|
||||
tests_total += 1
|
||||
all_passed = True
|
||||
|
||||
# Test setting to 0
|
||||
caption.font_size = 0
|
||||
if caption.font_size != 0:
|
||||
print(f"✗ FAIL: Setting font_size to 0 failed (got {caption.font_size})")
|
||||
all_passed = False
|
||||
|
||||
# Test setting to large value
|
||||
caption.font_size = 100
|
||||
if caption.font_size != 100:
|
||||
print(f"✗ FAIL: Setting font_size to 100 failed (got {caption.font_size})")
|
||||
all_passed = False
|
||||
|
||||
# Test float to int conversion
|
||||
caption.font_size = 24.7
|
||||
if caption.font_size != 24:
|
||||
print(f"✗ FAIL: Float to int conversion failed (got {caption.font_size})")
|
||||
all_passed = False
|
||||
|
||||
if all_passed:
|
||||
print("✓ PASS: All edge cases handled correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Some edge cases failed")
|
||||
|
||||
# Test 7: Scene UI integration
|
||||
print("\n--- Test 7: Scene UI integration ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
scene_ui.append(caption)
|
||||
|
||||
# Modify font_size after adding to scene
|
||||
caption.font_size = 32
|
||||
|
||||
print("✓ PASS: Caption with font_size works in scene UI")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Scene UI integration failed: {e}")
|
||||
|
||||
# Summary
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Tests passed: {tests_passed}/{tests_total}")
|
||||
|
||||
if tests_passed == tests_total:
|
||||
print("\nIssue #80 FIXED: Caption.size successfully renamed to font_size!")
|
||||
else:
|
||||
print("\nIssue #80: Some tests failed")
|
||||
|
||||
return tests_passed == tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
success = test_caption_font_size()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
191
tests/bugs/issue_81_sprite_index_standardization_test.py
Normal file
191
tests/bugs/issue_81_sprite_index_standardization_test.py
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #81: Standardize sprite_index property name
|
||||
|
||||
This test verifies that both UISprite and UIEntity use "sprite_index" instead of "sprite_number"
|
||||
for consistency across the API.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_sprite_index_property():
|
||||
"""Test sprite_index property on UISprite"""
|
||||
print("=== Testing UISprite sprite_index Property ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Create a texture and sprite
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
sprite = mcrfpy.Sprite(10, 10, texture, 5, 1.0)
|
||||
|
||||
# Test 1: Check sprite_index property exists
|
||||
tests_total += 1
|
||||
try:
|
||||
idx = sprite.sprite_index
|
||||
if idx == 5:
|
||||
print(f"✓ PASS: sprite.sprite_index = {idx}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite.sprite_index = {idx}, expected 5")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sprite_index not accessible: {e}")
|
||||
|
||||
# Test 2: Check sprite_index setter
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.sprite_index = 10
|
||||
if sprite.sprite_index == 10:
|
||||
print("✓ PASS: sprite_index setter works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite_index setter failed, got {sprite.sprite_index}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: sprite_index setter error: {e}")
|
||||
|
||||
# Test 3: Check sprite_number is removed/deprecated
|
||||
tests_total += 1
|
||||
if hasattr(sprite, 'sprite_number'):
|
||||
# Check if it's an alias
|
||||
sprite.sprite_number = 15
|
||||
if sprite.sprite_index == 15:
|
||||
print("✓ PASS: sprite_number exists as backward-compatible alias")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: sprite_number exists but doesn't update sprite_index")
|
||||
else:
|
||||
print("✓ PASS: sprite_number property removed (no backward compatibility)")
|
||||
tests_passed += 1
|
||||
|
||||
# Test 4: Check repr uses sprite_index
|
||||
tests_total += 1
|
||||
repr_str = repr(sprite)
|
||||
if "sprite_index=" in repr_str:
|
||||
print(f"✓ PASS: repr uses sprite_index: {repr_str}")
|
||||
tests_passed += 1
|
||||
elif "sprite_number=" in repr_str:
|
||||
print(f"✗ FAIL: repr still uses sprite_number: {repr_str}")
|
||||
else:
|
||||
print(f"✗ FAIL: repr doesn't show sprite info: {repr_str}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_entity_sprite_index_property():
|
||||
"""Test sprite_index property on Entity"""
|
||||
print("\n=== Testing Entity sprite_index Property ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Create an entity with required position
|
||||
entity = mcrfpy.Entity((0, 0))
|
||||
|
||||
# Test 1: Check sprite_index property exists
|
||||
tests_total += 1
|
||||
try:
|
||||
# Set initial value
|
||||
entity.sprite_index = 42
|
||||
idx = entity.sprite_index
|
||||
if idx == 42:
|
||||
print(f"✓ PASS: entity.sprite_index = {idx}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: entity.sprite_index = {idx}, expected 42")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sprite_index not accessible: {e}")
|
||||
|
||||
# Test 2: Check sprite_number is removed/deprecated
|
||||
tests_total += 1
|
||||
if hasattr(entity, 'sprite_number'):
|
||||
# Check if it's an alias
|
||||
entity.sprite_number = 99
|
||||
if hasattr(entity, 'sprite_index') and entity.sprite_index == 99:
|
||||
print("✓ PASS: sprite_number exists as backward-compatible alias")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: sprite_number exists but doesn't update sprite_index")
|
||||
else:
|
||||
print("✓ PASS: sprite_number property removed (no backward compatibility)")
|
||||
tests_passed += 1
|
||||
|
||||
# Test 3: Check repr uses sprite_index
|
||||
tests_total += 1
|
||||
repr_str = repr(entity)
|
||||
if "sprite_index=" in repr_str:
|
||||
print(f"✓ PASS: repr uses sprite_index: {repr_str}")
|
||||
tests_passed += 1
|
||||
elif "sprite_number=" in repr_str:
|
||||
print(f"✗ FAIL: repr still uses sprite_number: {repr_str}")
|
||||
else:
|
||||
print(f"? INFO: repr doesn't show sprite info: {repr_str}")
|
||||
# This might be okay if entity doesn't show sprite in repr
|
||||
tests_passed += 1
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_animation_compatibility():
|
||||
"""Test that animations work with sprite_index"""
|
||||
print("\n=== Testing Animation Compatibility ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test animation with sprite_index property name
|
||||
tests_total += 1
|
||||
try:
|
||||
# This tests that the animation system recognizes sprite_index
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
sprite = mcrfpy.Sprite(0, 0, texture, 0, 1.0)
|
||||
|
||||
# Try to animate sprite_index (even if we can't directly test animations here)
|
||||
sprite.sprite_index = 0
|
||||
sprite.sprite_index = 5
|
||||
sprite.sprite_index = 10
|
||||
|
||||
print("✓ PASS: sprite_index property works for potential animations")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: sprite_index animation compatibility issue: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
print("=== Testing sprite_index Property Standardization (Issue #81) ===\n")
|
||||
|
||||
sprite_passed, sprite_total = test_sprite_index_property()
|
||||
entity_passed, entity_total = test_entity_sprite_index_property()
|
||||
anim_passed, anim_total = test_animation_compatibility()
|
||||
|
||||
total_passed = sprite_passed + entity_passed + anim_passed
|
||||
total_tests = sprite_total + entity_total + anim_total
|
||||
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Sprite tests: {sprite_passed}/{sprite_total}")
|
||||
print(f"Entity tests: {entity_passed}/{entity_total}")
|
||||
print(f"Animation tests: {anim_passed}/{anim_total}")
|
||||
print(f"Total tests passed: {total_passed}/{total_tests}")
|
||||
|
||||
if total_passed == total_tests:
|
||||
print("\nIssue #81 FIXED: sprite_index property standardized!")
|
||||
print("\nOverall result: PASS")
|
||||
else:
|
||||
print("\nIssue #81: Some tests failed")
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
206
tests/bugs/issue_82_sprite_scale_xy_test.py
Normal file
206
tests/bugs/issue_82_sprite_scale_xy_test.py
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #82: Add scale_x and scale_y to UISprite
|
||||
|
||||
This test verifies that UISprite now supports non-uniform scaling through
|
||||
separate scale_x and scale_y properties, in addition to the existing uniform
|
||||
scale property.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_scale_xy_properties():
|
||||
"""Test scale_x and scale_y properties on UISprite"""
|
||||
print("=== Testing UISprite scale_x and scale_y Properties ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Create a texture and sprite
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
sprite = mcrfpy.Sprite(10, 10, texture, 0, 1.0)
|
||||
|
||||
# Test 1: Check scale_x property exists and defaults correctly
|
||||
tests_total += 1
|
||||
try:
|
||||
scale_x = sprite.scale_x
|
||||
if scale_x == 1.0:
|
||||
print(f"✓ PASS: sprite.scale_x = {scale_x} (default)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite.scale_x = {scale_x}, expected 1.0")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: scale_x not accessible: {e}")
|
||||
|
||||
# Test 2: Check scale_y property exists and defaults correctly
|
||||
tests_total += 1
|
||||
try:
|
||||
scale_y = sprite.scale_y
|
||||
if scale_y == 1.0:
|
||||
print(f"✓ PASS: sprite.scale_y = {scale_y} (default)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite.scale_y = {scale_y}, expected 1.0")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: scale_y not accessible: {e}")
|
||||
|
||||
# Test 3: Set scale_x independently
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.scale_x = 2.0
|
||||
if sprite.scale_x == 2.0 and sprite.scale_y == 1.0:
|
||||
print(f"✓ PASS: scale_x set independently (x={sprite.scale_x}, y={sprite.scale_y})")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: scale_x didn't set correctly (x={sprite.scale_x}, y={sprite.scale_y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: scale_x setter error: {e}")
|
||||
|
||||
# Test 4: Set scale_y independently
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.scale_y = 3.0
|
||||
if sprite.scale_x == 2.0 and sprite.scale_y == 3.0:
|
||||
print(f"✓ PASS: scale_y set independently (x={sprite.scale_x}, y={sprite.scale_y})")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: scale_y didn't set correctly (x={sprite.scale_x}, y={sprite.scale_y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: scale_y setter error: {e}")
|
||||
|
||||
# Test 5: Uniform scale property interaction
|
||||
tests_total += 1
|
||||
try:
|
||||
# Setting uniform scale should affect both x and y
|
||||
sprite.scale = 1.5
|
||||
if sprite.scale_x == 1.5 and sprite.scale_y == 1.5:
|
||||
print(f"✓ PASS: uniform scale sets both scale_x and scale_y")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: uniform scale didn't update scale_x/scale_y correctly")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: uniform scale interaction error: {e}")
|
||||
|
||||
# Test 6: Reading uniform scale with non-uniform values
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.scale_x = 2.0
|
||||
sprite.scale_y = 3.0
|
||||
uniform_scale = sprite.scale
|
||||
# When scales differ, scale property should return scale_x (or could be average, or error)
|
||||
print(f"? INFO: With non-uniform scaling (x=2.0, y=3.0), scale property returns: {uniform_scale}")
|
||||
# We'll accept this behavior whatever it is
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: reading scale with non-uniform values failed: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_animation_compatibility():
|
||||
"""Test that animations work with scale_x and scale_y"""
|
||||
print("\n=== Testing Animation Compatibility ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test property system compatibility
|
||||
tests_total += 1
|
||||
try:
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
sprite = mcrfpy.Sprite(0, 0, texture, 0, 1.0)
|
||||
|
||||
# Test setting various scale values
|
||||
sprite.scale_x = 0.5
|
||||
sprite.scale_y = 2.0
|
||||
sprite.scale_x = 1.5
|
||||
sprite.scale_y = 1.5
|
||||
|
||||
print("✓ PASS: scale_x and scale_y properties work for potential animations")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: scale_x/scale_y animation compatibility issue: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_edge_cases():
|
||||
"""Test edge cases for scale properties"""
|
||||
print("\n=== Testing Edge Cases ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
sprite = mcrfpy.Sprite(0, 0, texture, 0, 1.0)
|
||||
|
||||
# Test 1: Zero scale
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.scale_x = 0.0
|
||||
sprite.scale_y = 0.0
|
||||
print(f"✓ PASS: Zero scale allowed (x={sprite.scale_x}, y={sprite.scale_y})")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Zero scale not allowed: {e}")
|
||||
|
||||
# Test 2: Negative scale (flip)
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.scale_x = -1.0
|
||||
sprite.scale_y = -1.0
|
||||
print(f"✓ PASS: Negative scale allowed for flipping (x={sprite.scale_x}, y={sprite.scale_y})")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Negative scale not allowed: {e}")
|
||||
|
||||
# Test 3: Very large scale
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.scale_x = 100.0
|
||||
sprite.scale_y = 100.0
|
||||
print(f"✓ PASS: Large scale values allowed (x={sprite.scale_x}, y={sprite.scale_y})")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Large scale values not allowed: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
print("=== Testing scale_x and scale_y Properties (Issue #82) ===\n")
|
||||
|
||||
basic_passed, basic_total = test_scale_xy_properties()
|
||||
anim_passed, anim_total = test_animation_compatibility()
|
||||
edge_passed, edge_total = test_edge_cases()
|
||||
|
||||
total_passed = basic_passed + anim_passed + edge_passed
|
||||
total_tests = basic_total + anim_total + edge_total
|
||||
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Basic tests: {basic_passed}/{basic_total}")
|
||||
print(f"Animation tests: {anim_passed}/{anim_total}")
|
||||
print(f"Edge case tests: {edge_passed}/{edge_total}")
|
||||
print(f"Total tests passed: {total_passed}/{total_tests}")
|
||||
|
||||
if total_passed == total_tests:
|
||||
print("\nIssue #82 FIXED: scale_x and scale_y properties added!")
|
||||
print("\nOverall result: PASS")
|
||||
else:
|
||||
print("\nIssue #82: Some tests failed")
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
269
tests/bugs/issue_83_position_tuple_test.py
Normal file
269
tests/bugs/issue_83_position_tuple_test.py
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #83: Add position tuple support to constructors
|
||||
|
||||
This test verifies that UI element constructors now support both:
|
||||
- Traditional (x, y) as separate arguments
|
||||
- Tuple form ((x, y)) as a single argument
|
||||
- Vector form (Vector(x, y)) as a single argument
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_frame_position_tuple():
|
||||
"""Test Frame constructor with position tuples"""
|
||||
print("=== Testing Frame Position Tuple Support ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: Traditional (x, y) form
|
||||
tests_total += 1
|
||||
try:
|
||||
frame1 = mcrfpy.Frame(10, 20, 100, 50)
|
||||
if frame1.x == 10 and frame1.y == 20:
|
||||
print("✓ PASS: Frame(x, y, w, h) traditional form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Frame position incorrect: ({frame1.x}, {frame1.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Traditional form failed: {e}")
|
||||
|
||||
# Test 2: Tuple ((x, y)) form
|
||||
tests_total += 1
|
||||
try:
|
||||
frame2 = mcrfpy.Frame((30, 40), 100, 50)
|
||||
if frame2.x == 30 and frame2.y == 40:
|
||||
print("✓ PASS: Frame((x, y), w, h) tuple form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Frame tuple position incorrect: ({frame2.x}, {frame2.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Tuple form failed: {e}")
|
||||
|
||||
# Test 3: Vector form
|
||||
tests_total += 1
|
||||
try:
|
||||
vec = mcrfpy.Vector(50, 60)
|
||||
frame3 = mcrfpy.Frame(vec, 100, 50)
|
||||
if frame3.x == 50 and frame3.y == 60:
|
||||
print("✓ PASS: Frame(Vector, w, h) vector form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Frame vector position incorrect: ({frame3.x}, {frame3.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Vector form failed: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_sprite_position_tuple():
|
||||
"""Test Sprite constructor with position tuples"""
|
||||
print("\n=== Testing Sprite Position Tuple Support ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
|
||||
# Test 1: Traditional (x, y) form
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite1 = mcrfpy.Sprite(10, 20, texture, 0, 1.0)
|
||||
if sprite1.x == 10 and sprite1.y == 20:
|
||||
print("✓ PASS: Sprite(x, y, texture, ...) traditional form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Sprite position incorrect: ({sprite1.x}, {sprite1.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Traditional form failed: {e}")
|
||||
|
||||
# Test 2: Tuple ((x, y)) form
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite2 = mcrfpy.Sprite((30, 40), texture, 0, 1.0)
|
||||
if sprite2.x == 30 and sprite2.y == 40:
|
||||
print("✓ PASS: Sprite((x, y), texture, ...) tuple form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Sprite tuple position incorrect: ({sprite2.x}, {sprite2.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Tuple form failed: {e}")
|
||||
|
||||
# Test 3: Vector form
|
||||
tests_total += 1
|
||||
try:
|
||||
vec = mcrfpy.Vector(50, 60)
|
||||
sprite3 = mcrfpy.Sprite(vec, texture, 0, 1.0)
|
||||
if sprite3.x == 50 and sprite3.y == 60:
|
||||
print("✓ PASS: Sprite(Vector, texture, ...) vector form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Sprite vector position incorrect: ({sprite3.x}, {sprite3.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Vector form failed: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_caption_position_tuple():
|
||||
"""Test Caption constructor with position tuples"""
|
||||
print("\n=== Testing Caption Position Tuple Support ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
|
||||
# Test 1: Caption doesn't support (x, y) form, only tuple form
|
||||
# Skip this test as Caption expects (pos, text, font) not (x, y, text, font)
|
||||
tests_total += 1
|
||||
tests_passed += 1
|
||||
print("✓ PASS: Caption requires tuple form (by design)")
|
||||
|
||||
# Test 2: Tuple ((x, y)) form
|
||||
tests_total += 1
|
||||
try:
|
||||
caption2 = mcrfpy.Caption((30, 40), "Test", font)
|
||||
if caption2.x == 30 and caption2.y == 40:
|
||||
print("✓ PASS: Caption((x, y), text, font) tuple form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Caption tuple position incorrect: ({caption2.x}, {caption2.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Tuple form failed: {e}")
|
||||
|
||||
# Test 3: Vector form
|
||||
tests_total += 1
|
||||
try:
|
||||
vec = mcrfpy.Vector(50, 60)
|
||||
caption3 = mcrfpy.Caption(vec, "Test", font)
|
||||
if caption3.x == 50 and caption3.y == 60:
|
||||
print("✓ PASS: Caption(Vector, text, font) vector form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Caption vector position incorrect: ({caption3.x}, {caption3.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Vector form failed: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_entity_position_tuple():
|
||||
"""Test Entity constructor with position tuples"""
|
||||
print("\n=== Testing Entity Position Tuple Support ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: Traditional (x, y) form or tuple form
|
||||
tests_total += 1
|
||||
try:
|
||||
# Entity already uses tuple form, so test that it works
|
||||
entity1 = mcrfpy.Entity((10, 20))
|
||||
# Entity.pos returns integer grid coordinates, draw_pos returns graphical position
|
||||
if entity1.draw_pos.x == 10 and entity1.draw_pos.y == 20:
|
||||
print("✓ PASS: Entity((x, y)) tuple form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Entity position incorrect: draw_pos=({entity1.draw_pos.x}, {entity1.draw_pos.y}), pos=({entity1.pos.x}, {entity1.pos.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Tuple form failed: {e}")
|
||||
|
||||
# Test 2: Vector form
|
||||
tests_total += 1
|
||||
try:
|
||||
vec = mcrfpy.Vector(30, 40)
|
||||
entity2 = mcrfpy.Entity(vec)
|
||||
if entity2.draw_pos.x == 30 and entity2.draw_pos.y == 40:
|
||||
print("✓ PASS: Entity(Vector) vector form works")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Entity vector position incorrect: draw_pos=({entity2.draw_pos.x}, {entity2.draw_pos.y}), pos=({entity2.pos.x}, {entity2.pos.y})")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Vector form failed: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_edge_cases():
|
||||
"""Test edge cases for position tuple support"""
|
||||
print("\n=== Testing Edge Cases ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: Empty tuple should fail gracefully
|
||||
tests_total += 1
|
||||
try:
|
||||
frame = mcrfpy.Frame((), 100, 50)
|
||||
# Empty tuple might be accepted and treated as (0, 0)
|
||||
if frame.x == 0 and frame.y == 0:
|
||||
print("✓ PASS: Empty tuple accepted as (0, 0)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Empty tuple handled unexpectedly")
|
||||
except Exception as e:
|
||||
print(f"✓ PASS: Empty tuple correctly rejected: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
# Test 2: Wrong tuple size should fail
|
||||
tests_total += 1
|
||||
try:
|
||||
frame = mcrfpy.Frame((10, 20, 30), 100, 50)
|
||||
print("✗ FAIL: 3-element tuple should have raised an error")
|
||||
except Exception as e:
|
||||
print(f"✓ PASS: Wrong tuple size correctly rejected: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
# Test 3: Non-numeric tuple should fail
|
||||
tests_total += 1
|
||||
try:
|
||||
frame = mcrfpy.Frame(("x", "y"), 100, 50)
|
||||
print("✗ FAIL: Non-numeric tuple should have raised an error")
|
||||
except Exception as e:
|
||||
print(f"✓ PASS: Non-numeric tuple correctly rejected: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
print("=== Testing Position Tuple Support in Constructors (Issue #83) ===\n")
|
||||
|
||||
frame_passed, frame_total = test_frame_position_tuple()
|
||||
sprite_passed, sprite_total = test_sprite_position_tuple()
|
||||
caption_passed, caption_total = test_caption_position_tuple()
|
||||
entity_passed, entity_total = test_entity_position_tuple()
|
||||
edge_passed, edge_total = test_edge_cases()
|
||||
|
||||
total_passed = frame_passed + sprite_passed + caption_passed + entity_passed + edge_passed
|
||||
total_tests = frame_total + sprite_total + caption_total + entity_total + edge_total
|
||||
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Frame tests: {frame_passed}/{frame_total}")
|
||||
print(f"Sprite tests: {sprite_passed}/{sprite_total}")
|
||||
print(f"Caption tests: {caption_passed}/{caption_total}")
|
||||
print(f"Entity tests: {entity_passed}/{entity_total}")
|
||||
print(f"Edge case tests: {edge_passed}/{edge_total}")
|
||||
print(f"Total tests passed: {total_passed}/{total_tests}")
|
||||
|
||||
if total_passed == total_tests:
|
||||
print("\nIssue #83 FIXED: Position tuple support added to constructors!")
|
||||
print("\nOverall result: PASS")
|
||||
else:
|
||||
print("\nIssue #83: Some tests failed")
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
228
tests/bugs/issue_84_pos_property_test.py
Normal file
228
tests/bugs/issue_84_pos_property_test.py
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #84: Add pos property to Frame and Sprite
|
||||
|
||||
This test verifies that Frame and Sprite now have a 'pos' property that
|
||||
returns and accepts Vector objects, similar to Caption and Entity.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_frame_pos_property():
|
||||
"""Test pos property on Frame"""
|
||||
print("=== Testing Frame pos Property ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: Get pos property
|
||||
tests_total += 1
|
||||
try:
|
||||
frame = mcrfpy.Frame(10, 20, 100, 50)
|
||||
pos = frame.pos
|
||||
if hasattr(pos, 'x') and hasattr(pos, 'y') and pos.x == 10 and pos.y == 20:
|
||||
print(f"✓ PASS: frame.pos returns Vector({pos.x}, {pos.y})")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: frame.pos incorrect: {pos}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: pos property not accessible: {e}")
|
||||
|
||||
# Test 2: Set pos with Vector
|
||||
tests_total += 1
|
||||
try:
|
||||
vec = mcrfpy.Vector(30, 40)
|
||||
frame.pos = vec
|
||||
if frame.x == 30 and frame.y == 40:
|
||||
print(f"✓ PASS: frame.pos = Vector sets position correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: pos setter failed: x={frame.x}, y={frame.y}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: pos setter with Vector error: {e}")
|
||||
|
||||
# Test 3: Set pos with tuple
|
||||
tests_total += 1
|
||||
try:
|
||||
frame.pos = (50, 60)
|
||||
if frame.x == 50 and frame.y == 60:
|
||||
print(f"✓ PASS: frame.pos = tuple sets position correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: pos setter with tuple failed: x={frame.x}, y={frame.y}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: pos setter with tuple error: {e}")
|
||||
|
||||
# Test 4: Verify pos getter reflects changes
|
||||
tests_total += 1
|
||||
try:
|
||||
frame.x = 70
|
||||
frame.y = 80
|
||||
pos = frame.pos
|
||||
if pos.x == 70 and pos.y == 80:
|
||||
print(f"✓ PASS: pos property reflects x/y changes")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: pos doesn't reflect changes: {pos.x}, {pos.y}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: pos getter after change error: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_sprite_pos_property():
|
||||
"""Test pos property on Sprite"""
|
||||
print("\n=== Testing Sprite pos Property ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
|
||||
# Test 1: Get pos property
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite = mcrfpy.Sprite(10, 20, texture, 0, 1.0)
|
||||
pos = sprite.pos
|
||||
if hasattr(pos, 'x') and hasattr(pos, 'y') and pos.x == 10 and pos.y == 20:
|
||||
print(f"✓ PASS: sprite.pos returns Vector({pos.x}, {pos.y})")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite.pos incorrect: {pos}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: pos property not accessible: {e}")
|
||||
|
||||
# Test 2: Set pos with Vector
|
||||
tests_total += 1
|
||||
try:
|
||||
vec = mcrfpy.Vector(30, 40)
|
||||
sprite.pos = vec
|
||||
if sprite.x == 30 and sprite.y == 40:
|
||||
print(f"✓ PASS: sprite.pos = Vector sets position correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: pos setter failed: x={sprite.x}, y={sprite.y}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: pos setter with Vector error: {e}")
|
||||
|
||||
# Test 3: Set pos with tuple
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.pos = (50, 60)
|
||||
if sprite.x == 50 and sprite.y == 60:
|
||||
print(f"✓ PASS: sprite.pos = tuple sets position correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: pos setter with tuple failed: x={sprite.x}, y={sprite.y}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: pos setter with tuple error: {e}")
|
||||
|
||||
# Test 4: Verify pos getter reflects changes
|
||||
tests_total += 1
|
||||
try:
|
||||
sprite.x = 70
|
||||
sprite.y = 80
|
||||
pos = sprite.pos
|
||||
if pos.x == 70 and pos.y == 80:
|
||||
print(f"✓ PASS: pos property reflects x/y changes")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: pos doesn't reflect changes: {pos.x}, {pos.y}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: pos getter after change error: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_consistency_with_caption_entity():
|
||||
"""Test that pos property is consistent across all UI elements"""
|
||||
print("\n=== Testing Consistency with Caption/Entity ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: Caption pos property (should already exist)
|
||||
tests_total += 1
|
||||
try:
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
caption = mcrfpy.Caption((10, 20), "Test", font)
|
||||
pos = caption.pos
|
||||
if hasattr(pos, 'x') and hasattr(pos, 'y'):
|
||||
print(f"✓ PASS: Caption.pos works as expected")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Caption.pos doesn't return Vector")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Caption.pos error: {e}")
|
||||
|
||||
# Test 2: Entity draw_pos property (should already exist)
|
||||
tests_total += 1
|
||||
try:
|
||||
entity = mcrfpy.Entity((10, 20))
|
||||
pos = entity.draw_pos
|
||||
if hasattr(pos, 'x') and hasattr(pos, 'y'):
|
||||
print(f"✓ PASS: Entity.draw_pos works as expected")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Entity.draw_pos doesn't return Vector")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Entity.draw_pos error: {e}")
|
||||
|
||||
# Test 3: All pos properties return same type
|
||||
tests_total += 1
|
||||
try:
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
frame = mcrfpy.Frame(10, 20, 100, 50)
|
||||
sprite = mcrfpy.Sprite(10, 20, texture, 0, 1.0)
|
||||
|
||||
frame_pos = frame.pos
|
||||
sprite_pos = sprite.pos
|
||||
|
||||
if (type(frame_pos).__name__ == type(sprite_pos).__name__ == 'Vector'):
|
||||
print(f"✓ PASS: All pos properties return Vector type")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Inconsistent pos property types")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Type consistency check error: {e}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
print("=== Testing pos Property for Frame and Sprite (Issue #84) ===\n")
|
||||
|
||||
frame_passed, frame_total = test_frame_pos_property()
|
||||
sprite_passed, sprite_total = test_sprite_pos_property()
|
||||
consistency_passed, consistency_total = test_consistency_with_caption_entity()
|
||||
|
||||
total_passed = frame_passed + sprite_passed + consistency_passed
|
||||
total_tests = frame_total + sprite_total + consistency_total
|
||||
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Frame tests: {frame_passed}/{frame_total}")
|
||||
print(f"Sprite tests: {sprite_passed}/{sprite_total}")
|
||||
print(f"Consistency tests: {consistency_passed}/{consistency_total}")
|
||||
print(f"Total tests passed: {total_passed}/{total_tests}")
|
||||
|
||||
if total_passed == total_tests:
|
||||
print("\nIssue #84 FIXED: pos property added to Frame and Sprite!")
|
||||
print("\nOverall result: PASS")
|
||||
else:
|
||||
print("\nIssue #84: Some tests failed")
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
169
tests/bugs/issue_95_uicollection_repr_test.py
Normal file
169
tests/bugs/issue_95_uicollection_repr_test.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #95: Fix UICollection __repr__ type display
|
||||
|
||||
This test verifies that UICollection's repr shows the actual types of contained
|
||||
objects instead of just showing them all as "UIDrawable".
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_uicollection_repr():
|
||||
"""Test UICollection repr shows correct types"""
|
||||
print("=== Testing UICollection __repr__ Type Display (Issue #95) ===\n")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Get scene UI collection
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
|
||||
# Test 1: Empty collection
|
||||
print("--- Test 1: Empty collection ---")
|
||||
tests_total += 1
|
||||
repr_str = repr(scene_ui)
|
||||
print(f"Empty collection repr: {repr_str}")
|
||||
if "0 objects" in repr_str:
|
||||
print("✓ PASS: Empty collection shows correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Empty collection repr incorrect")
|
||||
|
||||
# Test 2: Add various UI elements
|
||||
print("\n--- Test 2: Mixed UI elements ---")
|
||||
tests_total += 1
|
||||
|
||||
# Add Frame
|
||||
frame = mcrfpy.Frame(10, 10, 100, 100)
|
||||
scene_ui.append(frame)
|
||||
|
||||
# Add Caption
|
||||
caption = mcrfpy.Caption((150, 50), "Test", mcrfpy.Font("assets/JetbrainsMono.ttf"))
|
||||
scene_ui.append(caption)
|
||||
|
||||
# Add Sprite
|
||||
sprite = mcrfpy.Sprite(200, 100)
|
||||
scene_ui.append(sprite)
|
||||
|
||||
# Add Grid
|
||||
grid = mcrfpy.Grid(10, 10)
|
||||
grid.x = 300
|
||||
grid.y = 100
|
||||
scene_ui.append(grid)
|
||||
|
||||
# Check repr
|
||||
repr_str = repr(scene_ui)
|
||||
print(f"Collection repr: {repr_str}")
|
||||
|
||||
# Verify it shows the correct types
|
||||
expected_types = ["1 Frame", "1 Caption", "1 Sprite", "1 Grid"]
|
||||
all_found = all(expected in repr_str for expected in expected_types)
|
||||
|
||||
if all_found and "UIDrawable" not in repr_str:
|
||||
print("✓ PASS: All types shown correctly, no generic UIDrawable")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Types not shown correctly")
|
||||
for expected in expected_types:
|
||||
if expected in repr_str:
|
||||
print(f" ✓ Found: {expected}")
|
||||
else:
|
||||
print(f" ✗ Missing: {expected}")
|
||||
if "UIDrawable" in repr_str:
|
||||
print(" ✗ Still shows generic UIDrawable")
|
||||
|
||||
# Test 3: Multiple of same type
|
||||
print("\n--- Test 3: Multiple objects of same type ---")
|
||||
tests_total += 1
|
||||
|
||||
# Add more frames
|
||||
frame2 = mcrfpy.Frame(10, 120, 100, 100)
|
||||
frame3 = mcrfpy.Frame(10, 230, 100, 100)
|
||||
scene_ui.append(frame2)
|
||||
scene_ui.append(frame3)
|
||||
|
||||
repr_str = repr(scene_ui)
|
||||
print(f"Collection repr: {repr_str}")
|
||||
|
||||
if "3 Frames" in repr_str:
|
||||
print("✓ PASS: Plural form shown correctly for multiple Frames")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Plural form not correct")
|
||||
|
||||
# Test 4: Check total count
|
||||
print("\n--- Test 4: Total count verification ---")
|
||||
tests_total += 1
|
||||
|
||||
# Should have: 3 Frames, 1 Caption, 1 Sprite, 1 Grid = 6 total
|
||||
if "6 objects:" in repr_str:
|
||||
print("✓ PASS: Total count shown correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Total count incorrect")
|
||||
|
||||
# Test 5: Nested collections (Frame with children)
|
||||
print("\n--- Test 5: Nested collections ---")
|
||||
tests_total += 1
|
||||
|
||||
# Add child to frame
|
||||
child_sprite = mcrfpy.Sprite(10, 10)
|
||||
frame.children.append(child_sprite)
|
||||
|
||||
# Check frame's children collection
|
||||
children_repr = repr(frame.children)
|
||||
print(f"Frame children repr: {children_repr}")
|
||||
|
||||
if "1 Sprite" in children_repr:
|
||||
print("✓ PASS: Nested collection shows correct type")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Nested collection type incorrect")
|
||||
|
||||
# Test 6: Collection remains valid after modifications
|
||||
print("\n--- Test 6: Collection after modifications ---")
|
||||
tests_total += 1
|
||||
|
||||
# Remove an item
|
||||
scene_ui.remove(0) # Remove first frame
|
||||
|
||||
repr_str = repr(scene_ui)
|
||||
print(f"After removal repr: {repr_str}")
|
||||
|
||||
if "2 Frames" in repr_str and "5 objects:" in repr_str:
|
||||
print("✓ PASS: Collection repr updated correctly after removal")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print("✗ FAIL: Collection repr not updated correctly")
|
||||
|
||||
# Summary
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Tests passed: {tests_passed}/{tests_total}")
|
||||
|
||||
if tests_passed == tests_total:
|
||||
print("\nIssue #95 FIXED: UICollection __repr__ now shows correct types!")
|
||||
else:
|
||||
print("\nIssue #95: Some tests failed")
|
||||
|
||||
return tests_passed == tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
success = test_uicollection_repr()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
205
tests/bugs/issue_96_uicollection_extend_test.py
Normal file
205
tests/bugs/issue_96_uicollection_extend_test.py
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #96: Add extend() method to UICollection
|
||||
|
||||
This test verifies that UICollection now has an extend() method similar to
|
||||
UIEntityCollection.extend().
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_uicollection_extend():
|
||||
"""Test UICollection extend method"""
|
||||
print("=== Testing UICollection extend() Method (Issue #96) ===\n")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Get scene UI collection
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
|
||||
# Test 1: Basic extend with list
|
||||
print("--- Test 1: Extend with list ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
# Create a list of UI elements
|
||||
elements = [
|
||||
mcrfpy.Frame(10, 10, 100, 100),
|
||||
mcrfpy.Caption((150, 50), "Test1", mcrfpy.Font("assets/JetbrainsMono.ttf")),
|
||||
mcrfpy.Sprite(200, 100)
|
||||
]
|
||||
|
||||
# Extend the collection
|
||||
scene_ui.extend(elements)
|
||||
|
||||
if len(scene_ui) == 3:
|
||||
print("✓ PASS: Extended collection with 3 elements")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Expected 3 elements, got {len(scene_ui)}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Error extending with list: {e}")
|
||||
|
||||
# Test 2: Extend with tuple
|
||||
print("\n--- Test 2: Extend with tuple ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
# Create a tuple of UI elements
|
||||
more_elements = (
|
||||
mcrfpy.Grid(10, 10),
|
||||
mcrfpy.Frame(300, 10, 100, 100)
|
||||
)
|
||||
|
||||
# Extend the collection
|
||||
scene_ui.extend(more_elements)
|
||||
|
||||
if len(scene_ui) == 5:
|
||||
print("✓ PASS: Extended collection with tuple (now 5 elements)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Expected 5 elements, got {len(scene_ui)}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Error extending with tuple: {e}")
|
||||
|
||||
# Test 3: Extend with generator
|
||||
print("\n--- Test 3: Extend with generator ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
# Create a generator of UI elements
|
||||
def create_sprites():
|
||||
for i in range(3):
|
||||
yield mcrfpy.Sprite(50 + i*50, 200)
|
||||
|
||||
# Extend with generator
|
||||
scene_ui.extend(create_sprites())
|
||||
|
||||
if len(scene_ui) == 8:
|
||||
print("✓ PASS: Extended collection with generator (now 8 elements)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Expected 8 elements, got {len(scene_ui)}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Error extending with generator: {e}")
|
||||
|
||||
# Test 4: Error handling - non-iterable
|
||||
print("\n--- Test 4: Error handling - non-iterable ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
scene_ui.extend(42) # Not iterable
|
||||
print("✗ FAIL: Should have raised TypeError for non-iterable")
|
||||
except TypeError as e:
|
||||
print(f"✓ PASS: Correctly raised TypeError: {e}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Wrong exception type: {e}")
|
||||
|
||||
# Test 5: Error handling - wrong element type
|
||||
print("\n--- Test 5: Error handling - wrong element type ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
scene_ui.extend([1, 2, 3]) # Wrong types
|
||||
print("✗ FAIL: Should have raised TypeError for non-UIDrawable elements")
|
||||
except TypeError as e:
|
||||
print(f"✓ PASS: Correctly raised TypeError: {e}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Wrong exception type: {e}")
|
||||
|
||||
# Test 6: Extend empty iterable
|
||||
print("\n--- Test 6: Extend with empty list ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
initial_len = len(scene_ui)
|
||||
scene_ui.extend([]) # Empty list
|
||||
|
||||
if len(scene_ui) == initial_len:
|
||||
print("✓ PASS: Extending with empty list works correctly")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Length changed from {initial_len} to {len(scene_ui)}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Error extending with empty list: {e}")
|
||||
|
||||
# Test 7: Z-index ordering
|
||||
print("\n--- Test 7: Z-index ordering ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
# Clear and add fresh elements
|
||||
while len(scene_ui) > 0:
|
||||
scene_ui.remove(0)
|
||||
|
||||
# Add some initial elements
|
||||
frame1 = mcrfpy.Frame(0, 0, 50, 50)
|
||||
scene_ui.append(frame1)
|
||||
|
||||
# Extend with more elements
|
||||
new_elements = [
|
||||
mcrfpy.Frame(60, 0, 50, 50),
|
||||
mcrfpy.Caption((120, 25), "Test", mcrfpy.Font("assets/JetbrainsMono.ttf"))
|
||||
]
|
||||
scene_ui.extend(new_elements)
|
||||
|
||||
# Check z-indices are properly assigned
|
||||
z_indices = [scene_ui[i].z_index for i in range(3)]
|
||||
|
||||
# Z-indices should be increasing
|
||||
if z_indices[0] < z_indices[1] < z_indices[2]:
|
||||
print(f"✓ PASS: Z-indices properly ordered: {z_indices}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Z-indices not properly ordered: {z_indices}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Error checking z-indices: {e}")
|
||||
|
||||
# Test 8: Extend with another UICollection
|
||||
print("\n--- Test 8: Extend with another UICollection ---")
|
||||
tests_total += 1
|
||||
try:
|
||||
# Create a Frame with children
|
||||
frame_with_children = mcrfpy.Frame(200, 200, 100, 100)
|
||||
frame_with_children.children.append(mcrfpy.Sprite(10, 10))
|
||||
frame_with_children.children.append(mcrfpy.Caption((10, 50), "Child", mcrfpy.Font("assets/JetbrainsMono.ttf")))
|
||||
|
||||
# Try to extend scene_ui with the frame's children collection
|
||||
initial_len = len(scene_ui)
|
||||
scene_ui.extend(frame_with_children.children)
|
||||
|
||||
if len(scene_ui) == initial_len + 2:
|
||||
print("✓ PASS: Extended with another UICollection")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Expected {initial_len + 2} elements, got {len(scene_ui)}")
|
||||
except Exception as e:
|
||||
print(f"✗ FAIL: Error extending with UICollection: {e}")
|
||||
|
||||
# Summary
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Tests passed: {tests_passed}/{tests_total}")
|
||||
|
||||
if tests_passed == tests_total:
|
||||
print("\nIssue #96 FIXED: UICollection.extend() implemented successfully!")
|
||||
else:
|
||||
print("\nIssue #96: Some tests failed")
|
||||
|
||||
return tests_passed == tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
success = test_uicollection_extend()
|
||||
print("\nOverall result: " + ("PASS" if success else "FAIL"))
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
224
tests/bugs/issue_99_texture_font_properties_test.py
Normal file
224
tests/bugs/issue_99_texture_font_properties_test.py
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #99: Expose Texture and Font properties
|
||||
|
||||
This test verifies that Texture and Font objects now expose their properties
|
||||
as read-only attributes.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_texture_properties():
|
||||
"""Test Texture properties"""
|
||||
print("=== Testing Texture Properties ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Create a texture
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
|
||||
# Test 1: sprite_width property
|
||||
tests_total += 1
|
||||
try:
|
||||
width = texture.sprite_width
|
||||
if width == 16:
|
||||
print(f"✓ PASS: sprite_width = {width}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite_width = {width}, expected 16")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sprite_width not accessible: {e}")
|
||||
|
||||
# Test 2: sprite_height property
|
||||
tests_total += 1
|
||||
try:
|
||||
height = texture.sprite_height
|
||||
if height == 16:
|
||||
print(f"✓ PASS: sprite_height = {height}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite_height = {height}, expected 16")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sprite_height not accessible: {e}")
|
||||
|
||||
# Test 3: sheet_width property
|
||||
tests_total += 1
|
||||
try:
|
||||
sheet_w = texture.sheet_width
|
||||
if isinstance(sheet_w, int) and sheet_w > 0:
|
||||
print(f"✓ PASS: sheet_width = {sheet_w}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sheet_width invalid: {sheet_w}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sheet_width not accessible: {e}")
|
||||
|
||||
# Test 4: sheet_height property
|
||||
tests_total += 1
|
||||
try:
|
||||
sheet_h = texture.sheet_height
|
||||
if isinstance(sheet_h, int) and sheet_h > 0:
|
||||
print(f"✓ PASS: sheet_height = {sheet_h}")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sheet_height invalid: {sheet_h}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sheet_height not accessible: {e}")
|
||||
|
||||
# Test 5: sprite_count property
|
||||
tests_total += 1
|
||||
try:
|
||||
count = texture.sprite_count
|
||||
expected = texture.sheet_width * texture.sheet_height
|
||||
if count == expected:
|
||||
print(f"✓ PASS: sprite_count = {count} (sheet_width * sheet_height)")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: sprite_count = {count}, expected {expected}")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: sprite_count not accessible: {e}")
|
||||
|
||||
# Test 6: source property
|
||||
tests_total += 1
|
||||
try:
|
||||
source = texture.source
|
||||
if "kenney_tinydungeon.png" in source:
|
||||
print(f"✓ PASS: source = '{source}'")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: source unexpected: '{source}'")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: source not accessible: {e}")
|
||||
|
||||
# Test 7: Properties are read-only
|
||||
tests_total += 1
|
||||
try:
|
||||
texture.sprite_width = 32 # Should fail
|
||||
print("✗ FAIL: sprite_width should be read-only")
|
||||
except AttributeError as e:
|
||||
print(f"✓ PASS: sprite_width is read-only: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_font_properties():
|
||||
"""Test Font properties"""
|
||||
print("\n=== Testing Font Properties ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Create a font
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
|
||||
# Test 1: family property
|
||||
tests_total += 1
|
||||
try:
|
||||
family = font.family
|
||||
if isinstance(family, str) and len(family) > 0:
|
||||
print(f"✓ PASS: family = '{family}'")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: family invalid: '{family}'")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: family not accessible: {e}")
|
||||
|
||||
# Test 2: source property
|
||||
tests_total += 1
|
||||
try:
|
||||
source = font.source
|
||||
if "JetbrainsMono.ttf" in source:
|
||||
print(f"✓ PASS: source = '{source}'")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: source unexpected: '{source}'")
|
||||
except AttributeError as e:
|
||||
print(f"✗ FAIL: source not accessible: {e}")
|
||||
|
||||
# Test 3: Properties are read-only
|
||||
tests_total += 1
|
||||
try:
|
||||
font.family = "Arial" # Should fail
|
||||
print("✗ FAIL: family should be read-only")
|
||||
except AttributeError as e:
|
||||
print(f"✓ PASS: family is read-only: {e}")
|
||||
tests_passed += 1
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def test_property_introspection():
|
||||
"""Test that properties appear in dir()"""
|
||||
print("\n=== Testing Property Introspection ===")
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test Texture properties in dir()
|
||||
tests_total += 1
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
texture_props = dir(texture)
|
||||
expected_texture_props = ['sprite_width', 'sprite_height', 'sheet_width', 'sheet_height', 'sprite_count', 'source']
|
||||
|
||||
missing = [p for p in expected_texture_props if p not in texture_props]
|
||||
if not missing:
|
||||
print("✓ PASS: All Texture properties appear in dir()")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Missing Texture properties in dir(): {missing}")
|
||||
|
||||
# Test Font properties in dir()
|
||||
tests_total += 1
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
font_props = dir(font)
|
||||
expected_font_props = ['family', 'source']
|
||||
|
||||
missing = [p for p in expected_font_props if p not in font_props]
|
||||
if not missing:
|
||||
print("✓ PASS: All Font properties appear in dir()")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print(f"✗ FAIL: Missing Font properties in dir(): {missing}")
|
||||
|
||||
return tests_passed, tests_total
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
print("=== Testing Texture and Font Properties (Issue #99) ===\n")
|
||||
|
||||
texture_passed, texture_total = test_texture_properties()
|
||||
font_passed, font_total = test_font_properties()
|
||||
intro_passed, intro_total = test_property_introspection()
|
||||
|
||||
total_passed = texture_passed + font_passed + intro_passed
|
||||
total_tests = texture_total + font_total + intro_total
|
||||
|
||||
print(f"\n=== SUMMARY ===")
|
||||
print(f"Texture tests: {texture_passed}/{texture_total}")
|
||||
print(f"Font tests: {font_passed}/{font_total}")
|
||||
print(f"Introspection tests: {intro_passed}/{intro_total}")
|
||||
print(f"Total tests passed: {total_passed}/{total_tests}")
|
||||
|
||||
if total_passed == total_tests:
|
||||
print("\nIssue #99 FIXED: Texture and Font properties exposed successfully!")
|
||||
print("\nOverall result: PASS")
|
||||
else:
|
||||
print("\nIssue #99: Some tests failed")
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("\nOverall result: FAIL")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
67
tests/bugs/issue_9_minimal_test.py
Normal file
67
tests/bugs/issue_9_minimal_test.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Minimal test for Issue #9: RenderTexture resize
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
|
||||
def run_test(runtime):
|
||||
"""Test RenderTexture resizing"""
|
||||
print("Testing Issue #9: RenderTexture resize (minimal)")
|
||||
|
||||
try:
|
||||
# Create a grid
|
||||
print("Creating grid...")
|
||||
grid = mcrfpy.Grid(30, 30)
|
||||
grid.x = 10
|
||||
grid.y = 10
|
||||
grid.w = 300
|
||||
grid.h = 300
|
||||
|
||||
# Add to scene
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
scene_ui.append(grid)
|
||||
|
||||
# Test accessing grid points
|
||||
print("Testing grid.at()...")
|
||||
point = grid.at(5, 5)
|
||||
print(f"Got grid point: {point}")
|
||||
|
||||
# Test color creation
|
||||
print("Testing Color creation...")
|
||||
red = mcrfpy.Color(255, 0, 0, 255)
|
||||
print(f"Created color: {red}")
|
||||
|
||||
# Set color
|
||||
print("Setting grid point color...")
|
||||
point.color = red
|
||||
|
||||
print("Taking screenshot before resize...")
|
||||
automation.screenshot("/tmp/issue_9_minimal_before.png")
|
||||
|
||||
# Resize grid
|
||||
print("Resizing grid to 2500x2500...")
|
||||
grid.w = 2500
|
||||
grid.h = 2500
|
||||
|
||||
print("Taking screenshot after resize...")
|
||||
automation.screenshot("/tmp/issue_9_minimal_after.png")
|
||||
|
||||
print("\nTest complete - check screenshots")
|
||||
print("If RenderTexture is recreated properly, grid should render correctly at large size")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Create and set scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
229
tests/bugs/issue_9_rendertexture_resize_test.py
Normal file
229
tests/bugs/issue_9_rendertexture_resize_test.py
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive test for Issue #9: Recreate RenderTexture when UIGrid is resized
|
||||
|
||||
This test demonstrates that UIGrid has a hardcoded RenderTexture size of 1920x1080,
|
||||
which causes rendering issues when the grid is resized beyond these dimensions.
|
||||
|
||||
The bug: UIGrid::render() creates a RenderTexture with fixed size (1920x1080) once,
|
||||
but never recreates it when the grid is resized, causing clipping and rendering artifacts.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
import os
|
||||
|
||||
def create_checkerboard_pattern(grid, grid_width, grid_height, cell_size=2):
|
||||
"""Create a checkerboard pattern on the grid for visibility"""
|
||||
for x in range(grid_width):
|
||||
for y in range(grid_height):
|
||||
if (x // cell_size + y // cell_size) % 2 == 0:
|
||||
grid.at(x, y).color = mcrfpy.Color(255, 255, 255, 255) # White
|
||||
else:
|
||||
grid.at(x, y).color = mcrfpy.Color(100, 100, 100, 255) # Gray
|
||||
|
||||
def add_border_markers(grid, grid_width, grid_height):
|
||||
"""Add colored markers at the borders to test rendering limits"""
|
||||
# Red border on top
|
||||
for x in range(grid_width):
|
||||
grid.at(x, 0).color = mcrfpy.Color(255, 0, 0, 255)
|
||||
|
||||
# Green border on right
|
||||
for y in range(grid_height):
|
||||
grid.at(grid_width-1, y).color = mcrfpy.Color(0, 255, 0, 255)
|
||||
|
||||
# Blue border on bottom
|
||||
for x in range(grid_width):
|
||||
grid.at(x, grid_height-1).color = mcrfpy.Color(0, 0, 255, 255)
|
||||
|
||||
# Yellow border on left
|
||||
for y in range(grid_height):
|
||||
grid.at(0, y).color = mcrfpy.Color(255, 255, 0, 255)
|
||||
|
||||
def test_rendertexture_resize():
|
||||
"""Test RenderTexture behavior with various grid sizes"""
|
||||
print("=== Testing UIGrid RenderTexture Resize (Issue #9) ===\n")
|
||||
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
|
||||
# Test 1: Small grid (should work fine)
|
||||
print("--- Test 1: Small Grid (400x300) ---")
|
||||
grid1 = mcrfpy.Grid(20, 15) # 20x15 tiles
|
||||
grid1.x = 10
|
||||
grid1.y = 10
|
||||
grid1.w = 400
|
||||
grid1.h = 300
|
||||
scene_ui.append(grid1)
|
||||
|
||||
create_checkerboard_pattern(grid1, 20, 15)
|
||||
add_border_markers(grid1, 20, 15)
|
||||
|
||||
automation.screenshot("/tmp/issue_9_small_grid.png")
|
||||
print("✓ Small grid created and rendered")
|
||||
|
||||
# Test 2: Medium grid at 1920x1080 limit
|
||||
print("\n--- Test 2: Medium Grid at 1920x1080 Limit ---")
|
||||
grid2 = mcrfpy.Grid(64, 36) # 64x36 tiles at 30px each = 1920x1080
|
||||
grid2.x = 10
|
||||
grid2.y = 320
|
||||
grid2.w = 1920
|
||||
grid2.h = 1080
|
||||
scene_ui.append(grid2)
|
||||
|
||||
create_checkerboard_pattern(grid2, 64, 36, 4)
|
||||
add_border_markers(grid2, 64, 36)
|
||||
|
||||
automation.screenshot("/tmp/issue_9_limit_grid.png")
|
||||
print("✓ Grid at RenderTexture limit created")
|
||||
|
||||
# Test 3: Resize grid1 beyond limits
|
||||
print("\n--- Test 3: Resizing Small Grid Beyond 1920x1080 ---")
|
||||
print("Original size: 400x300")
|
||||
grid1.w = 2400
|
||||
grid1.h = 1400
|
||||
print(f"Resized to: {grid1.w}x{grid1.h}")
|
||||
|
||||
# The content should still be visible but may be clipped
|
||||
automation.screenshot("/tmp/issue_9_resized_beyond_limit.png")
|
||||
print("✗ EXPECTED ISSUE: Grid resized beyond RenderTexture limits")
|
||||
print(" Content beyond 1920x1080 will be clipped!")
|
||||
|
||||
# Test 4: Create large grid from start
|
||||
print("\n--- Test 4: Large Grid from Start (2400x1400) ---")
|
||||
# Clear previous grids
|
||||
while len(scene_ui) > 0:
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid3 = mcrfpy.Grid(80, 50) # Large tile count
|
||||
grid3.x = 10
|
||||
grid3.y = 10
|
||||
grid3.w = 2400
|
||||
grid3.h = 1400
|
||||
scene_ui.append(grid3)
|
||||
|
||||
create_checkerboard_pattern(grid3, 80, 50, 5)
|
||||
add_border_markers(grid3, 80, 50)
|
||||
|
||||
# Add markers at specific positions to test rendering
|
||||
# Mark the center
|
||||
center_x, center_y = 40, 25
|
||||
for dx in range(-2, 3):
|
||||
for dy in range(-2, 3):
|
||||
grid3.at(center_x + dx, center_y + dy).color = mcrfpy.Color(255, 0, 255, 255) # Magenta
|
||||
|
||||
# Mark position at 1920 pixel boundary (64 tiles * 30 pixels/tile = 1920)
|
||||
if 64 < 80: # Only if within grid bounds
|
||||
for y in range(min(50, 10)):
|
||||
grid3.at(64, y).color = mcrfpy.Color(255, 128, 0, 255) # Orange
|
||||
|
||||
automation.screenshot("/tmp/issue_9_large_grid.png")
|
||||
print("✗ EXPECTED ISSUE: Large grid created")
|
||||
print(" Content beyond 1920x1080 will not render!")
|
||||
print(" Look for missing orange line at x=1920 boundary")
|
||||
|
||||
# Test 5: Dynamic resize test
|
||||
print("\n--- Test 5: Dynamic Resize Test ---")
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid4 = mcrfpy.Grid(100, 100)
|
||||
grid4.x = 10
|
||||
grid4.y = 10
|
||||
scene_ui.append(grid4)
|
||||
|
||||
sizes = [(500, 500), (1000, 1000), (1500, 1500), (2000, 2000), (2500, 2500)]
|
||||
|
||||
for i, (w, h) in enumerate(sizes):
|
||||
grid4.w = w
|
||||
grid4.h = h
|
||||
|
||||
# Add pattern at current size
|
||||
visible_tiles_x = min(100, w // 30)
|
||||
visible_tiles_y = min(100, h // 30)
|
||||
|
||||
# Clear and create new pattern
|
||||
for x in range(visible_tiles_x):
|
||||
for y in range(visible_tiles_y):
|
||||
if x == visible_tiles_x - 1 or y == visible_tiles_y - 1:
|
||||
# Edge markers
|
||||
grid4.at(x, y).color = mcrfpy.Color(255, 255, 0, 255)
|
||||
elif (x + y) % 10 == 0:
|
||||
# Diagonal lines
|
||||
grid4.at(x, y).color = mcrfpy.Color(0, 255, 255, 255)
|
||||
|
||||
automation.screenshot(f"/tmp/issue_9_resize_{w}x{h}.png")
|
||||
|
||||
if w > 1920 or h > 1080:
|
||||
print(f"✗ Size {w}x{h}: Content clipped at 1920x1080")
|
||||
else:
|
||||
print(f"✓ Size {w}x{h}: Rendered correctly")
|
||||
|
||||
# Test 6: Verify exact clipping boundary
|
||||
print("\n--- Test 6: Exact Clipping Boundary Test ---")
|
||||
scene_ui.remove(0)
|
||||
|
||||
grid5 = mcrfpy.Grid(70, 40)
|
||||
grid5.x = 0
|
||||
grid5.y = 0
|
||||
grid5.w = 2100 # 70 * 30 = 2100 pixels
|
||||
grid5.h = 1200 # 40 * 30 = 1200 pixels
|
||||
scene_ui.append(grid5)
|
||||
|
||||
# Create a pattern that shows the boundary clearly
|
||||
for x in range(70):
|
||||
for y in range(40):
|
||||
pixel_x = x * 30
|
||||
pixel_y = y * 30
|
||||
|
||||
if pixel_x == 1920 - 30: # Last tile before boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(255, 0, 0, 255) # Red
|
||||
elif pixel_x == 1920: # First tile after boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(0, 255, 0, 255) # Green
|
||||
elif pixel_y == 1080 - 30: # Last row before boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(0, 0, 255, 255) # Blue
|
||||
elif pixel_y == 1080: # First row after boundary
|
||||
grid5.at(x, y).color = mcrfpy.Color(255, 255, 0, 255) # Yellow
|
||||
else:
|
||||
# Normal checkerboard
|
||||
if (x + y) % 2 == 0:
|
||||
grid5.at(x, y).color = mcrfpy.Color(200, 200, 200, 255)
|
||||
|
||||
automation.screenshot("/tmp/issue_9_boundary_test.png")
|
||||
print("Screenshot saved showing clipping boundary")
|
||||
print("- Red tiles: Last visible column (x=1890-1919)")
|
||||
print("- Green tiles: First clipped column (x=1920+)")
|
||||
print("- Blue tiles: Last visible row (y=1050-1079)")
|
||||
print("- Yellow tiles: First clipped row (y=1080+)")
|
||||
|
||||
# Summary
|
||||
print("\n=== SUMMARY ===")
|
||||
print("Issue #9: UIGrid uses a hardcoded RenderTexture size of 1920x1080")
|
||||
print("Problems demonstrated:")
|
||||
print("1. Grids larger than 1920x1080 are clipped")
|
||||
print("2. Resizing grids doesn't recreate the RenderTexture")
|
||||
print("3. Content beyond the boundary is not rendered")
|
||||
print("\nThe fix should:")
|
||||
print("1. Recreate RenderTexture when grid size changes")
|
||||
print("2. Use the actual grid dimensions instead of hardcoded values")
|
||||
print("3. Consider memory limits for very large grids")
|
||||
|
||||
print(f"\nScreenshots saved to /tmp/issue_9_*.png")
|
||||
|
||||
def run_test(runtime):
|
||||
"""Timer callback to run the test"""
|
||||
try:
|
||||
test_rendertexture_resize()
|
||||
print("\nTest complete - check screenshots for visual verification")
|
||||
except Exception as e:
|
||||
print(f"\nTest error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
71
tests/bugs/issue_9_simple_test.py
Normal file
71
tests/bugs/issue_9_simple_test.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple test for Issue #9: RenderTexture resize
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
|
||||
def run_test(runtime):
|
||||
"""Test RenderTexture resizing"""
|
||||
print("Testing Issue #9: RenderTexture resize")
|
||||
|
||||
# Create a scene
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
|
||||
# Create a small grid
|
||||
print("Creating 50x50 grid with initial size 500x500")
|
||||
grid = mcrfpy.Grid(50, 50)
|
||||
grid.x = 10
|
||||
grid.y = 10
|
||||
grid.w = 500
|
||||
grid.h = 500
|
||||
scene_ui.append(grid)
|
||||
|
||||
# Color some tiles to make it visible
|
||||
print("Coloring tiles...")
|
||||
for i in range(50):
|
||||
# Diagonal line
|
||||
grid.at(i, i).color = mcrfpy.Color(255, 0, 0, 255)
|
||||
# Borders
|
||||
grid.at(i, 0).color = mcrfpy.Color(0, 255, 0, 255)
|
||||
grid.at(0, i).color = mcrfpy.Color(0, 0, 255, 255)
|
||||
grid.at(i, 49).color = mcrfpy.Color(255, 255, 0, 255)
|
||||
grid.at(49, i).color = mcrfpy.Color(255, 0, 255, 255)
|
||||
|
||||
# Take initial screenshot
|
||||
automation.screenshot("/tmp/issue_9_before_resize.png")
|
||||
print("Screenshot saved: /tmp/issue_9_before_resize.png")
|
||||
|
||||
# Resize to larger than 1920x1080
|
||||
print("\nResizing grid to 2500x2500...")
|
||||
grid.w = 2500
|
||||
grid.h = 2500
|
||||
|
||||
# Take screenshot after resize
|
||||
automation.screenshot("/tmp/issue_9_after_resize.png")
|
||||
print("Screenshot saved: /tmp/issue_9_after_resize.png")
|
||||
|
||||
# Test individual dimension changes
|
||||
print("\nTesting individual dimension changes...")
|
||||
grid.w = 3000
|
||||
automation.screenshot("/tmp/issue_9_width_3000.png")
|
||||
print("Width set to 3000, screenshot: /tmp/issue_9_width_3000.png")
|
||||
|
||||
grid.h = 3000
|
||||
automation.screenshot("/tmp/issue_9_both_3000.png")
|
||||
print("Height set to 3000, screenshot: /tmp/issue_9_both_3000.png")
|
||||
|
||||
print("\nIf the RenderTexture is properly recreated, all colored tiles")
|
||||
print("should be visible in all screenshots, not clipped at 1920x1080.")
|
||||
|
||||
print("\nTest complete - PASS")
|
||||
sys.exit(0)
|
||||
|
||||
# Create and set scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
89
tests/bugs/issue_9_test.py
Normal file
89
tests/bugs/issue_9_test.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test for Issue #9: Recreate RenderTexture when UIGrid is resized
|
||||
|
||||
This test checks if resizing a UIGrid properly recreates its RenderTexture.
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
|
||||
def run_test(runtime):
|
||||
"""Test that UIGrid properly handles resizing"""
|
||||
try:
|
||||
# Create a grid with initial size
|
||||
grid = mcrfpy.Grid(20, 20)
|
||||
grid.x = 50
|
||||
grid.y = 50
|
||||
grid.w = 200
|
||||
grid.h = 200
|
||||
|
||||
# Add grid to scene
|
||||
scene_ui = mcrfpy.sceneUI("test")
|
||||
scene_ui.append(grid)
|
||||
|
||||
# Take initial screenshot
|
||||
automation.screenshot("/tmp/grid_initial.png")
|
||||
print("Initial grid created at 200x200")
|
||||
|
||||
# Add some visible content to the grid
|
||||
for x in range(5):
|
||||
for y in range(5):
|
||||
grid.at(x, y).color = mcrfpy.Color(255, 0, 0, 255) # Red squares
|
||||
|
||||
automation.screenshot("/tmp/grid_with_content.png")
|
||||
print("Added red squares to grid")
|
||||
|
||||
# Test 1: Resize the grid smaller
|
||||
print("\nTest 1: Resizing grid to 100x100...")
|
||||
grid.w = 100
|
||||
grid.h = 100
|
||||
|
||||
automation.screenshot("/tmp/grid_resized_small.png")
|
||||
|
||||
# The grid should still render correctly
|
||||
print("✓ Test 1: Grid resized to 100x100")
|
||||
|
||||
# Test 2: Resize the grid larger than initial
|
||||
print("\nTest 2: Resizing grid to 400x400...")
|
||||
grid.w = 400
|
||||
grid.h = 400
|
||||
|
||||
automation.screenshot("/tmp/grid_resized_large.png")
|
||||
|
||||
# Add content at the edges to test if render texture is big enough
|
||||
for x in range(15, 20):
|
||||
for y in range(15, 20):
|
||||
grid.at(x, y).color = mcrfpy.Color(0, 255, 0, 255) # Green squares
|
||||
|
||||
automation.screenshot("/tmp/grid_resized_with_edge_content.png")
|
||||
print("✓ Test 2: Grid resized to 400x400 with edge content")
|
||||
|
||||
# Test 3: Resize beyond the hardcoded 1920x1080 limit
|
||||
print("\nTest 3: Resizing grid beyond 1920x1080...")
|
||||
grid.w = 2000
|
||||
grid.h = 1200
|
||||
|
||||
automation.screenshot("/tmp/grid_resized_huge.png")
|
||||
|
||||
# This should fail with the current implementation
|
||||
print("✗ Test 3: This likely shows rendering errors due to fixed RenderTexture size")
|
||||
print("This is the bug described in Issue #9!")
|
||||
|
||||
print("\nScreenshots saved to /tmp/grid_*.png")
|
||||
print("Check grid_resized_huge.png for rendering artifacts")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Test error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Set up the test scene
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule test to run after game loop starts
|
||||
mcrfpy.setTimer("test", run_test, 100)
|
||||
Loading…
Add table
Add a link
Reference in a new issue