1import unittest
2from test import support
3import ctypes
4import gc
5
6MyCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
7OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong)
8
9import _ctypes_test
10dll = ctypes.CDLL(_ctypes_test.__file__)
11
12class RefcountTestCase(unittest.TestCase):
13
14    @support.refcount_test
15    def test_1(self):
16        from sys import getrefcount as grc
17
18        f = dll._testfunc_callback_i_if
19        f.restype = ctypes.c_int
20        f.argtypes = [ctypes.c_int, MyCallback]
21
22        def callback(value):
23            #print "called back with", value
24            return value
25
26        self.assertEqual(grc(callback), 2)
27        cb = MyCallback(callback)
28
29        self.assertGreater(grc(callback), 2)
30        result = f(-10, cb)
31        self.assertEqual(result, -18)
32        cb = None
33
34        gc.collect()
35
36        self.assertEqual(grc(callback), 2)
37
38
39    @support.refcount_test
40    def test_refcount(self):
41        from sys import getrefcount as grc
42        def func(*args):
43            pass
44        # this is the standard refcount for func
45        self.assertEqual(grc(func), 2)
46
47        # the CFuncPtr instance holds at least one refcount on func:
48        f = OtherCallback(func)
49        self.assertGreater(grc(func), 2)
50
51        # and may release it again
52        del f
53        self.assertGreaterEqual(grc(func), 2)
54
55        # but now it must be gone
56        gc.collect()
57        self.assertEqual(grc(func), 2)
58
59        class X(ctypes.Structure):
60            _fields_ = [("a", OtherCallback)]
61        x = X()
62        x.a = OtherCallback(func)
63
64        # the CFuncPtr instance holds at least one refcount on func:
65        self.assertGreater(grc(func), 2)
66
67        # and may release it again
68        del x
69        self.assertGreaterEqual(grc(func), 2)
70
71        # and now it must be gone again
72        gc.collect()
73        self.assertEqual(grc(func), 2)
74
75        f = OtherCallback(func)
76
77        # the CFuncPtr instance holds at least one refcount on func:
78        self.assertGreater(grc(func), 2)
79
80        # create a cycle
81        f.cycle = f
82
83        del f
84        gc.collect()
85        self.assertEqual(grc(func), 2)
86
87class AnotherLeak(unittest.TestCase):
88    def test_callback(self):
89        import sys
90
91        proto = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int)
92        def func(a, b):
93            return a * b * 2
94        f = proto(func)
95
96        a = sys.getrefcount(ctypes.c_int)
97        f(1, 2)
98        self.assertEqual(sys.getrefcount(ctypes.c_int), a)
99
100if __name__ == '__main__':
101    unittest.main()
102