2025-11-13 13:29:16 +01:00

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")