3D / voxel unit tests
This commit is contained in:
parent
e12e80e511
commit
71cd2b9b41
22 changed files with 4705 additions and 0 deletions
219
tests/unit/model3d_test.py
Normal file
219
tests/unit/model3d_test.py
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
# model3d_test.py - Unit test for Model3D 3D model resource
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_model3d_cube():
|
||||
"""Test Model3D.cube() creates valid model"""
|
||||
cube = mcrfpy.Model3D.cube(2.0)
|
||||
|
||||
assert cube.name == "cube", f"Expected name='cube', got '{cube.name}'"
|
||||
assert cube.vertex_count == 24, f"Expected 24 vertices, got {cube.vertex_count}"
|
||||
assert cube.triangle_count == 12, f"Expected 12 triangles, got {cube.triangle_count}"
|
||||
assert cube.has_skeleton == False, f"Expected has_skeleton=False, got {cube.has_skeleton}"
|
||||
assert cube.mesh_count == 1, f"Expected 1 mesh, got {cube.mesh_count}"
|
||||
|
||||
# Check bounds for size=2.0 cube
|
||||
bounds = cube.bounds
|
||||
assert bounds is not None, "Bounds should not be None"
|
||||
min_b, max_b = bounds
|
||||
assert min_b == (-1.0, -1.0, -1.0), f"Expected min=(-1,-1,-1), got {min_b}"
|
||||
assert max_b == (1.0, 1.0, 1.0), f"Expected max=(1,1,1), got {max_b}"
|
||||
|
||||
print("[PASS] test_model3d_cube")
|
||||
|
||||
def test_model3d_cube_default_size():
|
||||
"""Test Model3D.cube() with default size"""
|
||||
cube = mcrfpy.Model3D.cube()
|
||||
|
||||
# Default size is 1.0, so bounds should be -0.5 to 0.5
|
||||
bounds = cube.bounds
|
||||
min_b, max_b = bounds
|
||||
assert abs(min_b[0] - (-0.5)) < 0.001, f"Expected min.x=-0.5, got {min_b[0]}"
|
||||
assert abs(max_b[0] - 0.5) < 0.001, f"Expected max.x=0.5, got {max_b[0]}"
|
||||
|
||||
print("[PASS] test_model3d_cube_default_size")
|
||||
|
||||
def test_model3d_plane():
|
||||
"""Test Model3D.plane() creates valid model"""
|
||||
plane = mcrfpy.Model3D.plane(4.0, 2.0, 2)
|
||||
|
||||
assert plane.name == "plane", f"Expected name='plane', got '{plane.name}'"
|
||||
# 2 segments = 3x3 grid = 9 vertices
|
||||
assert plane.vertex_count == 9, f"Expected 9 vertices, got {plane.vertex_count}"
|
||||
# 2x2 quads = 8 triangles
|
||||
assert plane.triangle_count == 8, f"Expected 8 triangles, got {plane.triangle_count}"
|
||||
assert plane.has_skeleton == False, f"Expected has_skeleton=False"
|
||||
|
||||
# Bounds should be width/2, 0, depth/2
|
||||
bounds = plane.bounds
|
||||
min_b, max_b = bounds
|
||||
assert abs(min_b[0] - (-2.0)) < 0.001, f"Expected min.x=-2, got {min_b[0]}"
|
||||
assert abs(max_b[0] - 2.0) < 0.001, f"Expected max.x=2, got {max_b[0]}"
|
||||
assert abs(min_b[2] - (-1.0)) < 0.001, f"Expected min.z=-1, got {min_b[2]}"
|
||||
assert abs(max_b[2] - 1.0) < 0.001, f"Expected max.z=1, got {max_b[2]}"
|
||||
|
||||
print("[PASS] test_model3d_plane")
|
||||
|
||||
def test_model3d_plane_default():
|
||||
"""Test Model3D.plane() with default parameters"""
|
||||
plane = mcrfpy.Model3D.plane()
|
||||
|
||||
# Default is 1x1 with 1 segment = 4 vertices, 2 triangles
|
||||
assert plane.vertex_count == 4, f"Expected 4 vertices, got {plane.vertex_count}"
|
||||
assert plane.triangle_count == 2, f"Expected 2 triangles, got {plane.triangle_count}"
|
||||
|
||||
print("[PASS] test_model3d_plane_default")
|
||||
|
||||
def test_model3d_sphere():
|
||||
"""Test Model3D.sphere() creates valid model"""
|
||||
sphere = mcrfpy.Model3D.sphere(1.0, 8, 6)
|
||||
|
||||
assert sphere.name == "sphere", f"Expected name='sphere', got '{sphere.name}'"
|
||||
# vertices = (segments+1) * (rings+1) = 9 * 7 = 63
|
||||
assert sphere.vertex_count == 63, f"Expected 63 vertices, got {sphere.vertex_count}"
|
||||
# triangles = 2 * segments * rings = 2 * 8 * 6 = 96
|
||||
assert sphere.triangle_count == 96, f"Expected 96 triangles, got {sphere.triangle_count}"
|
||||
|
||||
# Bounds should be radius in all directions
|
||||
bounds = sphere.bounds
|
||||
min_b, max_b = bounds
|
||||
assert abs(min_b[0] - (-1.0)) < 0.001, f"Expected min.x=-1, got {min_b[0]}"
|
||||
assert abs(max_b[0] - 1.0) < 0.001, f"Expected max.x=1, got {max_b[0]}"
|
||||
|
||||
print("[PASS] test_model3d_sphere")
|
||||
|
||||
def test_model3d_sphere_default():
|
||||
"""Test Model3D.sphere() with default parameters"""
|
||||
sphere = mcrfpy.Model3D.sphere()
|
||||
|
||||
# Default radius=0.5, segments=16, rings=12
|
||||
# vertices = 17 * 13 = 221
|
||||
assert sphere.vertex_count == 221, f"Expected 221 vertices, got {sphere.vertex_count}"
|
||||
# triangles = 2 * 16 * 12 = 384
|
||||
assert sphere.triangle_count == 384, f"Expected 384 triangles, got {sphere.triangle_count}"
|
||||
|
||||
print("[PASS] test_model3d_sphere_default")
|
||||
|
||||
def test_model3d_empty():
|
||||
"""Test creating empty Model3D"""
|
||||
empty = mcrfpy.Model3D()
|
||||
|
||||
assert empty.name == "unnamed", f"Expected name='unnamed', got '{empty.name}'"
|
||||
assert empty.vertex_count == 0, f"Expected 0 vertices, got {empty.vertex_count}"
|
||||
assert empty.triangle_count == 0, f"Expected 0 triangles, got {empty.triangle_count}"
|
||||
assert empty.mesh_count == 0, f"Expected 0 meshes, got {empty.mesh_count}"
|
||||
|
||||
print("[PASS] test_model3d_empty")
|
||||
|
||||
def test_model3d_repr():
|
||||
"""Test Model3D string representation"""
|
||||
cube = mcrfpy.Model3D.cube()
|
||||
repr_str = repr(cube)
|
||||
|
||||
assert "Model3D" in repr_str, f"Expected 'Model3D' in repr, got {repr_str}"
|
||||
assert "cube" in repr_str, f"Expected 'cube' in repr, got {repr_str}"
|
||||
assert "24" in repr_str, f"Expected vertex count in repr, got {repr_str}"
|
||||
|
||||
print("[PASS] test_model3d_repr")
|
||||
|
||||
def test_entity3d_model_property():
|
||||
"""Test Entity3D.model property"""
|
||||
e = mcrfpy.Entity3D(pos=(0, 0))
|
||||
|
||||
# Initially no model
|
||||
assert e.model is None, f"Expected model=None, got {e.model}"
|
||||
|
||||
# Assign model
|
||||
cube = mcrfpy.Model3D.cube()
|
||||
e.model = cube
|
||||
assert e.model is not None, "Expected model to be set"
|
||||
assert e.model.name == "cube", f"Expected model.name='cube', got {e.model.name}"
|
||||
|
||||
# Swap model
|
||||
sphere = mcrfpy.Model3D.sphere()
|
||||
e.model = sphere
|
||||
assert e.model.name == "sphere", f"Expected model.name='sphere', got {e.model.name}"
|
||||
|
||||
# Clear model
|
||||
e.model = None
|
||||
assert e.model is None, f"Expected model=None after clearing"
|
||||
|
||||
print("[PASS] test_entity3d_model_property")
|
||||
|
||||
def test_entity3d_model_type_error():
|
||||
"""Test Entity3D.model raises TypeError for invalid input"""
|
||||
e = mcrfpy.Entity3D()
|
||||
|
||||
try:
|
||||
e.model = "not a model"
|
||||
print("[FAIL] test_entity3d_model_type_error: Expected TypeError")
|
||||
return
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
e.model = 123
|
||||
print("[FAIL] test_entity3d_model_type_error: Expected TypeError")
|
||||
return
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
print("[PASS] test_entity3d_model_type_error")
|
||||
|
||||
def test_entity3d_with_model_in_viewport():
|
||||
"""Test Entity3D with model in a Viewport3D"""
|
||||
vp = mcrfpy.Viewport3D()
|
||||
vp.set_grid_size(16, 16)
|
||||
|
||||
# Create entity with model
|
||||
cube = mcrfpy.Model3D.cube(0.5)
|
||||
e = mcrfpy.Entity3D(pos=(8, 8))
|
||||
e.model = cube
|
||||
|
||||
# Add to viewport
|
||||
vp.entities.append(e)
|
||||
|
||||
# Verify model is preserved
|
||||
retrieved = vp.entities[0]
|
||||
assert retrieved.model is not None, "Expected model to be preserved"
|
||||
assert retrieved.model.name == "cube", f"Expected model.name='cube', got {retrieved.model.name}"
|
||||
|
||||
print("[PASS] test_entity3d_with_model_in_viewport")
|
||||
|
||||
def run_all_tests():
|
||||
"""Run all Model3D tests"""
|
||||
tests = [
|
||||
test_model3d_cube,
|
||||
test_model3d_cube_default_size,
|
||||
test_model3d_plane,
|
||||
test_model3d_plane_default,
|
||||
test_model3d_sphere,
|
||||
test_model3d_sphere_default,
|
||||
test_model3d_empty,
|
||||
test_model3d_repr,
|
||||
test_entity3d_model_property,
|
||||
test_entity3d_model_type_error,
|
||||
test_entity3d_with_model_in_viewport,
|
||||
]
|
||||
|
||||
passed = 0
|
||||
failed = 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:
|
||||
print(f"[ERROR] {test.__name__}: {type(e).__name__}: {e}")
|
||||
failed += 1
|
||||
|
||||
print(f"\n=== Results: {passed} passed, {failed} failed ===")
|
||||
return failed == 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = run_all_tests()
|
||||
sys.exit(0 if success else 1)
|
||||
Loading…
Add table
Add a link
Reference in a new issue