95 lines
3.4 KiB
Python
95 lines
3.4 KiB
Python
# --- Tests for standard rotations (Happy Path) ---
|
|
import math
|
|
|
|
import pytest
|
|
from _pytest.python_api import approx
|
|
|
|
from src.matrixmania.compute import rot_3D
|
|
|
|
|
|
# --- HELPER FUNCTION ---
|
|
def assert_matrices_3x3_close(m1, m2, abs_tol=1e-9):
|
|
"""
|
|
Helper function to compare two 3x3 matrices element-wise
|
|
using math.isclose for float comparison.
|
|
"""
|
|
assert len(m1) == 3 and all(len(row) == 3 for row in m1), "Matrix 1 is not 3x3"
|
|
assert len(m2) == 3 and all(len(row) == 3 for row in m2), "Matrix 2 is not 3x3"
|
|
|
|
for i in range(3):
|
|
for j in range(3):
|
|
assert math.isclose(m1[i][j], m2[i][j], abs_tol=abs_tol), \
|
|
f"Mismatch at [{i}][{j}]: {m1[i][j]} != {m2[i][j]}"
|
|
# --- END HELPER FUNCTION ---
|
|
|
|
|
|
def test_90_deg_rotation_x_axis():
|
|
"""Tests a 90-degree (pi/2) rotation around the X-axis."""
|
|
# cos(pi/2) = 0, sin(pi/2) = 1
|
|
# Expected: [[1, 0, 0], [0, 0, -1], [0, 1, 0]]
|
|
expected = [[1.0, 0.0, 0.0], [0.0, 0.0, -1.0], [0.0, 1.0, 0.0]]
|
|
assert_matrices_3x3_close(rot_3D(math.pi / 2, "x"), expected)
|
|
|
|
|
|
def test_90_deg_rotation_y_axis():
|
|
"""Tests a 90-degree (pi/2) rotation around the Y-axis."""
|
|
# cos(pi/2) = 0, sin(pi/2) = 1
|
|
# Expected: [[0, 0, 1], [0, 1, 0], [-1, 0, 0]]
|
|
expected = [[0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [-1.0, 0.0, 0.0]]
|
|
assert_matrices_3x3_close(rot_3D(math.pi / 2, "y"), expected)
|
|
|
|
|
|
def test_90_deg_rotation_z_axis():
|
|
"""Tests a 90-degree (pi/2) rotation around the Z-axis."""
|
|
# cos(pi/2) = 0, sin(pi/2) = 1
|
|
# Expected: [[0, -1, 0], [1, 0, 0], [0, 0, 1]]
|
|
expected = [[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]]
|
|
assert_matrices_3x3_close(rot_3D(math.pi / 2, "z"), expected)
|
|
|
|
|
|
def test_0_radian_rotation():
|
|
"""Tests 0-radian rotation for all axes (should return identity matrix)."""
|
|
identity = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
|
|
assert_matrices_3x3_close(rot_3D(0, "x"), identity)
|
|
assert_matrices_3x3_close(rot_3D(0.0, "y"), identity)
|
|
assert_matrices_3x3_close(rot_3D(0, "z"), identity)
|
|
|
|
|
|
def test_pi_rotation_y_axis():
|
|
"""Tests a 180-degree (pi) rotation around Y."""
|
|
# cos(pi) = -1, sin(pi) = 0
|
|
# Expected: [[-1, 0, 0], [0, 1, 0], [0, 0, -1]]
|
|
expected = [[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]]
|
|
assert_matrices_3x3_close(rot_3D(math.pi, "y"), expected)
|
|
|
|
|
|
# --- Tests for error handling (Invalid Inputs) ---
|
|
|
|
def test_error_invalid_axis_string():
|
|
"""Tests that a ValueError is raised for an invalid axis string."""
|
|
with pytest.raises(ValueError, match="axis must be either x, y or z"):
|
|
rot_3D(math.pi / 2, "a")
|
|
|
|
|
|
def test_error_invalid_axis_case():
|
|
"""Tests that the axis check is case-sensitive."""
|
|
with pytest.raises(ValueError, match="axis must be either x, y or z"):
|
|
rot_3D(math.pi / 2, "X") # 'X' is not 'x'
|
|
|
|
|
|
def test_error_invalid_axis_type():
|
|
"""Tests that a ValueError is raised if the axis is not a string."""
|
|
with pytest.raises(ValueError, match="axis must be a string"):
|
|
rot_3D(math.pi / 2, 1)
|
|
|
|
|
|
def test_error_invalid_angle_type():
|
|
"""Tests that a ValueError is raised if the angle is not numeric."""
|
|
with pytest.raises(ValueError, match="instance of a numeric datatype"):
|
|
rot_3D("pi", "x")
|
|
|
|
|
|
def test_error_invalid_angle_type_none():
|
|
"""Tests that a ValueError is raised if the angle is None."""
|
|
with pytest.raises(ValueError, match="instance of a numeric datatype"):
|
|
rot_3D(None, "z") |