"""Tests der eigenen Vektorklasse.""" import copy import math import pickle import pytest from sandbox.physics.vector import ZERO, Vector2 def test_addition_and_subtraction() -> None: assert Vector2(1, 2) + Vector2(3, -5) == Vector2(4, -3) assert Vector2(1, 2) - Vector2(3, -5) == Vector2(-2, 7) def test_scaling_from_both_sides_and_division() -> None: assert Vector2(1.5, -2) * 2 == Vector2(3, -4) assert 2 * Vector2(1.5, -2) == Vector2(3, -4) assert Vector2(3, -4) / 2 == Vector2(1.5, -2) def test_negation() -> None: assert -Vector2(1, -2) == Vector2(-1, 2) def test_dot_product() -> None: assert Vector2(1, 2).dot(Vector2(3, 4)) == 11 assert Vector2(1, 0).dot(Vector2(0, 5)) == 0 # orthogonal def test_cross_product_sign_and_value() -> None: assert Vector2(1, 0).cross(Vector2(0, 1)) == 1 assert Vector2(0, 1).cross(Vector2(1, 0)) == -1 assert Vector2(2, 3).cross(Vector2(4, 6)) == 0 # parallel def test_length_and_length_squared() -> None: v = Vector2(3, 4) assert v.length() == 5 assert v.length_squared() == 25 def test_normalized_has_unit_length_and_same_direction() -> None: n = Vector2(3, 4).normalized() assert n.length() == pytest.approx(1.0) assert n.is_close(Vector2(0.6, 0.8)) def test_normalizing_the_zero_vector_returns_zero_instead_of_nan() -> None: assert Vector2(0, 0).normalized() == ZERO def test_perpendicular_is_rotated_by_plus_90_degrees() -> None: v = Vector2(2, 1) p = v.perpendicular() assert p == Vector2(-1, 2) assert v.dot(p) == 0 assert v.cross(p) > 0 def test_distance() -> None: assert Vector2(1, 1).distance_to(Vector2(4, 5)) == 5 def test_clamped_limits_only_long_vectors() -> None: short = Vector2(1, 1) assert short.clamped(10) is short clamped = Vector2(30, 40).clamped(10) assert clamped.length() == pytest.approx(10) assert clamped.is_close(Vector2(6, 8)) def test_lerp() -> None: a = Vector2(0, 10) b = Vector2(10, 20) assert a.lerp(b, 0) == a assert a.lerp(b, 1) == b assert a.lerp(b, 0.5) == Vector2(5, 15) def test_is_finite() -> None: assert Vector2(1, 2).is_finite() assert not Vector2(math.nan, 0).is_finite() assert not Vector2(0, math.inf).is_finite() def test_is_close_uses_absolute_tolerance() -> None: assert Vector2(1, 1).is_close(Vector2(1 + 1e-12, 1)) assert not Vector2(1, 1).is_close(Vector2(1.1, 1)) assert Vector2(1, 1).is_close(Vector2(1.1, 1), tolerance=0.2) def test_equality_hash_and_comparison_with_other_types() -> None: assert Vector2(1, 2) == Vector2(1.0, 2.0) assert Vector2(1, 2) != Vector2(2, 1) assert Vector2(1, 2) != (1, 2) assert len({Vector2(1, 2), Vector2(1, 2), Vector2(2, 1)}) == 2 def test_vectors_are_immutable() -> None: v = Vector2(1, 2) with pytest.raises(AttributeError): v.x = 5 # type: ignore[misc] with pytest.raises(AttributeError): del v.y assert v == Vector2(1, 2) def test_operations_do_not_modify_operands() -> None: a = Vector2(1, 2) b = Vector2(3, 4) _ = (a + b, a - b, a * 3, a / 2, -a, a.normalized(), a.clamped(0.1), a.lerp(b, 0.5)) assert a == Vector2(1, 2) assert b == Vector2(3, 4) def test_copy_and_pickle_round_trip() -> None: v = Vector2(1.5, -2.5) assert copy.copy(v) == v assert copy.deepcopy(v) == v assert pickle.loads(pickle.dumps(v)) == v def test_repr_and_tuple() -> None: assert repr(Vector2(1.5, 2)) == "Vector2(1.5, 2)" assert Vector2(1.5, 2).to_tuple() == (1.5, 2)