McRogueFace/tests/unit/billboard_test.py
2026-02-06 16:15:07 -05:00

260 lines
8.2 KiB
Python

# billboard_test.py - Unit test for Billboard 3D camera-facing sprites
import mcrfpy
import sys
def test_billboard_creation():
"""Test basic Billboard creation and default properties"""
bb = mcrfpy.Billboard()
# Default sprite index
assert bb.sprite_index == 0, f"Expected sprite_index=0, got {bb.sprite_index}"
# Default position
assert bb.pos == (0.0, 0.0, 0.0), f"Expected pos=(0,0,0), got {bb.pos}"
# Default scale
assert bb.scale == 1.0, f"Expected scale=1.0, got {bb.scale}"
# Default facing mode
assert bb.facing == "camera_y", f"Expected facing='camera_y', got {bb.facing}"
# Default theta/phi (for fixed mode)
assert bb.theta == 0.0, f"Expected theta=0.0, got {bb.theta}"
assert bb.phi == 0.0, f"Expected phi=0.0, got {bb.phi}"
# Default opacity and visibility
assert bb.opacity == 1.0, f"Expected opacity=1.0, got {bb.opacity}"
assert bb.visible == True, f"Expected visible=True, got {bb.visible}"
print("[PASS] test_billboard_creation")
def test_billboard_with_kwargs():
"""Test Billboard creation with keyword arguments"""
bb = mcrfpy.Billboard(
sprite_index=5,
pos=(10.0, 5.0, -3.0),
scale=2.5,
facing="camera",
opacity=0.8
)
assert bb.sprite_index == 5, f"Expected sprite_index=5, got {bb.sprite_index}"
assert bb.pos == (10.0, 5.0, -3.0), f"Expected pos=(10,5,-3), got {bb.pos}"
assert bb.scale == 2.5, f"Expected scale=2.5, got {bb.scale}"
assert bb.facing == "camera", f"Expected facing='camera', got {bb.facing}"
assert abs(bb.opacity - 0.8) < 0.001, f"Expected opacity~=0.8, got {bb.opacity}"
print("[PASS] test_billboard_with_kwargs")
def test_billboard_facing_modes():
"""Test all Billboard facing modes"""
bb = mcrfpy.Billboard()
# Test camera mode (full rotation to face camera)
bb.facing = "camera"
assert bb.facing == "camera", f"Expected facing='camera', got {bb.facing}"
# Test camera_y mode (only Y-axis rotation, stays upright)
bb.facing = "camera_y"
assert bb.facing == "camera_y", f"Expected facing='camera_y', got {bb.facing}"
# Test fixed mode (uses theta/phi angles)
bb.facing = "fixed"
assert bb.facing == "fixed", f"Expected facing='fixed', got {bb.facing}"
print("[PASS] test_billboard_facing_modes")
def test_billboard_fixed_rotation():
"""Test fixed mode rotation angles (theta/phi)"""
bb = mcrfpy.Billboard(facing="fixed")
# Set theta (horizontal rotation)
bb.theta = 1.5708 # ~90 degrees
assert abs(bb.theta - 1.5708) < 0.001, f"Expected theta~=1.5708, got {bb.theta}"
# Set phi (vertical tilt)
bb.phi = 0.7854 # ~45 degrees
assert abs(bb.phi - 0.7854) < 0.001, f"Expected phi~=0.7854, got {bb.phi}"
print("[PASS] test_billboard_fixed_rotation")
def test_billboard_property_modification():
"""Test modifying Billboard properties after creation"""
bb = mcrfpy.Billboard()
# Modify position
bb.pos = (5.0, 10.0, 15.0)
assert bb.pos == (5.0, 10.0, 15.0), f"Expected pos=(5,10,15), got {bb.pos}"
# Modify sprite index
bb.sprite_index = 42
assert bb.sprite_index == 42, f"Expected sprite_index=42, got {bb.sprite_index}"
# Modify scale
bb.scale = 0.5
assert bb.scale == 0.5, f"Expected scale=0.5, got {bb.scale}"
# Modify opacity
bb.opacity = 0.25
assert abs(bb.opacity - 0.25) < 0.001, f"Expected opacity~=0.25, got {bb.opacity}"
# Modify visibility
bb.visible = False
assert bb.visible == False, f"Expected visible=False, got {bb.visible}"
print("[PASS] test_billboard_property_modification")
def test_billboard_opacity_clamping():
"""Test that opacity is clamped to 0-1 range"""
bb = mcrfpy.Billboard()
# Test upper clamp
bb.opacity = 2.0
assert bb.opacity == 1.0, f"Expected opacity=1.0 after clamping, got {bb.opacity}"
# Test lower clamp
bb.opacity = -0.5
assert bb.opacity == 0.0, f"Expected opacity=0.0 after clamping, got {bb.opacity}"
print("[PASS] test_billboard_opacity_clamping")
def test_billboard_repr():
"""Test Billboard string representation"""
bb = mcrfpy.Billboard(pos=(1.0, 2.0, 3.0), sprite_index=7, facing="camera")
repr_str = repr(bb)
# Check that repr contains expected information
assert "Billboard" in repr_str, f"Expected 'Billboard' in repr, got {repr_str}"
print("[PASS] test_billboard_repr")
def test_billboard_with_texture():
"""Test Billboard with texture assignment"""
# Use default_texture which is always available
tex = mcrfpy.default_texture
bb = mcrfpy.Billboard(texture=tex, sprite_index=0)
# Verify texture is assigned
assert bb.texture is not None, "Expected texture to be assigned"
assert bb.sprite_index == 0, f"Expected sprite_index=0, got {bb.sprite_index}"
# Change sprite index
bb.sprite_index = 10
assert bb.sprite_index == 10, f"Expected sprite_index=10, got {bb.sprite_index}"
# Test assigning texture via property
bb2 = mcrfpy.Billboard()
assert bb2.texture is None, "Expected no texture initially"
bb2.texture = tex
assert bb2.texture is not None, "Expected texture after assignment"
# Test setting texture to None
bb2.texture = None
assert bb2.texture is None, "Expected None after clearing texture"
print("[PASS] test_billboard_with_texture")
def test_viewport3d_billboard_methods():
"""Test Viewport3D billboard management methods"""
vp = mcrfpy.Viewport3D()
# Initial count should be 0
assert vp.billboard_count() == 0, f"Expected 0, got {vp.billboard_count()}"
# Add billboards
bb1 = mcrfpy.Billboard(pos=(1, 0, 1), scale=1.0)
vp.add_billboard(bb1)
assert vp.billboard_count() == 1, f"Expected 1, got {vp.billboard_count()}"
bb2 = mcrfpy.Billboard(pos=(2, 0, 2), scale=0.5)
vp.add_billboard(bb2)
assert vp.billboard_count() == 2, f"Expected 2, got {vp.billboard_count()}"
# Get billboard by index
retrieved = vp.get_billboard(0)
assert retrieved.pos == (1.0, 0.0, 1.0), f"Expected (1,0,1), got {retrieved.pos}"
# Modify retrieved billboard
retrieved.pos = (5, 1, 5)
assert retrieved.pos == (5.0, 1.0, 5.0), f"Expected (5,1,5), got {retrieved.pos}"
# Clear all billboards
vp.clear_billboards()
assert vp.billboard_count() == 0, f"Expected 0 after clear, got {vp.billboard_count()}"
print("[PASS] test_viewport3d_billboard_methods")
def test_viewport3d_billboard_index_bounds():
"""Test get_billboard index bounds checking"""
vp = mcrfpy.Viewport3D()
# Empty viewport - any index should fail
try:
vp.get_billboard(0)
assert False, "Should have raised IndexError"
except IndexError:
pass
# Add one billboard
bb = mcrfpy.Billboard()
vp.add_billboard(bb)
# Index 0 should work
vp.get_billboard(0)
# Index 1 should fail
try:
vp.get_billboard(1)
assert False, "Should have raised IndexError"
except IndexError:
pass
# Negative index should fail
try:
vp.get_billboard(-1)
assert False, "Should have raised IndexError"
except IndexError:
pass
print("[PASS] test_viewport3d_billboard_index_bounds")
def run_all_tests():
"""Run all Billboard tests"""
tests = [
test_billboard_creation,
test_billboard_with_kwargs,
test_billboard_facing_modes,
test_billboard_fixed_rotation,
test_billboard_property_modification,
test_billboard_opacity_clamping,
test_billboard_repr,
test_billboard_with_texture,
test_viewport3d_billboard_methods,
test_viewport3d_billboard_index_bounds,
]
passed = 0
failed = 0
skipped = 0
for test in tests:
try:
test()
passed += 1
except AssertionError as e:
print(f"[FAIL] {test.__name__}: {e}")
failed += 1
except Exception as e:
if "[SKIP]" in str(e):
skipped += 1
else:
print(f"[ERROR] {test.__name__}: {e}")
failed += 1
print(f"\n=== Results: {passed} passed, {failed} failed, {skipped} skipped ===")
return failed == 0
if __name__ == "__main__":
success = run_all_tests()
sys.exit(0 if success else 1)