204 lines
6.5 KiB
Python
204 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Unit tests for Vector convenience features (Issue #109)
|
|
|
|
Tests:
|
|
- Sequence protocol: indexing, negative indexing, iteration, unpacking
|
|
- Tuple comparison: Vector == tuple, Vector != tuple
|
|
- Integer conversion: .floor() method, .int property
|
|
- Boolean check: falsey for (0, 0)
|
|
"""
|
|
|
|
import mcrfpy
|
|
import sys
|
|
|
|
def approx(a, b, epsilon=1e-5):
|
|
"""Check if two floats are approximately equal (handles float32 precision)"""
|
|
return abs(a - b) < epsilon
|
|
|
|
def test_indexing():
|
|
"""Test sequence protocol indexing"""
|
|
# Use values that are exact in float32: 3.5 = 7/2, 7.5 = 15/2
|
|
v = mcrfpy.Vector(3.5, 7.5)
|
|
|
|
# Positive indices
|
|
assert v[0] == 3.5, f"v[0] should be 3.5, got {v[0]}"
|
|
assert v[1] == 7.5, f"v[1] should be 7.5, got {v[1]}"
|
|
|
|
# Negative indices
|
|
assert v[-1] == 7.5, f"v[-1] should be 7.5, got {v[-1]}"
|
|
assert v[-2] == 3.5, f"v[-2] should be 3.5, got {v[-2]}"
|
|
|
|
# Out of bounds
|
|
try:
|
|
_ = v[2]
|
|
assert False, "v[2] should raise IndexError"
|
|
except IndexError:
|
|
pass
|
|
|
|
try:
|
|
_ = v[-3]
|
|
assert False, "v[-3] should raise IndexError"
|
|
except IndexError:
|
|
pass
|
|
|
|
print(" [PASS] Indexing")
|
|
|
|
def test_length():
|
|
"""Test len() on Vector"""
|
|
v = mcrfpy.Vector(1, 2)
|
|
assert len(v) == 2, f"len(Vector) should be 2, got {len(v)}"
|
|
print(" [PASS] Length")
|
|
|
|
def test_iteration():
|
|
"""Test iteration and unpacking"""
|
|
# Use values that are exact in float32
|
|
v = mcrfpy.Vector(10.5, 20.5)
|
|
|
|
# Iteration - use approximate comparison for float32 precision
|
|
values = list(v)
|
|
assert len(values) == 2, f"list(v) should have 2 elements"
|
|
assert approx(values[0], 10.5), f"list(v)[0] should be ~10.5, got {values[0]}"
|
|
assert approx(values[1], 20.5), f"list(v)[1] should be ~20.5, got {values[1]}"
|
|
|
|
# Unpacking
|
|
x, y = v
|
|
assert approx(x, 10.5), f"Unpacked x should be ~10.5, got {x}"
|
|
assert approx(y, 20.5), f"Unpacked y should be ~20.5, got {y}"
|
|
|
|
# tuple() conversion
|
|
t = tuple(v)
|
|
assert len(t) == 2 and approx(t[0], 10.5) and approx(t[1], 20.5), f"tuple(v) should be ~(10.5, 20.5), got {t}"
|
|
|
|
print(" [PASS] Iteration and unpacking")
|
|
|
|
def test_tuple_comparison():
|
|
"""Test comparison with tuples"""
|
|
# Use integer values which are exact in float32
|
|
v = mcrfpy.Vector(5, 6)
|
|
|
|
# Vector == tuple (integers are exact)
|
|
assert v == (5, 6), "Vector(5, 6) should equal (5, 6)"
|
|
assert v == (5.0, 6.0), "Vector(5, 6) should equal (5.0, 6.0)"
|
|
|
|
# Vector != tuple
|
|
assert v != (5, 7), "Vector(5, 6) should not equal (5, 7)"
|
|
assert v != (4, 6), "Vector(5, 6) should not equal (4, 6)"
|
|
|
|
# Tuple == Vector (reverse comparison)
|
|
assert (5, 6) == v, "(5, 6) should equal Vector(5, 6)"
|
|
assert (5, 7) != v, "(5, 7) should not equal Vector(5, 6)"
|
|
|
|
# Edge cases
|
|
v_zero = mcrfpy.Vector(0, 0)
|
|
assert v_zero == (0, 0), "Vector(0, 0) should equal (0, 0)"
|
|
assert v_zero == (0.0, 0.0), "Vector(0, 0) should equal (0.0, 0.0)"
|
|
|
|
# Negative values - use exact float32 values (x.5 are exact)
|
|
v_neg = mcrfpy.Vector(-3.5, -7.5)
|
|
assert v_neg == (-3.5, -7.5), "Vector(-3.5, -7.5) should equal (-3.5, -7.5)"
|
|
|
|
print(" [PASS] Tuple comparison")
|
|
|
|
def test_floor_method():
|
|
"""Test .floor() method"""
|
|
# Use values that clearly floor to different integers
|
|
v = mcrfpy.Vector(3.75, -2.25) # exact in float32
|
|
floored = v.floor()
|
|
|
|
assert isinstance(floored, mcrfpy.Vector), ".floor() should return a Vector"
|
|
assert floored.x == 3.0, f"floor(3.75) should be 3.0, got {floored.x}"
|
|
assert floored.y == -3.0, f"floor(-2.25) should be -3.0, got {floored.y}"
|
|
|
|
# Positive values (use exact float32 values)
|
|
v2 = mcrfpy.Vector(5.875, 0.125) # exact in float32
|
|
f2 = v2.floor()
|
|
assert f2 == (5.0, 0.0), f"floor(5.875, 0.125) should be (5.0, 0.0), got ({f2.x}, {f2.y})"
|
|
|
|
# Already integers
|
|
v3 = mcrfpy.Vector(10.0, 20.0)
|
|
f3 = v3.floor()
|
|
assert f3 == (10.0, 20.0), f"floor(10.0, 20.0) should be (10.0, 20.0)"
|
|
|
|
print(" [PASS] .floor() method")
|
|
|
|
def test_int_property():
|
|
"""Test .int property"""
|
|
# Use exact float32 values
|
|
v = mcrfpy.Vector(3.75, -2.25)
|
|
int_tuple = v.int
|
|
|
|
assert isinstance(int_tuple, tuple), ".int should return a tuple"
|
|
assert len(int_tuple) == 2, ".int tuple should have 2 elements"
|
|
assert int_tuple == (3, -3), f".int should be (3, -3), got {int_tuple}"
|
|
|
|
# Check it's hashable (can be used as dict key)
|
|
d = {}
|
|
d[v.int] = "test"
|
|
assert d[(3, -3)] == "test", ".int tuple should work as dict key"
|
|
|
|
# Positive values (use exact float32 values)
|
|
v2 = mcrfpy.Vector(5.875, 0.125)
|
|
assert v2.int == (5, 0), f".int should be (5, 0), got {v2.int}"
|
|
|
|
print(" [PASS] .int property")
|
|
|
|
def test_bool_check():
|
|
"""Test boolean conversion (already implemented, verify it works)"""
|
|
v_zero = mcrfpy.Vector(0, 0)
|
|
v_nonzero = mcrfpy.Vector(1, 0)
|
|
v_nonzero2 = mcrfpy.Vector(0, 1)
|
|
|
|
assert not bool(v_zero), "Vector(0, 0) should be falsey"
|
|
assert bool(v_nonzero), "Vector(1, 0) should be truthy"
|
|
assert bool(v_nonzero2), "Vector(0, 1) should be truthy"
|
|
|
|
# In if statement
|
|
if v_zero:
|
|
assert False, "Vector(0, 0) should not pass if check"
|
|
|
|
if not v_nonzero:
|
|
assert False, "Vector(1, 0) should pass if check"
|
|
|
|
print(" [PASS] Boolean check")
|
|
|
|
def test_combined_operations():
|
|
"""Test that new features work together with existing operations"""
|
|
# Use exact float32 values
|
|
v1 = mcrfpy.Vector(3.5, 4.5)
|
|
v2 = mcrfpy.Vector(1.5, 2.5)
|
|
|
|
# Arithmetic then tuple comparison (sums are exact)
|
|
result = v1 + v2
|
|
assert result == (5.0, 7.0), f"(3.5+1.5, 4.5+2.5) should equal (5.0, 7.0), got ({result.x}, {result.y})"
|
|
|
|
# Floor then use as dict key
|
|
floored = v1.floor()
|
|
positions = {floored.int: "player"}
|
|
assert (3, 4) in positions, "floored.int should work as dict key"
|
|
|
|
# Unpack, modify, compare (products are exact)
|
|
x, y = v1
|
|
v3 = mcrfpy.Vector(x * 2, y * 2)
|
|
assert v3 == (7.0, 9.0), f"Unpacking and creating new vector should work, got ({v3.x}, {v3.y})"
|
|
|
|
print(" [PASS] Combined operations")
|
|
|
|
def run_tests():
|
|
"""Run all tests"""
|
|
print("Testing Vector convenience features (Issue #109)...")
|
|
|
|
test_indexing()
|
|
test_length()
|
|
test_iteration()
|
|
test_tuple_comparison()
|
|
test_floor_method()
|
|
test_int_property()
|
|
test_bool_check()
|
|
test_combined_operations()
|
|
|
|
print("\n[ALL TESTS PASSED]")
|
|
sys.exit(0)
|
|
|
|
# Run tests immediately (no game loop needed)
|
|
run_tests()
|