Removed the subdir.
This commit is contained in:
182
pyrate/tests/plan/geometry/helpers/test_normalize.py
Normal file
182
pyrate/tests/plan/geometry/helpers/test_normalize.py
Normal file
@ -0,0 +1,182 @@
|
||||
"""This module asserts correct runtime behaviour of the :mod:`pyrate.plan.geometry.helpers` functions
|
||||
for normalization.
|
||||
"""
|
||||
|
||||
# Python standard
|
||||
from abc import ABC
|
||||
from abc import abstractmethod
|
||||
|
||||
# Typing
|
||||
from typing import Callable
|
||||
from typing import Sequence
|
||||
from typing import Tuple
|
||||
|
||||
# Generic testing
|
||||
from unittest import TestCase
|
||||
|
||||
# Numeric testing
|
||||
from numpy import allclose
|
||||
from numpy import array
|
||||
|
||||
# Hypothesis testing
|
||||
from hypothesis import given
|
||||
import hypothesis.strategies as st
|
||||
|
||||
# Test helpers
|
||||
from pyrate.plan.geometry.helpers import normalize_direction
|
||||
from pyrate.plan.geometry.helpers import normalize_latitude
|
||||
from pyrate.plan.geometry.helpers import normalize_longitude
|
||||
from pyrate.plan.geometry.helpers import ScalarOrArray
|
||||
|
||||
|
||||
class TestNormalize(TestCase, ABC):
|
||||
"""Makes sure the normalizations are well-behaved."""
|
||||
|
||||
@abstractmethod
|
||||
def _get_normalization_function(self) -> Callable[[ScalarOrArray], ScalarOrArray]:
|
||||
"""Get the function to be tested."""
|
||||
|
||||
@abstractmethod
|
||||
def _get_min(self) -> float:
|
||||
"""Get the desired minimum value (inclusive)."""
|
||||
|
||||
@abstractmethod
|
||||
def _get_max(self) -> float:
|
||||
"""Get the desired maximum value, see :meth:`TestNormalize._max_is_inclusive`."""
|
||||
|
||||
def _max_is_inclusive(self) -> bool: # pylint: disable=no-self-use
|
||||
"""If :meth:`TestNormalize._get_max` is to be seen as inclusive or exclusive"""
|
||||
return False
|
||||
|
||||
@abstractmethod
|
||||
def _get_concrete_examples(self) -> Sequence[Tuple[float, float]]:
|
||||
"""Get some concrete values to be tested as a sequence of ``(non-normalized, normalized)``."""
|
||||
|
||||
@given(st.floats(allow_infinity=False, allow_nan=False))
|
||||
def test_bounds(self, value: float) -> None:
|
||||
"""Assures that the normalized value is within its bounds."""
|
||||
|
||||
normalized = self._get_normalization_function()(value)
|
||||
|
||||
# make sure the normalized value is within bounds
|
||||
self.assertGreaterEqual(normalized, self._get_min())
|
||||
|
||||
if self._max_is_inclusive():
|
||||
self.assertLessEqual(normalized, self._get_max())
|
||||
else:
|
||||
self.assertLess(normalized, self._get_max())
|
||||
|
||||
@given(st.floats(allow_infinity=False, allow_nan=False))
|
||||
def test_normalizing_twice(self, value: float) -> None:
|
||||
"""Assures that normalizing twice does not really change the value."""
|
||||
|
||||
normalized = self._get_normalization_function()(value)
|
||||
normalized_twice = self._get_normalization_function()(normalized)
|
||||
|
||||
self.assertAlmostEqual(normalized, normalized_twice, places=10)
|
||||
|
||||
@given(st.floats(min_value=-400, max_value=+400))
|
||||
def test_already_normalized_values(self, value: float) -> None:
|
||||
"""Assures that values stay unchanged if and only if are already normalized (i.e. within bounds)."""
|
||||
below_max = value < self._get_max() or (self._max_is_inclusive() and value == self._get_max())
|
||||
if self._get_min() <= value and below_max:
|
||||
self.assertAlmostEqual(self._get_normalization_function()(value), value, delta=1e-12)
|
||||
else:
|
||||
self.assertNotEqual(self._get_normalization_function()(value), value)
|
||||
|
||||
def test_concrete_examples(self) -> None:
|
||||
"""Checks the result for the concrete examples given in :meth:`~_get_concrete_examples`."""
|
||||
function = self._get_normalization_function()
|
||||
|
||||
for index, (non_normalized, normalized) in enumerate(self._get_concrete_examples()):
|
||||
with self.subTest(f"example triple #{index}"):
|
||||
self.assertAlmostEqual(function(non_normalized), normalized, delta=1e-12)
|
||||
|
||||
def test_concrete_examples_as_array(self) -> None:
|
||||
"""Checks the result for the concrete examples given in :meth:`~_get_concrete_examples`."""
|
||||
function = self._get_normalization_function()
|
||||
data = array(self._get_concrete_examples()).T
|
||||
self.assertTrue(allclose(function(data[0, :]), data[1, :]))
|
||||
|
||||
|
||||
class TestNormalizeLatitude(TestNormalize):
|
||||
"""Tests :func:`pyrate.plan.geometry.helpers.normalize_latitude`."""
|
||||
|
||||
def _get_normalization_function(self) -> Callable[[ScalarOrArray], ScalarOrArray]:
|
||||
return normalize_latitude
|
||||
|
||||
def _get_min(self) -> float:
|
||||
return -90.0
|
||||
|
||||
def _get_max(self) -> float:
|
||||
return 90.0
|
||||
|
||||
def _max_is_inclusive(self) -> bool:
|
||||
return True
|
||||
|
||||
def _get_concrete_examples(self) -> Sequence[Tuple[float, float]]:
|
||||
return [
|
||||
(0, 0),
|
||||
(90, 90),
|
||||
(-90, -90),
|
||||
(100, 80),
|
||||
(180, 0),
|
||||
(270, -90),
|
||||
(-180, 0),
|
||||
(-270, 90),
|
||||
(-10, -10),
|
||||
]
|
||||
|
||||
|
||||
class TestNormalizeLongitude(TestNormalize):
|
||||
"""Tests :func:`pyrate.plan.geometry.helpers.normalize_longitude`."""
|
||||
|
||||
def _get_normalization_function(self) -> Callable[[ScalarOrArray], ScalarOrArray]:
|
||||
return normalize_longitude
|
||||
|
||||
def _get_min(self) -> float:
|
||||
return -180.0
|
||||
|
||||
def _get_max(self) -> float:
|
||||
return 180.0
|
||||
|
||||
def _get_concrete_examples(self) -> Sequence[Tuple[float, float]]:
|
||||
return [
|
||||
(0, 0),
|
||||
(90, 90),
|
||||
(-90, -90),
|
||||
(100, 100),
|
||||
(180, -180),
|
||||
(-180, -180),
|
||||
(270, -90),
|
||||
(-10, -10),
|
||||
]
|
||||
|
||||
|
||||
class TestNormalizeDirection(TestNormalize):
|
||||
"""Tests :func:`pyrate.plan.geometry.helpers.normalize_direction`."""
|
||||
|
||||
def _get_normalization_function(self) -> Callable[[ScalarOrArray], ScalarOrArray]:
|
||||
return normalize_direction
|
||||
|
||||
def _get_min(self) -> float:
|
||||
return 0.0
|
||||
|
||||
def _get_max(self) -> float:
|
||||
return 360.0
|
||||
|
||||
def _get_concrete_examples(self) -> Sequence[Tuple[float, float]]:
|
||||
return [
|
||||
(0, 0),
|
||||
(90, 90),
|
||||
(-90, 270),
|
||||
(100, 100),
|
||||
(180, 180),
|
||||
(-180, 180),
|
||||
(270, 270),
|
||||
(-10, 350),
|
||||
]
|
||||
|
||||
|
||||
# Do not execute the base class as a test, see https://stackoverflow.com/a/43353680/3753684
|
||||
del TestNormalize
|
Reference in New Issue
Block a user