diff --git a/tests/unit/meta/test_Callback.py b/tests/unit/meta/test_Callback.py index df5fca2dc..db6db1ea2 100644 --- a/tests/unit/meta/test_Callback.py +++ b/tests/unit/meta/test_Callback.py @@ -51,56 +51,72 @@ def test_floatCallback(self): assert testCallback == 123.456 assert testCallback + 1 == 124.456 - def test_callback_instances(self): - """Test that callback() returns distinct instances but cached classes.""" - - # Create two callbacks for int + def test_callbackInstances(self): + # `callback()` returns distinct instances, but the underlying class is + # cached per wrapped type. cb1 = callback(int) cb2 = callback(int) - - # Create one callback for str cb3 = callback(str) - # Test 1: Different instances - print("Test 1: Different instances") - print(f" cb1 is cb2: {cb1 is cb2}") # Should be False - print(f" cb1 is not cb2: {cb1 is not cb2}") # Should be True - assert cb1 is not cb2, "cb1 and cb2 should be different instances" - - # Test 2: Same class for same type - print("\nTest 2: Same class for same type") - print(f" cb1.__class__ is cb2.__class__: {cb1.__class__ is cb2.__class__}") # Should be True - assert cb1.__class__ is cb2.__class__, "cb1 and cb2 should have the same class" - - # Test 3: Different classes for different types - print("\nTest 3: Different classes for different types") - print(f" cb1.__class__ is cb3.__class__: {cb1.__class__ is cb3.__class__}") # Should be False - assert cb1.__class__ is not cb3.__class__, "cb1 and cb3 should have different classes" - - # Test 4: Class names are correct - print("\nTest 4: Class names are correct") - print(f" cb1.__class__.__name__: {cb1.__class__.__name__}") # Should be Callback[int] - print(f" cb3.__class__.__name__: {cb3.__class__.__name__}") # Should be Callback[str] - assert cb1.__class__.__name__ == "Callback[int]", f"Expected 'Callback[int]', got '{cb1.__class__.__name__}'" - assert cb3.__class__.__name__ == "Callback[str]", f"Expected 'Callback[str]', got '{cb3.__class__.__name__}'" - - # Test 5: Functionality still works - print("\nTest 5: Functionality still works") + # Distinct instances ... + assert cb1 is not cb2 + # ... but a single cached class per wrapped type ... + assert cb1.__class__ is cb2.__class__ + # ... and a distinct class for each distinct wrapped type. + assert cb1.__class__ is not cb3.__class__ + + # Generated class names encode the wrapped type. + assert cb1.__class__.__name__ == "Callback[int]" + assert cb3.__class__.__name__ == "Callback[str]" + + # Independent state: updating one instance does not affect the other. cb1.update(10) cb2.update(20) + assert cb1.get() == 10 + assert cb2.get() == 20 + + # Forwarded magic methods continue to work for each instance. + assert cb1 + 5 == 15 + assert cb2 + 5 == 25 - print(f" cb1.get(): {cb1.get()}") # Should be 10 - print(f" cb2.get(): {cb2.get()}") # Should be 20 - assert cb1.get() == 10, f"Expected 10, got {cb1.get()}" - assert cb2.get() == 20, f"Expected 20, got {cb2.get()}" - - # Test 6: Magic methods work - print("\nTest 6: Magic methods work") - result1 = cb1 + 5 - result2 = cb2 + 5 - print(f" cb1 + 5: {result1}") # Should be 15 - print(f" cb2 + 5: {result2}") # Should be 25 - assert result1 == 15, f"Expected 15, got {result1}" - assert result2 == 25, f"Expected 25, got {result2}" - - print("\n✅ All tests passed!") + def test_strSet(self): + # Once populated, `str()` is forwarded to the wrapped value. + testCallback = callback(str) + testCallback.update("test") + assert str(testCallback) == "test" + + def test_listCallback(self): + testCallback = callback(list) + testCallback.update([1, 2, 3]) + assert testCallback.get() == [1, 2, 3] + assert len(testCallback) == 3 + assert testCallback[1] == 2 + assert 2 in testCallback + assert list(iter(testCallback)) == [1, 2, 3] + + def test_setItemForwarded(self): + testCallback = callback(list) + testCallback.update([1, 2, 3]) + testCallback[0] = 99 + assert testCallback.get() == [99, 2, 3] + + def test_setAttrForwardedWhenSet(self): + # When populated, setting a non-internal attribute is forwarded to the + # wrapped value. + class _Bag: + pass + + bag = _Bag() + testCallback = callback(_Bag) + testCallback.update(bag) + testCallback.name = "forwarded" + assert bag.name == "forwarded" + + def test_unsetMagicMethodThrows(self): + # A representative forwarded magic method should raise when the + # callback is not populated. + testCallback = callback(int) + with pytest.raises(AttributeError): + testCallback + 1 + with pytest.raises(AttributeError): + len(callback(list))