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

1# SPDX-FileCopyrightText: 2026-present Pavel M. Penev <pavpen@gmail.com> 

2# 

3# SPDX-License-Identifier: MIT 

4 

5import pytest 

6 

7from pavpen.geometry_calculator.vector_field_operations import VectorFieldOperations 

8 

9 

10class UnimplementedVectorFieldOperations[Scalar, Vector](VectorFieldOperations[Scalar, Vector]): 

11 @property 

12 def additive_identity(self) -> Vector: 

13 return super().additive_identity 

14 

15 @property 

16 def multiplicative_negator(self) -> Scalar: 

17 return super().multiplicative_negator 

18 

19 def negated(self, value: Vector) -> Vector: 

20 return self.scaled(value, self.multiplicative_negator) 

21 

22 def added(self, addend1: Vector, addend2: Vector) -> Vector: 

23 return super().added(addend1, addend2) 

24 

25 def subtracted(self, diminuend: Vector, subtrahend: Vector) -> Vector: 

26 return self.added(diminuend, self.negated(subtrahend)) 

27 

28 def scaled(self, multiplicand: Vector, scalar: Scalar) -> Vector: 

29 return super().scaled(multiplicand=multiplicand, scalar=scalar) 

30 

31 def inner_multiplied(self, multiplicand1: Vector, multiplicand2: Vector) -> Scalar: 

32 return super().inner_multiplied(multiplicand1, multiplicand2) 

33 

34 def norm(self, value: Vector) -> Scalar: 

35 return super().norm(value) 

36 

37 def normalized(self, value: Vector) -> Vector: 

38 return super().normalized(value) 

39 

40 def projection_length_along(self, projected: Vector, direction: Vector) -> Scalar: 

41 return super().projection_length_along(projected=projected, direction=direction) 

42 

43 

44class TestVectorFieldOperationsDefaultImplementations: 

45 """Tests the default implementations in [VectorFieldOperations], 

46 to which [UnimplementedVectorFieldOperations] forwards method calls 

47 

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

52 

53 def test_additive_identity_throws(self): 

54 # Setup 

55 operations = UnimplementedVectorFieldOperations[float, complex]() 

56 

57 # Act, and verify 

58 with pytest.raises(NotImplementedError): 

59 _ = operations.additive_identity 

60 

61 def test_multiplicative_negator_throws(self): 

62 # Setup 

63 operations = UnimplementedVectorFieldOperations[float, complex]() 

64 

65 # Act, and verify 

66 with pytest.raises(NotImplementedError): 

67 _ = operations.multiplicative_negator 

68 

69 def test_added_throws(self): 

70 # Setup 

71 operations = UnimplementedVectorFieldOperations[float, complex]() 

72 

73 # Act, and verify 

74 with pytest.raises(NotImplementedError): 

75 operations.added(3 + 4j, 5 + 6j) 

76 

77 def test_scaled_throws(self): 

78 # Setup 

79 operations = UnimplementedVectorFieldOperations[float, complex]() 

80 

81 # Act, and verify 

82 with pytest.raises(NotImplementedError): 

83 operations.scaled(3 + 4j, 7) 

84 

85 def test_inner_multiplied_throws(self): 

86 # Setup 

87 operations = UnimplementedVectorFieldOperations[float, complex]() 

88 

89 # Act, and verify 

90 with pytest.raises(NotImplementedError): 

91 operations.inner_multiplied(3 + 4j, 7 + 8j) 

92 

93 def test_norm_throws(self): 

94 # Setup 

95 operations = UnimplementedVectorFieldOperations[float, complex]() 

96 

97 # Act, and verify 

98 with pytest.raises(NotImplementedError): 

99 operations.norm(7 + 8j) 

100 

101 def test_normalized_throws(self): 

102 # Setup 

103 operations = UnimplementedVectorFieldOperations[float, complex]() 

104 

105 # Act, and verify 

106 with pytest.raises(NotImplementedError): 

107 operations.normalized(7 + 8j) 

108 

109 def test_projection_length_on_throws(self): 

110 # Setup 

111 operations = UnimplementedVectorFieldOperations[float, complex]() 

112 

113 # Act, and verify 

114 with pytest.raises(NotImplementedError): 

115 operations.projection_length_along(7 + 8j, 9 + 10j) 

116 

117 

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 

125 

126 def scaled(self, multiplicand: tuple[int, int], scalar: int) -> tuple[int, int]: 

127 return (multiplicand[0] * scalar, multiplicand[1] * scalar) 

128 

129 vector_field_operations = SampleVectorFieldOperations() 

130 

131 # Act and verify 

132 assert vector_field_operations.negated((3, -2)) == ((-3, 2)) 

133 

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

139 

140 vector_field_operations = SampleVectorFieldOperations() 

141 

142 # Act and verify 

143 assert vector_field_operations.summed((3, -2), (4, 5)) == ((7, 3)) 

144 

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

150 

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

153 

154 vector_field_operations = SampleVectorFieldOperations() 

155 

156 # Act and verify 

157 assert vector_field_operations.subtracted((3, -2), (4, 6)) == ((-1, -8))