diff --git a/tests/test_utils_typecast.py b/tests/test_utils_typecast.py new file mode 100644 index 000000000..69937eaa9 --- /dev/null +++ b/tests/test_utils_typecast.py @@ -0,0 +1,91 @@ +import pytest +import sys +import os +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from slic.utils.typecast import * +from slic.utils.typecast import _cast, _ensure_subclass + + +class Animal: + def speak(self): + return "..." + +class Dog(Animal): + def speak(self): + return "woof" + +class Bulldog(Dog): + def snore(self): + return "woooooof" + +class Car: + def drive(self): + return "vroom" + + +# --- Tests --- + +def test_downcast_success(): + d = Dog() + downcast(d, Bulldog) + assert d.speak() == "woof" + assert d.snore() == "woooooof" + +def test_upcast_success(): + b = Bulldog() + assert d.snore() == "woooooof" + assert not hasattr(d, "speak") + upcast(b, Dog) + assert b.speak() == "woof" + assert not hasattr(b, "snore") + + upcast(b, Animal) + assert b.speak() == "..." + +def test_downcast_invalid(): + a = Animal() + with pytest.raises(TypeError, match="is not a subclass"): + downcast(a, Bulldog) + +def test_upcast_invalid(): + c = Car() + with pytest.raises(TypeError, match="is not a subclass"): + upcast(c, Dog) + +def test_object_identity_preserved(): + d = Dog() + d.name = "Rex" + ref = downcast(d, Bulldog) + assert ref is d + assert d.name == "Rex" + +class A: pass +class B(A): pass +class C: pass + +def test_ensure_subclass_valid(): + _ensure_subclass(B, A) + +def test_ensure_subclass_invalid(): + with pytest.raises(TypeError, match="is not a subclass"): + _ensure_subclass(C, A) + +class A: + def hello(self): + return "hi from A" + +class B(A): + def greet(self): + return "hello from B" + +def test_cast_changes_class(): + a = A() + _cast(a, B) + assert isinstance(a, B) + assert a.greet() == "hello from B" + assert a.hello() == "hi from A" + +def test_cast_preserves_identity(): + a = A() + ref = _cast(a, B) + assert ref is a # same obj in memory \ No newline at end of file