Coverage for tests \ test_orthonormal_basis_calculator.py: 100%

40 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 math 

6from typing import cast 

7 

8import pytest 

9 

10from pavpen.geometry_calculator.faults import CalculationPrecisionExceededError 

11from pavpen.geometry_calculator.orthonormal_basis_calculator import OrthonormalBasisCalculator 

12from pavpen.geometry_calculator.vector_field_implementations.tuple_float_vector_field_operations import ( 

13 TupleFloatVectorFieldOperations, 

14) 

15 

16 

17class TestOrthonormalBasisCalculator: 

18 def test_constructor_throws_on_empty_points_collection(self): 

19 # Setup 

20 vector_field_operations = TupleFloatVectorFieldOperations.for_1d() 

21 

22 # Act, and verify 

23 with pytest.raises(ValueError, match=r"The `points` argument must contain at least one element."): 

24 OrthonormalBasisCalculator( 

25 vector_field_operations=vector_field_operations, 

26 float_tolerance=1e-8, 

27 points=[], 

28 ) 

29 

30 def test_calculate_throws_on_empty_points_collection(self): 

31 # Setup 

32 vector_field_operations = TupleFloatVectorFieldOperations.for_1d() 

33 points = [(3.0,)] 

34 calculator = OrthonormalBasisCalculator( 

35 vector_field_operations=vector_field_operations, 

36 float_tolerance=1e-8, 

37 points=points, 

38 ) 

39 

40 # Act, and verify 

41 points.clear() 

42 with pytest.raises( 

43 ValueError, 

44 match=r"The `points` collection in OrthonormalBasisCalculator must contain at least one element.", 

45 ): 

46 calculator.calculate() 

47 

48 def test_calculate_throws_on_redundant_points(self): 

49 # Setup 

50 vector_field_operations = TupleFloatVectorFieldOperations.for_3d() 

51 calculator = OrthonormalBasisCalculator( 

52 vector_field_operations=vector_field_operations, 

53 float_tolerance=1e-8, 

54 points=[(0, 0, 0), (0, 0, -1), (0, 0, 1)], 

55 ) 

56 

57 # Act, and verify 

58 with pytest.raises(CalculationPrecisionExceededError) as exc_info: 

59 calculator.calculate(require_points_are_non_redundant=True) 

60 

61 # Verify 

62 exception = cast("CalculationPrecisionExceededError", exc_info.value) 

63 assert exception.tolerance == pytest.approx(1e-8) 

64 assert exception.calculation_error == float("inf") 

65 assert exception.value_name == "non_spanned_component_norm" 

66 

67 def test_calculate_ignores_redundant_points(self): 

68 # Setup 

69 vector_field_operations = TupleFloatVectorFieldOperations.for_3d() 

70 calculator = OrthonormalBasisCalculator( 

71 vector_field_operations=vector_field_operations, 

72 float_tolerance=1e-8, 

73 points=[(0, 0, 0), (0, 0, -1), (0, 0, 1)], 

74 ) 

75 

76 # Act 

77 basis_vectors = calculator.calculate(require_points_are_non_redundant=False).basis_vectors 

78 

79 # Verify 

80 assert len(basis_vectors) == 1 

81 assert basis_vectors[0] == pytest.approx((0, 0, -1)) 

82 

83 def test_calculate_returns_an_orthonormal_basis(self): 

84 # Setup 

85 vector_field_operations = TupleFloatVectorFieldOperations.for_3d() 

86 calculator = OrthonormalBasisCalculator( 

87 vector_field_operations=vector_field_operations, 

88 float_tolerance=1e-8, 

89 points=[(1, 1, 1), (1, 1, -1), (0, 0, 0)], 

90 ) 

91 

92 # Act 

93 basis_vectors = calculator.calculate(require_points_are_non_redundant=True).basis_vectors 

94 

95 # Verify 

96 assert len(basis_vectors) == 2 

97 assert basis_vectors[0] == pytest.approx((0, 0, -1)) 

98 assert basis_vectors[1] == pytest.approx((-1.0 / math.sqrt(2), -1.0 / math.sqrt(2), 0))