McRogueFace/tests/exhaustive_api_demo.py

1204 lines
33 KiB
Python

#!/usr/bin/env python3
"""
McRogueFace Exhaustive API Demonstration
========================================
This script demonstrates EVERY constructor variant and EVERY method
for EVERY UI object type in McRogueFace. It serves as both a test
suite and a comprehensive API reference with working examples.
The script is organized by UI object type, showing:
1. All constructor variants (empty, partial args, full args)
2. All properties (get and set)
3. All methods with different parameter combinations
4. Special behaviors and edge cases
Author: Claude
Purpose: Complete API demonstration and validation
"""
import mcrfpy
from mcrfpy import Color, Vector, Font, Texture, Frame, Caption, Sprite, Grid, Entity
import sys
# Test configuration
VERBOSE = True # Print detailed information about each test
def print_section(title):
"""Print a section header"""
print("\n" + "="*60)
print(f" {title}")
print("="*60)
def print_test(test_name, success=True):
"""Print test result"""
status = "✓ PASS" if success else "✗ FAIL"
print(f" {status} - {test_name}")
def test_color_api():
"""Test all Color constructors and methods"""
print_section("COLOR API TESTS")
# Constructor variants
print("\n Constructors:")
# Empty constructor (defaults to white)
c1 = Color()
print_test(f"Color() = ({c1.r}, {c1.g}, {c1.b}, {c1.a})")
# Single value (grayscale)
c2 = Color(128)
print_test(f"Color(128) = ({c2.r}, {c2.g}, {c2.b}, {c2.a})")
# RGB only (alpha defaults to 255)
c3 = Color(255, 128, 0)
print_test(f"Color(255, 128, 0) = ({c3.r}, {c3.g}, {c3.b}, {c3.a})")
# Full RGBA
c4 = Color(100, 150, 200, 128)
print_test(f"Color(100, 150, 200, 128) = ({c4.r}, {c4.g}, {c4.b}, {c4.a})")
# From hex string
c5 = Color.from_hex("#FF8800")
print_test(f"Color.from_hex('#FF8800') = ({c5.r}, {c5.g}, {c5.b}, {c5.a})")
c6 = Color.from_hex("#FF8800AA")
print_test(f"Color.from_hex('#FF8800AA') = ({c6.r}, {c6.g}, {c6.b}, {c6.a})")
# Methods
print("\n Methods:")
# to_hex
hex_str = c4.to_hex()
print_test(f"Color(100, 150, 200, 128).to_hex() = '{hex_str}'")
# lerp (linear interpolation)
c_start = Color(0, 0, 0)
c_end = Color(255, 255, 255)
c_mid = c_start.lerp(c_end, 0.5)
print_test(f"Black.lerp(White, 0.5) = ({c_mid.r}, {c_mid.g}, {c_mid.b}, {c_mid.a})")
# Property access
print("\n Properties:")
c = Color(10, 20, 30, 40)
print_test(f"Initial: r={c.r}, g={c.g}, b={c.b}, a={c.a}")
c.r = 200
c.g = 150
c.b = 100
c.a = 255
print_test(f"After modification: r={c.r}, g={c.g}, b={c.b}, a={c.a}")
return True
def test_vector_api():
"""Test all Vector constructors and methods"""
print_section("VECTOR API TESTS")
# Constructor variants
print("\n Constructors:")
# Empty constructor
v1 = Vector()
print_test(f"Vector() = ({v1.x}, {v1.y})")
# Single value (both x and y)
v2 = Vector(5.0)
print_test(f"Vector(5.0) = ({v2.x}, {v2.y})")
# Full x, y
v3 = Vector(10.5, 20.3)
print_test(f"Vector(10.5, 20.3) = ({v3.x}, {v3.y})")
# Methods
print("\n Methods:")
# magnitude
v = Vector(3, 4)
mag = v.magnitude()
print_test(f"Vector(3, 4).magnitude() = {mag}")
# normalize
v_norm = v.normalize()
print_test(f"Vector(3, 4).normalize() = ({v_norm.x:.3f}, {v_norm.y:.3f})")
# dot product
v_a = Vector(2, 3)
v_b = Vector(4, 5)
dot = v_a.dot(v_b)
print_test(f"Vector(2, 3).dot(Vector(4, 5)) = {dot}")
# distance_to
dist = v_a.distance_to(v_b)
print_test(f"Vector(2, 3).distance_to(Vector(4, 5)) = {dist:.3f}")
# Operators
print("\n Operators:")
# Addition
v_sum = v_a + v_b
print_test(f"Vector(2, 3) + Vector(4, 5) = ({v_sum.x}, {v_sum.y})")
# Subtraction
v_diff = v_b - v_a
print_test(f"Vector(4, 5) - Vector(2, 3) = ({v_diff.x}, {v_diff.y})")
# Multiplication (scalar)
v_mult = v_a * 2.5
print_test(f"Vector(2, 3) * 2.5 = ({v_mult.x}, {v_mult.y})")
# Division (scalar)
v_div = v_b / 2.0
print_test(f"Vector(4, 5) / 2.0 = ({v_div.x}, {v_div.y})")
# Comparison
v_eq1 = Vector(1, 2)
v_eq2 = Vector(1, 2)
v_neq = Vector(3, 4)
print_test(f"Vector(1, 2) == Vector(1, 2) = {v_eq1 == v_eq2}")
print_test(f"Vector(1, 2) != Vector(3, 4) = {v_eq1 != v_neq}")
return True
def test_frame_api():
"""Test all Frame constructors and methods"""
print_section("FRAME API TESTS")
# Create a test scene
mcrfpy.createScene("api_test")
mcrfpy.setScene("api_test")
ui = mcrfpy.sceneUI("api_test")
# Constructor variants
print("\n Constructors:")
# Empty constructor
f1 = Frame()
print_test(f"Frame() - pos=({f1.x}, {f1.y}), size=({f1.w}, {f1.h})")
ui.append(f1)
# Position only
f2 = Frame(100, 50)
print_test(f"Frame(100, 50) - pos=({f2.x}, {f2.y}), size=({f2.w}, {f2.h})")
ui.append(f2)
# Position and size
f3 = Frame(200, 100, 150, 75)
print_test(f"Frame(200, 100, 150, 75) - pos=({f3.x}, {f3.y}), size=({f3.w}, {f3.h})")
ui.append(f3)
# Full constructor
f4 = Frame(300, 200, 200, 100,
fill_color=Color(100, 100, 200),
outline_color=Color(255, 255, 0),
outline=3)
print_test("Frame with all parameters")
ui.append(f4)
# With click handler
def on_click(x, y, button):
print(f" Frame clicked at ({x}, {y}) with button {button}")
f5 = Frame(500, 300, 100, 100, click=on_click)
print_test("Frame with click handler")
ui.append(f5)
# Properties
print("\n Properties:")
# Position and size
f = Frame(10, 20, 30, 40)
print_test(f"Initial: x={f.x}, y={f.y}, w={f.w}, h={f.h}")
f.x = 50
f.y = 60
f.w = 70
f.h = 80
print_test(f"Modified: x={f.x}, y={f.y}, w={f.w}, h={f.h}")
# Colors
f.fill_color = Color(255, 0, 0, 128)
f.outline_color = Color(0, 255, 0)
f.outline = 5.0
print_test(f"Colors set, outline={f.outline}")
# Visibility and opacity
f.visible = False
f.opacity = 0.5
print_test(f"visible={f.visible}, opacity={f.opacity}")
f.visible = True # Reset
# Z-index
f.z_index = 10
print_test(f"z_index={f.z_index}")
# Children collection
child1 = Frame(5, 5, 20, 20)
child2 = Frame(30, 5, 20, 20)
f.children.append(child1)
f.children.append(child2)
print_test(f"children.count = {len(f.children)}")
# Clip children
f.clip_children = True
print_test(f"clip_children={f.clip_children}")
# Methods
print("\n Methods:")
# get_bounds
bounds = f.get_bounds()
print_test(f"get_bounds() = {bounds}")
# move
old_pos = (f.x, f.y)
f.move(10, 15)
new_pos = (f.x, f.y)
print_test(f"move(10, 15): {old_pos} -> {new_pos}")
# resize
old_size = (f.w, f.h)
f.resize(100, 120)
new_size = (f.w, f.h)
print_test(f"resize(100, 120): {old_size} -> {new_size}")
# Position tuple property
f.pos = (150, 175)
print_test(f"pos property: ({f.x}, {f.y})")
# Children collection methods
print("\n Children Collection:")
# Clear and test
f.children.extend([Frame(0, 0, 10, 10) for _ in range(3)])
print_test(f"extend() - count = {len(f.children)}")
# Index access
first_child = f.children[0]
print_test(f"children[0] = Frame at ({first_child.x}, {first_child.y})")
# Remove
f.children.remove(first_child)
print_test(f"remove() - count = {len(f.children)}")
# Iteration
count = 0
for child in f.children:
count += 1
print_test(f"iteration - counted {count} children")
return True
def test_caption_api():
"""Test all Caption constructors and methods"""
print_section("CAPTION API TESTS")
ui = mcrfpy.sceneUI("api_test")
# Constructor variants
print("\n Constructors:")
# Empty constructor
c1 = Caption()
print_test(f"Caption() - text='{c1.text}', pos=({c1.x}, {c1.y})")
ui.append(c1)
# Text only
c2 = Caption("Hello World")
print_test(f"Caption('Hello World') - pos=({c2.x}, {c2.y})")
ui.append(c2)
# Text and position
c3 = Caption("Positioned Text", 100, 50)
print_test(f"Caption('Positioned Text', 100, 50)")
ui.append(c3)
# With font (would need Font object)
# font = Font("assets/fonts/arial.ttf", 16)
# c4 = Caption("Custom Font", 200, 100, font)
# Full constructor
c5 = Caption("Styled Text", 300, 150,
fill_color=Color(255, 255, 0),
outline_color=Color(255, 0, 0),
outline=2)
print_test("Caption with all style parameters")
ui.append(c5)
# With click handler
def caption_click(x, y, button):
print(f" Caption clicked at ({x}, {y})")
c6 = Caption("Clickable", 400, 200, click=caption_click)
print_test("Caption with click handler")
ui.append(c6)
# Properties
print("\n Properties:")
c = Caption("Test Caption", 10, 20)
# Text
c.text = "Modified Text"
print_test(f"text = '{c.text}'")
# Position
c.x = 50
c.y = 60
print_test(f"position = ({c.x}, {c.y})")
# Colors and style
c.fill_color = Color(0, 255, 255)
c.outline_color = Color(255, 0, 255)
c.outline = 3.0
print_test("Colors and outline set")
# Size (read-only, computed from text)
print_test(f"size (computed) = ({c.w}, {c.h})")
# Common properties
c.visible = True
c.opacity = 0.8
c.z_index = 5
print_test(f"visible={c.visible}, opacity={c.opacity}, z_index={c.z_index}")
# Methods
print("\n Methods:")
# get_bounds
bounds = c.get_bounds()
print_test(f"get_bounds() = {bounds}")
# move
c.move(25, 30)
print_test(f"move(25, 30) - new pos = ({c.x}, {c.y})")
# Special text behaviors
print("\n Text Behaviors:")
# Empty text
c.text = ""
print_test(f"Empty text - size = ({c.w}, {c.h})")
# Multiline text
c.text = "Line 1\nLine 2\nLine 3"
print_test(f"Multiline text - size = ({c.w}, {c.h})")
# Very long text
c.text = "A" * 100
print_test(f"Long text (100 chars) - size = ({c.w}, {c.h})")
return True
def test_sprite_api():
"""Test all Sprite constructors and methods"""
print_section("SPRITE API TESTS")
ui = mcrfpy.sceneUI("api_test")
# Try to load a texture for testing
texture = None
try:
texture = Texture("assets/sprites/player.png", grid_size=(32, 32))
print_test("Texture loaded successfully")
except:
print_test("Texture load failed - using None", False)
# Constructor variants
print("\n Constructors:")
# Empty constructor
s1 = Sprite()
print_test(f"Sprite() - pos=({s1.x}, {s1.y}), sprite_index={s1.sprite_index}")
ui.append(s1)
# Position only
s2 = Sprite(100, 50)
print_test(f"Sprite(100, 50)")
ui.append(s2)
# Position and texture
s3 = Sprite(200, 100, texture)
print_test(f"Sprite(200, 100, texture)")
ui.append(s3)
# Full constructor
s4 = Sprite(300, 150, texture, sprite_index=5, scale=2.0)
print_test(f"Sprite with texture, index=5, scale=2.0")
ui.append(s4)
# With click handler
def sprite_click(x, y, button):
print(f" Sprite clicked!")
s5 = Sprite(400, 200, texture, click=sprite_click)
print_test("Sprite with click handler")
ui.append(s5)
# Properties
print("\n Properties:")
s = Sprite(10, 20, texture)
# Position
s.x = 50
s.y = 60
print_test(f"position = ({s.x}, {s.y})")
# Position tuple
s.pos = (75, 85)
print_test(f"pos tuple = ({s.x}, {s.y})")
# Sprite index
s.sprite_index = 10
print_test(f"sprite_index = {s.sprite_index}")
# Scale
s.scale = 1.5
print_test(f"scale = {s.scale}")
# Size (computed from texture and scale)
print_test(f"size (computed) = ({s.w}, {s.h})")
# Texture
s.texture = texture # Can reassign texture
print_test("Texture reassigned")
# Common properties
s.visible = True
s.opacity = 0.9
s.z_index = 3
print_test(f"visible={s.visible}, opacity={s.opacity}, z_index={s.z_index}")
# Methods
print("\n Methods:")
# get_bounds
bounds = s.get_bounds()
print_test(f"get_bounds() = {bounds}")
# move
old_pos = (s.x, s.y)
s.move(15, 20)
new_pos = (s.x, s.y)
print_test(f"move(15, 20): {old_pos} -> {new_pos}")
# Sprite animation test
print("\n Sprite Animation:")
# Test different sprite indices
for i in range(5):
s.sprite_index = i
print_test(f"Set sprite_index to {i}")
return True
def test_grid_api():
"""Test all Grid constructors and methods"""
print_section("GRID API TESTS")
ui = mcrfpy.sceneUI("api_test")
# Load texture for grid
texture = None
try:
texture = Texture("assets/sprites/tiles.png", grid_size=(16, 16))
print_test("Tile texture loaded")
except:
print_test("Tile texture load failed", False)
# Constructor variants
print("\n Constructors:")
# Empty constructor
g1 = Grid()
print_test(f"Grid() - pos=({g1.x}, {g1.y}), grid_size={g1.grid_size}")
ui.append(g1)
# Position only
g2 = Grid(100, 50)
print_test(f"Grid(100, 50)")
ui.append(g2)
# Position and grid size
g3 = Grid(200, 100, grid_size=(30, 20))
print_test(f"Grid with size (30, 20)")
ui.append(g3)
# With texture
g4 = Grid(300, 150, grid_size=(25, 15), texture=texture)
print_test("Grid with texture")
ui.append(g4)
# Full constructor
g5 = Grid(400, 200, grid_size=(20, 10), texture=texture,
tile_width=24, tile_height=24, scale=1.5)
print_test("Grid with all parameters")
ui.append(g5)
# With click handler
def grid_click(x, y, button):
print(f" Grid clicked at ({x}, {y})")
g6 = Grid(500, 250, click=grid_click)
print_test("Grid with click handler")
ui.append(g6)
# Properties
print("\n Properties:")
g = Grid(10, 20, grid_size=(40, 30))
# Position
g.x = 50
g.y = 60
print_test(f"position = ({g.x}, {g.y})")
# Grid dimensions
print_test(f"grid_size = {g.grid_size}")
print_test(f"grid_x = {g.grid_x}, grid_y = {g.grid_y}")
# Tile dimensions
g.tile_width = 20
g.tile_height = 20
print_test(f"tile size = ({g.tile_width}, {g.tile_height})")
# Scale
g.scale = 2.0
print_test(f"scale = {g.scale}")
# Texture
g.texture = texture
print_test("Texture assigned")
# Fill color
g.fill_color = Color(30, 30, 50)
print_test("Fill color set")
# Camera properties
g.center = (20.0, 15.0)
print_test(f"center (camera) = {g.center}")
g.zoom = 1.5
print_test(f"zoom = {g.zoom}")
# Common properties
g.visible = True
g.opacity = 0.95
g.z_index = 1
print_test(f"visible={g.visible}, opacity={g.opacity}, z_index={g.z_index}")
# Grid point access
print("\n Grid Points:")
# Access grid point
point = g.at(5, 5)
print_test(f"at(5, 5) returned GridPoint")
# Modify grid point
point.tilesprite = 10
point.tile_overlay = 2
point.walkable = False
point.transparent = True
point.color = Color(255, 0, 0, 128)
print_test("GridPoint properties modified")
# Check modifications
print_test(f" tilesprite = {point.tilesprite}")
print_test(f" walkable = {point.walkable}")
print_test(f" transparent = {point.transparent}")
# Entity collection
print("\n Entity Collection:")
# Create entities
if texture:
e1 = Entity(10.5, 10.5, texture, sprite_index=5)
e2 = Entity(15.0, 12.0, texture, sprite_index=8)
g.entities.append(e1)
g.entities.append(e2)
print_test(f"Added 2 entities, count = {len(g.entities)}")
# Access entities
first = g.entities[0]
print_test(f"entities[0] at ({first.x}, {first.y})")
# Iterate entities
count = 0
for entity in g.entities:
count += 1
print_test(f"Iterated {count} entities")
# Methods
print("\n Methods:")
# get_bounds
bounds = g.get_bounds()
print_test(f"get_bounds() = {bounds}")
# move
g.move(20, 25)
print_test(f"move(20, 25) - new pos = ({g.x}, {g.y})")
# Points array access
print("\n Points Array:")
# The points property is a 2D array
all_points = g.points
print_test(f"points array dimensions: {len(all_points)}x{len(all_points[0]) if all_points else 0}")
# Modify multiple points
for y in range(5):
for x in range(5):
pt = g.at(x, y)
pt.tilesprite = x + y * 5
pt.color = Color(x * 50, y * 50, 100)
print_test("Modified 5x5 area of grid")
return True
def test_entity_api():
"""Test all Entity constructors and methods"""
print_section("ENTITY API TESTS")
# Entities need to be in a grid
ui = mcrfpy.sceneUI("api_test")
# Create grid and texture
texture = None
try:
texture = Texture("assets/sprites/entities.png", grid_size=(32, 32))
print_test("Entity texture loaded")
except:
print_test("Entity texture load failed", False)
grid = Grid(50, 50, grid_size=(30, 30), texture=texture)
ui.append(grid)
# Constructor variants
print("\n Constructors:")
# Empty constructor
e1 = Entity()
print_test(f"Entity() - pos=({e1.x}, {e1.y}), sprite_index={e1.sprite_index}")
grid.entities.append(e1)
# Position only
e2 = Entity(5.5, 3.5)
print_test(f"Entity(5.5, 3.5)")
grid.entities.append(e2)
# Position and texture
e3 = Entity(10.0, 8.0, texture)
print_test("Entity with texture")
grid.entities.append(e3)
# Full constructor
e4 = Entity(15.5, 12.5, texture, sprite_index=7, scale=1.5)
print_test("Entity with all parameters")
grid.entities.append(e4)
# Properties
print("\n Properties:")
e = Entity(20.0, 15.0, texture, sprite_index=3)
grid.entities.append(e)
# Position (float coordinates in grid space)
e.x = 22.5
e.y = 16.5
print_test(f"position = ({e.x}, {e.y})")
# Position tuple
e.position = (24.0, 18.0)
print_test(f"position tuple = {e.position}")
# Sprite index
e.sprite_index = 12
print_test(f"sprite_index = {e.sprite_index}")
# Scale
e.scale = 2.0
print_test(f"scale = {e.scale}")
# Methods
print("\n Methods:")
# index() - get position in entity collection
idx = e.index()
print_test(f"index() in collection = {idx}")
# Gridstate (visibility per grid cell)
print("\n Grid State:")
# Access gridstate
if len(e.gridstate) > 0:
state = e.gridstate[0]
print_test(f"gridstate[0] - visible={state.visible}, discovered={state.discovered}")
# Modify visibility
state.visible = True
state.discovered = True
print_test("Modified gridstate visibility")
# at() method - check if entity occupies a grid point
# This would need a GridPointState object
# occupied = e.at(some_gridpoint_state)
# die() method - remove from grid
print("\n Entity Lifecycle:")
# Create temporary entity
temp_entity = Entity(25.0, 25.0, texture)
grid.entities.append(temp_entity)
count_before = len(grid.entities)
# Remove it
temp_entity.die()
count_after = len(grid.entities)
print_test(f"die() - entity count: {count_before} -> {count_after}")
# Entity movement
print("\n Entity Movement:")
# Test fractional positions (entities can be between grid cells)
e.position = (10.0, 10.0)
print_test(f"Integer position: {e.position}")
e.position = (10.5, 10.5)
print_test(f"Center of cell: {e.position}")
e.position = (10.25, 10.75)
print_test(f"Fractional position: {e.position}")
return True
def test_collections():
"""Test UICollection and EntityCollection behaviors"""
print_section("COLLECTION API TESTS")
ui = mcrfpy.sceneUI("api_test")
# Test UICollection (scene UI and frame children)
print("\n UICollection (Scene UI):")
# Clear scene
while len(ui) > 0:
ui.remove(ui[0])
print_test(f"Cleared - length = {len(ui)}")
# append
f1 = Frame(10, 10, 50, 50)
ui.append(f1)
print_test(f"append() - length = {len(ui)}")
# extend
frames = [Frame(x * 60, 10, 50, 50) for x in range(1, 4)]
ui.extend(frames)
print_test(f"extend() with 3 items - length = {len(ui)}")
# index access
item = ui[0]
print_test(f"ui[0] = Frame at ({item.x}, {item.y})")
# slice access
slice_items = ui[1:3]
print_test(f"ui[1:3] returned {len(slice_items)} items")
# index() method
idx = ui.index(f1)
print_test(f"index(frame) = {idx}")
# count() method
cnt = ui.count(f1)
print_test(f"count(frame) = {cnt}")
# in operator
contains = f1 in ui
print_test(f"frame in ui = {contains}")
# iteration
count = 0
for item in ui:
count += 1
print_test(f"Iteration counted {count} items")
# remove
ui.remove(f1)
print_test(f"remove() - length = {len(ui)}")
# Test Frame.children collection
print("\n UICollection (Frame Children):")
parent = Frame(100, 100, 300, 200)
ui.append(parent)
# Add children
child1 = Caption("Child 1", 10, 10)
child2 = Caption("Child 2", 10, 30)
child3 = Frame(10, 50, 50, 50)
parent.children.append(child1)
parent.children.append(child2)
parent.children.append(child3)
print_test(f"Added 3 children - count = {len(parent.children)}")
# Mixed types in collection
has_caption = any(isinstance(child, Caption) for child in parent.children)
has_frame = any(isinstance(child, Frame) for child in parent.children)
print_test(f"Mixed types: has Caption = {has_caption}, has Frame = {has_frame}")
# Test EntityCollection
print("\n EntityCollection (Grid Entities):")
texture = None
try:
texture = Texture("assets/sprites/entities.png", grid_size=(32, 32))
except:
pass
grid = Grid(400, 100, grid_size=(20, 20), texture=texture)
ui.append(grid)
# Add entities
entities = []
for i in range(5):
e = Entity(float(i * 2), float(i * 2), texture, sprite_index=i)
grid.entities.append(e)
entities.append(e)
print_test(f"Added 5 entities - count = {len(grid.entities)}")
# Access and iteration
first_entity = grid.entities[0]
print_test(f"entities[0] at ({first_entity.x}, {first_entity.y})")
# Remove entity
grid.entities.remove(first_entity)
print_test(f"Removed entity - count = {len(grid.entities)}")
return True
def test_animation_api():
"""Test Animation class API"""
print_section("ANIMATION API TESTS")
ui = mcrfpy.sceneUI("api_test")
# Import Animation
from mcrfpy import Animation
print("\n Animation Constructors:")
# Basic animation
anim1 = Animation("x", 100.0, 2.0)
print_test("Animation('x', 100.0, 2.0)")
# With easing
anim2 = Animation("y", 200.0, 3.0, "easeInOut")
print_test("Animation with easing='easeInOut'")
# Delta mode
anim3 = Animation("w", 50.0, 1.5, "linear", delta=True)
print_test("Animation with delta=True")
# Color animation
anim4 = Animation("fill_color", Color(255, 0, 0), 2.0)
print_test("Animation with Color target")
# Vector animation
anim5 = Animation("position", (10.0, 20.0), 2.5, "easeOutBounce")
print_test("Animation with position tuple")
# Sprite sequence
anim6 = Animation("sprite_index", [0, 1, 2, 3, 2, 1], 2.0)
print_test("Animation with sprite sequence")
# Properties
print("\n Animation Properties:")
# Check properties
print_test(f"property = '{anim1.property}'")
print_test(f"duration = {anim1.duration}")
print_test(f"elapsed = {anim1.elapsed}")
print_test(f"is_complete = {anim1.is_complete}")
print_test(f"is_delta = {anim3.is_delta}")
# Methods
print("\n Animation Methods:")
# Create test frame
frame = Frame(50, 50, 100, 100)
frame.fill_color = Color(100, 100, 100)
ui.append(frame)
# Start animation
anim1.start(frame)
print_test("start() called on frame")
# Get current value (before update)
current = anim1.get_current_value()
print_test(f"get_current_value() = {current}")
# Manual update (usually automatic)
anim1.update(0.5) # 0.5 seconds
print_test("update(0.5) called")
# Check elapsed time
print_test(f"elapsed after update = {anim1.elapsed}")
# All easing functions
print("\n Available Easing Functions:")
easings = [
"linear", "easeIn", "easeOut", "easeInOut",
"easeInQuad", "easeOutQuad", "easeInOutQuad",
"easeInCubic", "easeOutCubic", "easeInOutCubic",
"easeInQuart", "easeOutQuart", "easeInOutQuart",
"easeInSine", "easeOutSine", "easeInOutSine",
"easeInExpo", "easeOutExpo", "easeInOutExpo",
"easeInCirc", "easeOutCirc", "easeInOutCirc",
"easeInElastic", "easeOutElastic", "easeInOutElastic",
"easeInBack", "easeOutBack", "easeInOutBack",
"easeInBounce", "easeOutBounce", "easeInOutBounce"
]
# Test creating animation with each easing
for easing in easings[:10]: # Test first 10
try:
test_anim = Animation("x", 100.0, 1.0, easing)
print_test(f"Easing '{easing}'")
except:
print_test(f"Easing '{easing}' failed", False)
return True
def test_scene_api():
"""Test scene-related API functions"""
print_section("SCENE API TESTS")
print("\n Scene Management:")
# Create scene
mcrfpy.createScene("test_scene_1")
print_test("createScene('test_scene_1')")
mcrfpy.createScene("test_scene_2")
print_test("createScene('test_scene_2')")
# Set active scene
mcrfpy.setScene("test_scene_1")
print_test("setScene('test_scene_1')")
# Get scene UI
ui1 = mcrfpy.sceneUI("test_scene_1")
print_test(f"sceneUI('test_scene_1') - collection size = {len(ui1)}")
ui2 = mcrfpy.sceneUI("test_scene_2")
print_test(f"sceneUI('test_scene_2') - collection size = {len(ui2)}")
# Add content to scenes
ui1.append(Frame(10, 10, 100, 100))
ui1.append(Caption("Scene 1", 10, 120))
print_test(f"Added content to scene 1 - size = {len(ui1)}")
ui2.append(Frame(20, 20, 150, 150))
ui2.append(Caption("Scene 2", 20, 180))
print_test(f"Added content to scene 2 - size = {len(ui2)}")
# Scene transitions
print("\n Scene Transitions:")
# Note: Actual transition types would need to be tested visually
# TransitionType enum: None, Fade, SlideLeft, SlideRight, SlideUp, SlideDown
# Keypress handling
print("\n Input Handling:")
def test_keypress(scene_name, keycode):
print(f" Key pressed in {scene_name}: {keycode}")
mcrfpy.keypressScene("test_scene_1", test_keypress)
print_test("keypressScene() handler registered")
return True
def test_audio_api():
"""Test audio-related API functions"""
print_section("AUDIO API TESTS")
print("\n Sound Functions:")
# Create sound buffer
try:
mcrfpy.createSoundBuffer("test_sound", "assets/audio/click.wav")
print_test("createSoundBuffer('test_sound', 'click.wav')")
# Play sound
mcrfpy.playSound("test_sound")
print_test("playSound('test_sound')")
# Set volume
mcrfpy.setVolume("test_sound", 0.5)
print_test("setVolume('test_sound', 0.5)")
except Exception as e:
print_test(f"Audio functions failed: {e}", False)
return True
def test_timer_api():
"""Test timer API functions"""
print_section("TIMER API TESTS")
print("\n Timer Functions:")
# Timer callback
def timer_callback(runtime):
print(f" Timer fired at runtime: {runtime}")
# Set timer
mcrfpy.setTimer("test_timer", timer_callback, 1000) # 1 second
print_test("setTimer('test_timer', callback, 1000)")
# Delete timer
mcrfpy.delTimer("test_timer")
print_test("delTimer('test_timer')")
# Multiple timers
mcrfpy.setTimer("timer1", lambda r: print(f" Timer 1: {r}"), 500)
mcrfpy.setTimer("timer2", lambda r: print(f" Timer 2: {r}"), 750)
mcrfpy.setTimer("timer3", lambda r: print(f" Timer 3: {r}"), 1000)
print_test("Set 3 timers with different intervals")
# Clean up
mcrfpy.delTimer("timer1")
mcrfpy.delTimer("timer2")
mcrfpy.delTimer("timer3")
print_test("Cleaned up all timers")
return True
def test_edge_cases():
"""Test edge cases and error conditions"""
print_section("EDGE CASES AND ERROR HANDLING")
ui = mcrfpy.sceneUI("api_test")
print("\n Boundary Values:")
# Negative positions
f = Frame(-100, -50, 50, 50)
print_test(f"Negative position: ({f.x}, {f.y})")
# Zero size
f2 = Frame(0, 0, 0, 0)
print_test(f"Zero size: ({f2.w}, {f2.h})")
# Very large values
f3 = Frame(10000, 10000, 5000, 5000)
print_test(f"Large values: pos=({f3.x}, {f3.y}), size=({f3.w}, {f3.h})")
# Opacity bounds
f.opacity = -0.5
print_test(f"Opacity below 0: {f.opacity}")
f.opacity = 2.0
print_test(f"Opacity above 1: {f.opacity}")
# Color component bounds
c = Color(300, -50, 1000, 128)
print_test(f"Color out of bounds: ({c.r}, {c.g}, {c.b}, {c.a})")
print("\n Empty Collections:")
# Empty children
frame = Frame(0, 0, 100, 100)
print_test(f"Empty children collection: {len(frame.children)}")
# Access empty collection
try:
item = frame.children[0]
print_test("Accessing empty collection[0]", False)
except IndexError:
print_test("Accessing empty collection[0] raises IndexError")
print("\n Invalid Operations:")
# Grid without texture
g = Grid(0, 0, grid_size=(10, 10))
point = g.at(5, 5)
point.tilesprite = 10 # No texture to reference
print_test("Set tilesprite without texture")
# Entity without grid
e = Entity(5.0, 5.0)
# e.die() would fail if not in a grid
print_test("Created entity without grid")
return True
def run_all_tests():
"""Run all API tests"""
print("\n" + "="*60)
print(" McRogueFace Exhaustive API Test Suite")
print(" Testing every constructor and method...")
print("="*60)
# Run each test category
test_functions = [
test_color_api,
test_vector_api,
test_frame_api,
test_caption_api,
test_sprite_api,
test_grid_api,
test_entity_api,
test_collections,
test_animation_api,
test_scene_api,
test_audio_api,
test_timer_api,
test_edge_cases
]
passed = 0
failed = 0
for test_func in test_functions:
try:
if test_func():
passed += 1
else:
failed += 1
except Exception as e:
print(f"\n ERROR in {test_func.__name__}: {e}")
failed += 1
# Summary
print("\n" + "="*60)
print(f" TEST SUMMARY: {passed} passed, {failed} failed")
print("="*60)
# Visual test scene
print("\n Visual elements are displayed in the 'api_test' scene.")
print(" The test is complete. Press ESC to exit.")
def handle_exit(scene_name, keycode):
"""Handle ESC key to exit"""
if keycode == 256: # ESC
print("\nExiting API test suite...")
sys.exit(0)
# Set up exit handler
mcrfpy.keypressScene("api_test", handle_exit)
# Run after short delay to ensure scene is ready
def start_tests(runtime):
run_all_tests()
mcrfpy.setTimer("start_tests", start_tests, 100)
print("Starting McRogueFace Exhaustive API Demo...")
print("This will test EVERY constructor and method.")
print("Press ESC to exit at any time.")