1from PyObjCTools.TestSupport import *
2import objc
3
4setSignature = objc.setSignatureForSelector
5#setSignature(b"OCTestNULL", "callOut:", "i@:o^i")
6#setSignature(b"OCTestNULL", "callList:andInOut2:", "i@:@^i")
7#setSignature(b"OCTestNULL", "callList:andInOut:", "i@:@N^i")
8#setSignature(b"OCTestNULL", "callList:andIn:", "i@:@n^i")
9#setSignature(b"OCTestNULL", "callList:andOut:", "i@:@o^i")
10#setSignature(b"OCTestNULL", "on:callList:andInOut:", "i@:@@N^i")
11#setSignature(b"OCTestNULL", "on:callList:andIn:", "i@:@@n^i")
12#setSignature(b"OCTestNULL", "on:callList:andOut:", "i@:@@N^i") # 'N' is by design
13#setSignature(b"OCTestNULL", "on:callOut:", "v@:@N^i") # 'N' is by design
14
15objc.registerMetaDataForSelector(b"OCTestNULL", b"callOut:", dict(
16            arguments={
17                2: dict(type_modifier=b'o', null_accepted=True),
18            },
19        ))
20objc.registerMetaDataForSelector(b"OCTestNULL", b"callList:andInOut2:", dict(
21            arguments={
22                3: dict(type_modifier=b'o', null_accepted=True),
23            },
24        ))
25objc.registerMetaDataForSelector(b"OCTestNULL", b"callList:andInOut:", dict(
26            arguments={
27                3: dict(type_modifier=b'N', null_accepted=True),
28            },
29        ))
30objc.registerMetaDataForSelector(b"OCTestNULL", b"callList:andIn:", dict(
31            arguments={
32                3: dict(type_modifier=b'n', null_accepted=True),
33            },
34        ))
35objc.registerMetaDataForSelector(b"OCTestNULL", b"callList:andOut:", dict(
36            arguments={
37                3: dict(type_modifier=b'o', null_accepted=True),
38            },
39        ))
40objc.registerMetaDataForSelector(b"OCTestNULL", b"on:callList:andInOut:", dict(
41            arguments={
42                4: dict(type_modifier=b'N', null_accepted=True),
43            },
44        ))
45objc.registerMetaDataForSelector(b"OCTestNULL", b"on:callList:andIn:", dict(
46            arguments={
47                4: dict(type_modifier=b'n', null_accepted=True),
48            },
49        ))
50objc.registerMetaDataForSelector(b"OCTestNULL", b"on:callList:andOut:", dict(
51            arguments={
52                4: dict(type_modifier=b'N', null_accepted=True), # N is by design
53            },
54        ))
55objc.registerMetaDataForSelector(b"OCTestNULL", b"on:callOut:", dict(
56            arguments={
57                3: dict(type_modifier=b'N', null_accepted=True), # N is by design
58            },
59        ))
60
61from PyObjCTest.NULL import *
62
63class TestNULL (TestCase):
64    def testNULL(self):
65        self.assertHasAttr(objc, 'NULL')
66        self.assertEqual(repr(objc.NULL), 'objc.NULL')
67        self.assertRaises(TypeError, type(objc.NULL))
68
69
70class TestNullArgumentsHelper (objc.lookUpClass("NSObject")):
71
72    def callList_andInOut_(self, lst, value):
73        lst.append(str(value))
74        if value is objc.NULL:
75            return (13, value)
76        else:
77            return (13, value * 2)
78
79    callList_andInOut_ = objc.selector(callList_andInOut_,
80            signature=b"i@:@N^i")
81
82    def callList_andInOut2_(self, lst, value):
83        lst.append(repr(value))
84        if value is objc.NULL:
85            return 29
86        else:
87            return 29
88    callList_andInOut2_ = objc.selector(callList_andInOut2_,
89            signature=b"i@:@^i")
90
91    def callList_andIn_(self, lst, value):
92        lst.append(repr(value))
93        return 26
94
95    callList_andIn_ = objc.selector(callList_andIn_,
96            signature=b"i@:@n^i")
97
98    def callList_andOut_(self, lst, value):
99        assert value is None or value is objc.NULL
100        lst.append("Nothing here")
101        return (27, 99)
102    callList_andOut_ = objc.selector(callList_andOut_,
103            signature=b"i@:@o^i")
104
105    def callOut_(self, value):
106        assert value is None or value is objc.NULL
107        return 441;
108    callOut_ = objc.selector(callOut_, signature=b'v@:o^i')
109
110class TestNULLArguments (TestCase):
111    def testCallInOutNULL(self):
112        obj = OCTestNULL.alloc().init()
113
114        v = []
115        rv = obj.callList_andInOut_(v, 42)
116        self.assertEqual(v, ["42"])
117        self.assertEqual(rv, (12, 21))
118
119        v = []
120        rv = obj.callList_andInOut_(v, objc.NULL)
121        self.assertEqual(v, ["NULL"])
122        self.assertEqual(rv,  (12, objc.NULL))
123
124    def testCallInOutNULL2(self):
125        # If nothing is specified the bridge assumes the argument behaves
126        # like an 'in' argument.
127
128        obj = OCTestNULL.alloc().init()
129
130        v = []
131        self.assertRaises(ValueError, obj.callList_andInOut2_, v, 42)
132        self.assertEqual(v, [])
133
134        v = []
135        rv = obj.callList_andInOut2_(v, objc.NULL)
136        self.assertEqual(v, ["NULL"])
137        self.assertEqual(rv,  (12, objc.NULL))
138
139    def testCallInNULL(self):
140        obj = OCTestNULL.alloc().init()
141
142        v = []
143        rv = obj.callList_andIn_(v, 42)
144        self.assertEqual(v, ["42"])
145        self.assertEqual(rv, 24)
146
147        v = []
148        rv = obj.callList_andIn_(v, objc.NULL)
149        self.assertEqual(v, ["NULL"])
150        self.assertEqual(rv,  24)
151
152
153    def testCalledInOutNULL(self):
154        helper = OCTestNULL.alloc().init()
155        obj = TestNullArgumentsHelper.alloc().init()
156
157        v = []
158        rv = helper.on_callList_andInOut_(obj, v, 42)
159        self.assertEqual(v, ['42'])
160        self.assertEqual(rv, (13, 84))
161
162        v = []
163        rv = helper.on_callList_andInOut_(obj, v, objc.NULL)
164        self.assertEqual(v, ['objc.NULL'])
165        self.assertEqual(rv, (13, objc.NULL))
166
167    def testCalledInNULL(self):
168        helper = OCTestNULL.alloc().init()
169        obj = TestNullArgumentsHelper.alloc().init()
170
171        v = []
172        rv = helper.on_callList_andIn_(obj, v, 42)
173        self.assertEqual(v, ['42'])
174        self.assertEqual(rv, 26)
175
176        v = []
177        rv = helper.on_callList_andIn_(obj, v, objc.NULL)
178        self.assertEqual(v, ['objc.NULL'])
179        self.assertEqual(rv, 26)
180
181    def testCalledOutNULL(self):
182
183        helper = OCTestNULL.alloc().init()
184        obj = TestNullArgumentsHelper.alloc().init()
185
186        v = []
187        rv = helper.on_callList_andOut_(obj, v, 42)
188        self.assertEqual(v, ['Nothing here'])
189        self.assertEqual(rv, (27, 99))
190
191        v = []
192        rv = helper.on_callList_andOut_(obj, v, objc.NULL)
193        self.assertEqual(v, ['Nothing here'])
194        self.assertEqual(rv, (27, objc.NULL))
195
196        rv = helper.on_callOut_(obj, 42)
197        self.assertEqual(rv, 441)
198
199        rv = helper.on_callOut_(obj, objc.NULL)
200        self.assertEqual(rv, objc.NULL)
201
202    def dont_testCalledOutNULL(self):
203        """
204        XXX: I'm not happy about these semantics!
205
206        Current semantics: called method doesn't know about the NULL argument,
207        the result from Python is ignored.
208
209        New semantics:
210        - If the last argument is 'out' use new semantics, otherwise keep
211          current semantics
212        - If function has an optional last param stuf this with objc.NULL if
213          the argument is NULL, otherwise don't provide
214        - If the functioin has a required last param: stuff with objc.NULL or
215          None
216        """
217
218
219    def dont_testCallOutNULL(self):
220        """
221        Call a method with an 'out' argument with an additional method
222
223        - if not objc.NULL: raise TypeError
224        - argument should be NULL in objC
225        - result should be objc.NULL
226        """
227
228
229
230
231if __name__ == "__main__":
232    main()
233