Coverage for tests \ test_vector_field_operations.py: 100%
82 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-27 03:59 -0400
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-27 03:59 -0400
1# SPDX-FileCopyrightText: 2026-present Pavel M. Penev <pavpen@gmail.com>
2#
3# SPDX-License-Identifier: MIT
5import pytest
7from pavpen.geometry_calculator.vector_field_operations import VectorFieldOperations
10class UnimplementedVectorFieldOperations[Scalar, Vector](VectorFieldOperations[Scalar, Vector]):
11 @property
12 def additive_identity(self) -> Vector:
13 return super().additive_identity
15 @property
16 def multiplicative_negator(self) -> Scalar:
17 return super().multiplicative_negator
19 def negated(self, value: Vector) -> Vector:
20 return self.scaled(value, self.multiplicative_negator)
22 def added(self, addend1: Vector, addend2: Vector) -> Vector:
23 return super().added(addend1, addend2)
25 def subtracted(self, diminuend: Vector, subtrahend: Vector) -> Vector:
26 return self.added(diminuend, self.negated(subtrahend))
28 def scaled(self, multiplicand: Vector, scalar: Scalar) -> Vector:
29 return super().scaled(multiplicand=multiplicand, scalar=scalar)
31 def inner_multiplied(self, multiplicand1: Vector, multiplicand2: Vector) -> Scalar:
32 return super().inner_multiplied(multiplicand1, multiplicand2)
34 def norm(self, value: Vector) -> Scalar:
35 return super().norm(value)
37 def normalized(self, value: Vector) -> Vector:
38 return super().normalized(value)
40 def projection_length_along(self, projected: Vector, direction: Vector) -> Scalar:
41 return super().projection_length_along(projected=projected, direction=direction)
44class TestVectorFieldOperationsDefaultImplementations:
45 """Tests the default implementations in [VectorFieldOperations],
46 to which [UnimplementedVectorFieldOperations] forwards method calls
48 Also, code coverage include tests, so we test our
49 [UnimplementedVectorFieldOperations] helper class. (See the tip in
50 [Exclude code from test coverage](https://python-basics-tutorial.readthedocs.io/en/latest/test/pytest/coverage.html#exclude-code-from-test-coverage).)
51 """
53 def test_additive_identity_throws(self):
54 # Setup
55 operations = UnimplementedVectorFieldOperations[float, complex]()
57 # Act, and verify
58 with pytest.raises(NotImplementedError):
59 _ = operations.additive_identity
61 def test_multiplicative_negator_throws(self):
62 # Setup
63 operations = UnimplementedVectorFieldOperations[float, complex]()
65 # Act, and verify
66 with pytest.raises(NotImplementedError):
67 _ = operations.multiplicative_negator
69 def test_added_throws(self):
70 # Setup
71 operations = UnimplementedVectorFieldOperations[float, complex]()
73 # Act, and verify
74 with pytest.raises(NotImplementedError):
75 operations.added(3 + 4j, 5 + 6j)
77 def test_scaled_throws(self):
78 # Setup
79 operations = UnimplementedVectorFieldOperations[float, complex]()
81 # Act, and verify
82 with pytest.raises(NotImplementedError):
83 operations.scaled(3 + 4j, 7)
85 def test_inner_multiplied_throws(self):
86 # Setup
87 operations = UnimplementedVectorFieldOperations[float, complex]()
89 # Act, and verify
90 with pytest.raises(NotImplementedError):
91 operations.inner_multiplied(3 + 4j, 7 + 8j)
93 def test_norm_throws(self):
94 # Setup
95 operations = UnimplementedVectorFieldOperations[float, complex]()
97 # Act, and verify
98 with pytest.raises(NotImplementedError):
99 operations.norm(7 + 8j)
101 def test_normalized_throws(self):
102 # Setup
103 operations = UnimplementedVectorFieldOperations[float, complex]()
105 # Act, and verify
106 with pytest.raises(NotImplementedError):
107 operations.normalized(7 + 8j)
109 def test_projection_length_on_throws(self):
110 # Setup
111 operations = UnimplementedVectorFieldOperations[float, complex]()
113 # Act, and verify
114 with pytest.raises(NotImplementedError):
115 operations.projection_length_along(7 + 8j, 9 + 10j)
118class TestVectorFieldOperations:
119 def test_negated_is_equivalent_to_scaling_by_multiplicative_negator(self):
120 # Setup
121 class SampleVectorFieldOperations(UnimplementedVectorFieldOperations[int, tuple[int, int]]):
122 @property
123 def multiplicative_negator(self) -> int:
124 return -1
126 def scaled(self, multiplicand: tuple[int, int], scalar: int) -> tuple[int, int]:
127 return (multiplicand[0] * scalar, multiplicand[1] * scalar)
129 vector_field_operations = SampleVectorFieldOperations()
131 # Act and verify
132 assert vector_field_operations.negated((3, -2)) == ((-3, 2))
134 def test_summed_is_equivalent_to_composing_added(self):
135 # Setup
136 class SampleVectorFieldOperations(UnimplementedVectorFieldOperations[int, tuple[int, int]]):
137 def added(self, addend1: tuple[int, int], addend2: tuple[int, int]) -> tuple[int, int]:
138 return (addend1[0] + addend2[0], addend1[1] + addend2[1])
140 vector_field_operations = SampleVectorFieldOperations()
142 # Act and verify
143 assert vector_field_operations.summed((3, -2), (4, 5)) == ((7, 3))
145 def test_substracted_is_equivalent_to_currying_added_and_negated(self):
146 # Setup
147 class SampleVectorFieldOperations(UnimplementedVectorFieldOperations[int, tuple[int, int]]):
148 def negated(self, value: tuple[int, int]) -> tuple[int, int]:
149 return (-value[0], -value[1])
151 def added(self, addend1: tuple[int, int], addend2: tuple[int, int]) -> tuple[int, int]:
152 return (addend1[0] + addend2[0], addend1[1] + addend2[1])
154 vector_field_operations = SampleVectorFieldOperations()
156 # Act and verify
157 assert vector_field_operations.subtracted((3, -2), (4, 6)) == ((-1, -8))