Source code for betty.test_utils.attr

"""
Test utilities for :py:mod:`betty.attr`.
"""

from collections.abc import Sequence
from typing import TypeVar, Generic
from typing_extensions import override

from betty.attr import Attr

_InstanceT = TypeVar("_InstanceT")
_ValueT = TypeVar("_ValueT")
_SetT = TypeVar("_SetT")


[docs] class AttrTestBase(Generic[_InstanceT, _ValueT]): """ A base class for testing :py:class:`betty.attr.Attr` implementations. """
[docs] def get_instances(self) -> tuple[Sequence[_InstanceT], str]: """ Get instances with an attribute under test. :return: A 2-tuple with the instances, and the name of the attribute containing the :py:class:`betty.attr.Attr` under test. """ raise NotImplementedError(repr(self))
[docs] def test_get_attr(self) -> None: """ Tests :py:meth:`betty.attr.Attr.get_attr` implementations. """ instances, attr_name = self.get_instances() for instance in instances: sut = getattr(type(instance), attr_name) assert sut.get_attr(instance) is sut.get_attr(instance)
[docs] async def test___get___without_owner(self) -> None: """ Tests ``__get__`` implementations. """ instances, attr_name = self.get_instances() for instance in instances: assert isinstance( getattr(type(instance), attr_name), Attr, )
[docs] async def test___get___with_owner(self) -> None: """ Tests ``__get__`` implementations. """ instances, attr_name = self.get_instances() for instance in instances: assert getattr(instance, attr_name) is getattr(instance, attr_name)
[docs] class MutableAttrTestBase( Generic[_InstanceT, _ValueT, _SetT], AttrTestBase[_InstanceT, _ValueT] ): """ A base class for testing :py:class:`betty.attr.MutableAttr` implementations. """
[docs] @override def get_instances(self) -> tuple[Sequence[_InstanceT], str]: instances, attr_name = self.get_mutable_instances() return ([instance for instance, _ in instances], attr_name)
[docs] def get_mutable_instances( self, ) -> tuple[Sequence[tuple[_InstanceT, Sequence[_SetT]]], str]: """ Get instances with a mutable attribute under test. :return: A 2-tuple with the instances, and the name of the attribute containing the :py:class:`betty.attr.MutableAttr` under test. """ raise NotImplementedError(repr(self))
[docs] def assert_eq(self, get_value: _ValueT, set_value: _SetT) -> None: """ Assert that a get value and a set value are equal. """ raise NotImplementedError(repr(self))
[docs] def assert_ne(self, get_value: _ValueT, set_value: _SetT) -> None: """ Assert that a get value and a set value are not equal. """ try: self.assert_eq(get_value, set_value) raise AssertionError( f"get value {get_value} and set value {set_value} were unexpectedly equal" ) except AssertionError: pass
[docs] async def test___set__(self) -> None: """ Tests ``__set__`` implementations. """ instances, attr_name = self.get_mutable_instances() for instance, set_values in instances: for set_value in set_values: setattr(instance, attr_name, set_value) self.assert_eq(getattr(instance, attr_name), set_value)
[docs] def test_set_attr(self) -> None: """ Tests :py:meth:`betty.attr.MutableAttr.set_attr` implementations. """ instances, attr_name = self.get_mutable_instances() for instance, set_values in instances: for set_value in set_values: getattr(type(instance), attr_name).set_attr(instance, set_value) self.assert_eq(getattr(instance, attr_name), set_value)
[docs] async def test___delete__(self) -> None: """ Tests ``__delete__`` implementations. """ instances, attr_name = self.get_mutable_instances() for instance, set_values in instances: # Test deleting any default value. delattr(instance, attr_name) # Test deleting provided values. for set_value in set_values: setattr(instance, attr_name, set_value) delattr(instance, attr_name) self.assert_ne(getattr(instance, attr_name), set_value)
[docs] def test_del_attr(self) -> None: """ Tests :py:meth:`betty.attr.MutableAttr.del_attr` implementations. """ instances, attr_name = self.get_mutable_instances() for instance, set_values in instances: # Test deleting any default value. delattr(instance, attr_name) # Test deleting provided values. for set_value in set_values: setattr(instance, attr_name, set_value) getattr(type(instance), attr_name).del_attr(instance) self.assert_ne(getattr(instance, attr_name), set_value)
[docs] def test_new_attr(self) -> None: """ Tests :py:meth:`betty.attr.MutableAttr.del_attr` implementations. """ raise NotImplementedError(repr(self))