|
1 | 1 | import asyncio
|
| 2 | +import gc |
2 | 3 | from unittest.mock import Mock
|
3 | 4 |
|
4 | 5 | import pytest
|
5 | 6 |
|
6 |
| -from toga.handlers import AsyncResult, NativeHandler, simple_handler, wrapped_handler |
| 7 | +from toga.handlers import ( |
| 8 | + AsyncResult, |
| 9 | + NativeHandler, |
| 10 | + WeakrefCallable, |
| 11 | + simple_handler, |
| 12 | + wrapped_handler, |
| 13 | +) |
7 | 14 |
|
8 | 15 |
|
9 | 16 | class ExampleAsyncResult(AsyncResult):
|
@@ -742,3 +749,123 @@ def test_async_exception_cancelled_sync(event_loop):
|
742 | 749 |
|
743 | 750 | # The callback wasn't called
|
744 | 751 | on_result.assert_not_called()
|
| 752 | + |
| 753 | + |
| 754 | +def test_weakref_function_call(): |
| 755 | + """Test that WeakrefCallable correctly calls the wrapped function.""" |
| 756 | + |
| 757 | + def test_func(x, y=2): |
| 758 | + return x + y |
| 759 | + |
| 760 | + wrc = WeakrefCallable(test_func) |
| 761 | + |
| 762 | + # Test with positional arguments |
| 763 | + assert wrc(3) == 5 |
| 764 | + |
| 765 | + # Test with keyword arguments |
| 766 | + assert wrc(3, y=3) == 6 |
| 767 | + |
| 768 | + # Test with mixed arguments |
| 769 | + assert wrc(3, 4) == 7 |
| 770 | + |
| 771 | + |
| 772 | +def test_weakref_method_call(): |
| 773 | + """Test that WeakrefCallable correctly calls a method.""" |
| 774 | + |
| 775 | + class TestClass: |
| 776 | + def __init__(self, value): |
| 777 | + self.value = value |
| 778 | + |
| 779 | + def method(self, x): |
| 780 | + return self.value + x |
| 781 | + |
| 782 | + obj = TestClass(5) |
| 783 | + wrc = WeakrefCallable(obj.method) |
| 784 | + |
| 785 | + # Test method call |
| 786 | + assert wrc(3) == 8 |
| 787 | + |
| 788 | + |
| 789 | +def test_weakref_lambda_call(): |
| 790 | + """Test that WeakrefCallable works with lambda functions.""" |
| 791 | + # Store the lambda in a variable to prevent it from being garbage collected |
| 792 | + lambda_func = lambda x: x * 2 # noqa: E731 |
| 793 | + wrc = WeakrefCallable(lambda_func) |
| 794 | + assert wrc(5) == 10 |
| 795 | + |
| 796 | + |
| 797 | +def test_weakref_gc_function(): |
| 798 | + """Test that function is garbage collected properly.""" |
| 799 | + |
| 800 | + def create_function_wrapper(): |
| 801 | + def temp_func(x): |
| 802 | + return x * 3 |
| 803 | + |
| 804 | + return WeakrefCallable(temp_func) |
| 805 | + |
| 806 | + wrc = create_function_wrapper() |
| 807 | + |
| 808 | + # Force garbage collection |
| 809 | + gc.collect() |
| 810 | + |
| 811 | + # The function should be gone |
| 812 | + assert wrc.ref() is None |
| 813 | + |
| 814 | + |
| 815 | +def test_weakref_gc_method(): |
| 816 | + """Test that method and its object are garbage collected properly.""" |
| 817 | + |
| 818 | + class TempClass: |
| 819 | + def method(self, x): |
| 820 | + return x * 4 |
| 821 | + |
| 822 | + def create_method_wrapper(): |
| 823 | + obj = TempClass() |
| 824 | + return WeakrefCallable(obj.method), obj |
| 825 | + |
| 826 | + wrc, obj_ref = create_method_wrapper() |
| 827 | + |
| 828 | + # Object still exists, method should work |
| 829 | + assert wrc(2) == 8 |
| 830 | + |
| 831 | + # Delete the reference to the object |
| 832 | + del obj_ref |
| 833 | + |
| 834 | + # Force garbage collection |
| 835 | + gc.collect() |
| 836 | + |
| 837 | + # The method reference should be gone |
| 838 | + assert wrc.ref() is None |
| 839 | + |
| 840 | + |
| 841 | +def test_weakref_callable_object(): |
| 842 | + """Test that WeakrefCallable works with callable objects.""" |
| 843 | + |
| 844 | + class CallableObject: |
| 845 | + def __call__(self, x): |
| 846 | + return x * 5 |
| 847 | + |
| 848 | + obj = CallableObject() |
| 849 | + wrc = WeakrefCallable(obj) |
| 850 | + |
| 851 | + # Test call |
| 852 | + assert wrc(2) == 10 |
| 853 | + |
| 854 | + |
| 855 | +def test_weakref_none_result_when_function_gone(): |
| 856 | + """Test that calling the wrapper after the target is collected doesn't error.""" |
| 857 | + |
| 858 | + def create_function_wrapper(): |
| 859 | + def temp_func(x): |
| 860 | + return x * 3 |
| 861 | + |
| 862 | + return WeakrefCallable(temp_func) |
| 863 | + |
| 864 | + wrc = create_function_wrapper() |
| 865 | + |
| 866 | + # Force garbage collection |
| 867 | + gc.collect() |
| 868 | + |
| 869 | + # Calling the wrapper should not raise an error |
| 870 | + result = wrc(10) |
| 871 | + assert result is None |
0 commit comments