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