1import os, unittest
2from ctypes import *
3
4try:
5    WINFUNCTYPE
6except NameError:
7    # fake to enable this test on Linux
8    WINFUNCTYPE = CFUNCTYPE
9
10import _ctypes_test
11lib = CDLL(_ctypes_test.__file__)
12
13class CFuncPtrTestCase(unittest.TestCase):
14    def test_basic(self):
15        X = WINFUNCTYPE(c_int, c_int, c_int)
16
17        def func(*args):
18            return len(args)
19
20        x = X(func)
21        self.assertEqual(x.restype, c_int)
22        self.assertEqual(x.argtypes, (c_int, c_int))
23        self.assertEqual(sizeof(x), sizeof(c_voidp))
24        self.assertEqual(sizeof(X), sizeof(c_voidp))
25
26    def test_first(self):
27        StdCallback = WINFUNCTYPE(c_int, c_int, c_int)
28        CdeclCallback = CFUNCTYPE(c_int, c_int, c_int)
29
30        def func(a, b):
31            return a + b
32
33        s = StdCallback(func)
34        c = CdeclCallback(func)
35
36        self.assertEqual(s(1, 2), 3)
37        self.assertEqual(c(1, 2), 3)
38        # The following no longer raises a TypeError - it is now
39        # possible, as in C, to call cdecl functions with more parameters.
40        #self.assertRaises(TypeError, c, 1, 2, 3)
41        self.assertEqual(c(1, 2, 3, 4, 5, 6), 3)
42        if not WINFUNCTYPE is CFUNCTYPE and os.name != "ce":
43            self.assertRaises(TypeError, s, 1, 2, 3)
44
45    def test_structures(self):
46        WNDPROC = WINFUNCTYPE(c_long, c_int, c_int, c_int, c_int)
47
48        def wndproc(hwnd, msg, wParam, lParam):
49            return hwnd + msg + wParam + lParam
50
51        HINSTANCE = c_int
52        HICON = c_int
53        HCURSOR = c_int
54        LPCTSTR = c_char_p
55
56        class WNDCLASS(Structure):
57            _fields_ = [("style", c_uint),
58                        ("lpfnWndProc", WNDPROC),
59                        ("cbClsExtra", c_int),
60                        ("cbWndExtra", c_int),
61                        ("hInstance", HINSTANCE),
62                        ("hIcon", HICON),
63                        ("hCursor", HCURSOR),
64                        ("lpszMenuName", LPCTSTR),
65                        ("lpszClassName", LPCTSTR)]
66
67        wndclass = WNDCLASS()
68        wndclass.lpfnWndProc = WNDPROC(wndproc)
69
70        WNDPROC_2 = WINFUNCTYPE(c_long, c_int, c_int, c_int, c_int)
71
72        # This is no longer true, now that WINFUNCTYPE caches created types internally.
73        ## # CFuncPtr subclasses are compared by identity, so this raises a TypeError:
74        ## self.assertRaises(TypeError, setattr, wndclass,
75        ##                  "lpfnWndProc", WNDPROC_2(wndproc))
76        # instead:
77
78        self.assertTrue(WNDPROC is WNDPROC_2)
79        # 'wndclass.lpfnWndProc' leaks 94 references.  Why?
80        self.assertEqual(wndclass.lpfnWndProc(1, 2, 3, 4), 10)
81
82
83        f = wndclass.lpfnWndProc
84
85        del wndclass
86        del wndproc
87
88        self.assertEqual(f(10, 11, 12, 13), 46)
89
90    def test_dllfunctions(self):
91
92        def NoNullHandle(value):
93            if not value:
94                raise WinError()
95            return value
96
97        strchr = lib.my_strchr
98        strchr.restype = c_char_p
99        strchr.argtypes = (c_char_p, c_char)
100        self.assertEqual(strchr("abcdefghi", "b"), "bcdefghi")
101        self.assertEqual(strchr("abcdefghi", "x"), None)
102
103
104        strtok = lib.my_strtok
105        strtok.restype = c_char_p
106        # Neither of this does work: strtok changes the buffer it is passed
107##        strtok.argtypes = (c_char_p, c_char_p)
108##        strtok.argtypes = (c_string, c_char_p)
109
110        def c_string(init):
111            size = len(init) + 1
112            return (c_char*size)(*init)
113
114        s = "a\nb\nc"
115        b = c_string(s)
116
117##        b = (c_char * (len(s)+1))()
118##        b.value = s
119
120##        b = c_string(s)
121        self.assertEqual(strtok(b, "\n"), "a")
122        self.assertEqual(strtok(None, "\n"), "b")
123        self.assertEqual(strtok(None, "\n"), "c")
124        self.assertEqual(strtok(None, "\n"), None)
125
126if __name__ == '__main__':
127    unittest.main()
128