1from PyObjCTools.TestSupport import *
2import objc
3
4
5objc.registerMetaDataForSelector(
6    b"OC_CallbackTest", b"selWithSEL:SEL:andObject:", {
7        "arguments": {
8            2:  {
9                "sel_of_type":  b"q@:q",
10            },
11            3:  {
12                "sel_of_type": b"v@:@@^v",
13            }
14        }
15    }
16)
17
18objc.registerMetaDataForSelector(
19    b"OC_CallbackTest", b"selWithCallback:", {
20        "arguments": {
21            2:  {
22                "callable": {
23                    "retval": { "type": b"q", },
24                    "arguments": {
25                        "0":    { "type": b"@", },
26                        "1":    { "type": b"i", },
27                    }
28                }
29            },
30        }
31    }
32)
33
34objc.registerMetaDataForSelector(
35    b"OC_CallbackTest", b"selWithCallback2:", {
36        "arguments": {
37            2:  {
38                "callable": {
39                    "retval": { "type": b"d", },
40                    "arguments": {
41                    }
42                }
43            },
44        }
45    }
46)
47
48objc.registerMetaDataForSelector(
49    b"OC_CallbackTest", b"selWithCallback:andCallback:", {
50        "arguments": {
51            2:  {
52                "callable": {
53                    "arguments": {
54                        "0":    { "type": b"@", },
55                    }
56                }
57            },
58            3:  {
59                "callable": {
60                    "retval":   { "type": b"@", },
61                    "arguments": {
62                        "0":    { "type": b"q", },
63                        "1":    { "type": b"^v", },
64                    }
65                }
66            },
67        }
68    }
69)
70
71class OC_CallbackTest (objc.lookUpClass("NSObject")):
72    @objc.typedSelector(b"v@:" + objc._C_SEL + objc._C_SEL + b"@")
73    def selWithSEL_SEL_andObject_(self, s1, s2, o):
74        pass
75
76    @objc.typedSelector(b"v@:" + objc._C_SEL)
77    def selWithoutSEL_(self, o):
78        pass
79
80    @objc.typedSelector(b"v@:?")
81    def selWithCallback_(self, cb):
82        pass
83
84    @objc.typedSelector(b"v@:?")
85    def selWithCallback2_(self, cb):
86        pass
87
88    @objc.typedSelector(b"v@:??")
89    def selWithCallback_andCallback_(self, cb1, cb2):
90        pass
91
92
93
94
95class TestClosure (TestCase):
96    # tests for objc._makeClosure
97    # (note: as the name indicates _makeClosure is not a public API)
98    def testCallbackForUnsupported(self):
99        def function(arg):
100            pass
101
102        self.assertRaises(TypeError, objc._makeClosure, function, dir, -1)
103        self.assertRaises(TypeError, objc._makeClosure, function, function, -1)
104        self.assertRaises(TypeError, objc._makeClosure, function, 42, -1)
105        self.assertRaises(ValueError, objc._makeClosure, function, OC_CallbackTest.selWithSEL_SEL_andObject_,  -1)
106        self.assertRaises(ValueError, objc._makeClosure, function, OC_CallbackTest.selWithoutSEL_,  -1)
107
108    def testCreatingCallbacks(self):
109        def function(*arg):
110            pass
111        cl = objc._makeClosure(function, OC_CallbackTest.selWithCallback_, -1)
112        self.assertIn(' "objc.__imp__" ', repr(cl))
113        cl = objc._makeClosure(function, OC_CallbackTest.selWithCallback2_, -1)
114        self.assertIn(' "objc.__imp__" ', repr(cl))
115        cl = objc._makeClosure(function, OC_CallbackTest.selWithCallback_andCallback_, -1)
116        self.assertIn(' "objc.__imp__" ', repr(cl))
117        cl = objc._makeClosure(function, OC_CallbackTest.selWithCallback_andCallback_, 2)
118        self.assertIn(' "objc.__imp__" ', repr(cl))
119        cl = objc._makeClosure(function, OC_CallbackTest.selWithCallback_andCallback_, 3)
120        self.assertIn(' "objc.__imp__" ', repr(cl))
121
122        self.assertRaises(objc.BadPrototypeError, objc._makeClosure, lambda a: None, OC_CallbackTest.selWithCallback_, -1)
123        objc._makeClosure(lambda a, b: None, OC_CallbackTest.selWithCallback_, -1)
124        self.assertRaises(objc.BadPrototypeError, objc._makeClosure, lambda a, b, c: None, OC_CallbackTest.selWithCallback_, -1)
125
126    # TODO: Verify that C code has proper coverage
127
128
129class TestCallbackFor (TestCase):
130    # tests for objc.callbackFor
131    def testCallbackForUnsupported(self):
132        def function(arg):
133            pass
134
135        self.assertRaises(TypeError, objc.callbackFor(dir), function)
136        self.assertRaises(TypeError, objc.callbackFor(function), function)
137        self.assertRaises(TypeError, objc.callbackFor(42), function)
138        self.assertRaises(ValueError, objc.callbackFor(OC_CallbackTest.selWithSEL_SEL_andObject_), function)
139        self.assertRaises(ValueError, objc.callbackFor(OC_CallbackTest.selWithoutSEL_), function)
140
141    def testCreatingCallbacks(self):
142        def function(*arg):
143            pass
144
145        @objc.callbackFor(OC_CallbackTest.selWithCallback_)
146        def function(arg1, arg2):
147            pass
148        self.assertIn(' "objc.__imp__" ', repr(function.pyobjc_closure))
149
150        @objc.callbackFor(OC_CallbackTest.selWithCallback2_)
151        def function():
152            pass
153        self.assertIn(' "objc.__imp__" ', repr(function.pyobjc_closure))
154
155        @objc.callbackFor(OC_CallbackTest.selWithCallback_andCallback_, -1)
156        def function(a):
157            pass
158        self.assertIn(' "objc.__imp__" ', repr(function.pyobjc_closure))
159
160        @objc.callbackFor(OC_CallbackTest.selWithCallback_andCallback_, 2)
161        def function(a):
162            pass
163        self.assertIn(' "objc.__imp__" ', repr(function.pyobjc_closure))
164
165        @objc.callbackFor(OC_CallbackTest.selWithCallback_andCallback_, 3)
166        def function(a, b):
167            pass
168        self.assertIn(' "objc.__imp__" ', repr(function.pyobjc_closure))
169
170        self.assertRaises(objc.BadPrototypeError, objc.callbackFor(OC_CallbackTest.selWithCallback_), lambda a: None)
171        self.assertRaises(objc.BadPrototypeError, objc.callbackFor(OC_CallbackTest.selWithCallback_), lambda a,b,c: None)
172
173    # TODO: add tests that actually call the callback
174
175class TestSelectorFor (TestCase):
176    # tests for objc.selectorFor
177    def test_consistency(self):
178        self.assertEqual(OC_CallbackTest.selWithSEL_SEL_andObject_.signature,
179                b"v@:::@")
180
181    def testNotSelector(self):
182        self.assertRaises(ValueError, objc.selectorFor, OC_CallbackTest.selWithSEL_SEL_andObject_, 4)
183        self.assertRaises(ValueError, objc.selectorFor, OC_CallbackTest.selWithSEL_SEL_andObject_, 8)
184        self.assertRaises(ValueError, objc.selectorFor, OC_CallbackTest.selWithoutSEL_)
185
186    def testDefault(self):
187        @objc.selectorFor(OC_CallbackTest.selWithSEL_SEL_andObject_)
188        def selector(self, a):
189            pass
190
191        self.assertEqual(selector.signature, b"q@:q")
192
193    def testExplicit(self):
194
195        @objc.selectorFor(OC_CallbackTest.selWithSEL_SEL_andObject_, 2)
196        def selector(self, a):
197            pass
198
199        self.assertEqual(selector.signature, b"q@:q")
200
201        @objc.selectorFor(OC_CallbackTest.selWithSEL_SEL_andObject_, 3)
202        def selector(self, a):
203            pass
204
205        self.assertEqual(selector.signature, b"v@:@@^v")
206
207
208if __name__ == "__main__":
209    main()
210