1"""
2Tests for the new-style metadata format interface.
3
4Note: Tests for calling from python into ObjC are in test_metadata.py
5
6TODO:
7- Add more testcases: python methods that return the wrong value
8- The python->C interface (that is the contents of the metadata object) is
9  likely to change when the bridge is feature-complete.
10- Probably need special-casing for arrays (numarray and array.array)!
11"""
12import objc
13from PyObjCTools.TestSupport import *
14import warnings
15
16from PyObjCTest.metadata import *
17
18# To ensure we have the right metadata
19import PyObjCTest.test_metadata
20
21def setupMetaData():
22    # Note to self: what we think of as the first argument of a method is
23    # actually the third one, the objc runtime implicitly passed 'self' and
24    # the selector as well. Therefore we need to start counting at 2 instead
25    # of 0.
26    #
27    # Note2: the code below would normally be done using a metadata file
28    # instead of hardcoding.
29
30    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"make4Tuple:on:",
31            dict(
32                arguments={
33                  2+0:  dict(type_modifier=objc._C_IN, c_array_of_fixed_length=4, null_accepted=False),
34                }
35            )
36        )
37    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"null4Tuple:on:",
38            dict(
39                arguments={
40                  2+0:  dict(type_modifier=objc._C_IN, c_array_of_fixed_length=4, null_accepted=True),
41                }
42            )
43        )
44
45    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"makeObjectArray:on:",
46            dict(
47                arguments={
48                  2+0:  dict(type_modifier=objc._C_IN, c_array_delimited_by_null=True, null_accepted=False),
49                }
50            )
51        )
52    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"makeStringArray:on:",
53            dict(
54                arguments={
55                  2+0:  dict(type_modifier=objc._C_IN, c_array_delimited_by_null=True, null_accepted=False),
56                }
57            )
58        )
59    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullStringArray:on:",
60            dict(
61                arguments={
62                  2+0:  dict(type_modifier=objc._C_IN, c_array_delimited_by_null=True, null_accepted=True),
63                }
64            )
65        )
66    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"makeIntArray:count:on:",
67            dict(
68                arguments={
69                  2+0:  dict(type_modifier=objc._C_IN, c_array_length_in_arg=2+1, null_accepted=False),
70                }
71            )
72        )
73    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"makeIntArray:countPtr:on:",
74            dict(
75                arguments={
76                  2+0:  dict(type_modifier=objc._C_IN, c_array_length_in_arg=2+1, null_accepted=False),
77                  2+1:  dict(type_modifier=objc._C_IN, null_accepted=False),
78                }
79            )
80        )
81    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullIntArray:count:on:",
82            dict(
83                arguments={
84                  2+0:  dict(type_modifier=objc._C_IN, c_array_length_in_arg=2+1, null_accepted=True),
85                }
86            )
87        )
88
89    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"fillArray:uptoCount:on:",
90            dict(
91                arguments={
92                    2+0: dict(type_modifier=objc._C_OUT, c_array_length_in_arg=2+1, c_array_length_in_result=True, null_accepted=False),
93                }
94            )
95        )
96
97    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"fillArray:count:on:",
98            dict(
99                arguments={
100                    2+0: dict(type_modifier=objc._C_OUT, c_array_length_in_arg=2+1, null_accepted=False),
101                }
102            )
103        )
104    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullfillArray:count:on:",
105            dict(
106                arguments={
107                    2+0: dict(type_modifier=objc._C_OUT, c_array_length_in_arg=2+1, null_accepted=True),
108                }
109            )
110        )
111
112    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"maybeFillArray:on:",
113            dict(
114                arguments={
115                    2+0: dict(type_modifier=objc._C_OUT, c_array_of_fixed_length=4, c_array_length_in_result=True, null_accepted=False),
116                }
117            )
118        )
119    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"fill4Tuple:on:",
120            dict(
121                arguments={
122                    2+0: dict(type_modifier=objc._C_OUT, c_array_of_fixed_length=4, null_accepted=False),
123                }
124            )
125        )
126    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullfill4Tuple:on:",
127            dict(
128                arguments={
129                    2+0: dict(type_modifier=objc._C_OUT, c_array_of_fixed_length=4, null_accepted=True),
130                }
131            )
132        )
133    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"fillStringArray:on:",
134            dict(
135                arguments={
136                    2+0: dict(type_modifier=objc._C_OUT, c_array_delimited_by_null=True, null_accepted=False),
137                }
138            )
139        )
140    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullfillStringArray:on:",
141            dict(
142                arguments={
143                    2+0: dict(type_modifier=objc._C_OUT, c_array_delimited_by_null=True, null_accepted=True),
144                }
145            )
146        )
147
148    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"reverseArray:uptoCount:on:",
149            dict(
150                arguments={
151                    2+0: dict(type_modifier=objc._C_INOUT, c_array_length_in_arg=2+1, c_array_length_in_result=True, null_accepted=False),
152                }
153            )
154        )
155    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"reverseArray:count:on:",
156            dict(
157                arguments={
158                    2+0: dict(type_modifier=objc._C_INOUT, c_array_length_in_arg=2+1, null_accepted=False),
159                }
160            )
161        )
162    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullreverseArray:count:on:",
163            dict(
164                arguments={
165                    2+0: dict(type_modifier=objc._C_INOUT, c_array_length_in_arg=2+1, null_accepted=True),
166                }
167            )
168        )
169
170    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"reverseStrings:on:",
171            dict(
172                arguments={
173                    2+0: dict(type_modifier=objc._C_INOUT, c_array_delimited_by_null=True, null_accepted=False),
174                }
175            )
176        )
177    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullreverseStrings:on:",
178            dict(
179                arguments={
180                    2+0: dict(type_modifier=objc._C_INOUT, c_array_delimited_by_null=True, null_accepted=True),
181                }
182            )
183        )
184
185    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"maybeReverseArray:on:",
186            dict(
187                arguments={
188                    2+0: dict(type_modifier=objc._C_INOUT, c_array_of_fixed_length=4, c_array_length_in_result=True, null_accepted=True),
189                }
190            )
191        )
192    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"reverse4Tuple:on:",
193            dict(
194                arguments={
195                    2+0: dict(type_modifier=objc._C_INOUT, c_array_of_fixed_length=4, null_accepted=False),
196                }
197            )
198        )
199    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullreverse4Tuple:on:",
200            dict(
201                arguments={
202                    2+0: dict(type_modifier=objc._C_INOUT, c_array_of_fixed_length=4, null_accepted=True),
203                }
204            )
205        )
206
207
208
209
210    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"makeIntArrayOf5On:",
211            dict(
212                retval=dict(c_array_of_fixed_length=5)
213            ),
214        )
215
216    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"makeStringArrayOn:",
217            dict(
218                retval=dict(c_array_delimited_by_null=True),
219            ),
220        )
221
222    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"makeIntArrayOf:on:",
223            dict(
224                retval=dict(c_array_length_in_arg=2+0)
225            ),
226        )
227
228    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullIntArrayOf5On:",
229            dict(
230                retval=dict(c_array_of_fixed_length=5)
231            ),
232        )
233
234    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullStringArrayOn:",
235            dict(
236                retval=dict(c_array_delimited_by_null=True),
237            ),
238        )
239
240    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"nullIntArrayOf:on:",
241            dict(
242                retval=dict(c_array_length_in_arg=2+0)
243            ),
244        )
245
246
247    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"sumX:andY:on:",
248            dict(arguments={
249                    2+0: dict(type_modifier=objc._C_IN, null_accepted=False),
250                    2+1: dict(type_modifier=objc._C_IN, null_accepted=False),
251                }))
252    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"divBy5:remainder:on:",
253            dict(arguments={
254                    2+1: dict(type_modifier=objc._C_OUT, null_accepted=False),
255                }))
256    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"swapX:andY:on:",
257            dict(arguments={
258                    2+0: dict(type_modifier=objc._C_INOUT, null_accepted=False),
259                    2+1: dict(type_modifier=objc._C_INOUT, null_accepted=False),
260                }))
261    objc.registerMetaDataForSelector(b"OC_MetaDataTest", b"input:output:inputAndOutput:on:",
262            dict(arguments={
263                    2+0: dict(type_modifier=objc._C_IN, null_accepted=True),
264                    2+1: dict(type_modifier=objc._C_OUT, null_accepted=True),
265                    2+2: dict(type_modifier=objc._C_INOUT, null_accepted=True),
266            }))
267
268
269setupMetaData()
270
271class Py_MetaDataTest_AllArgs (OC_MetaDataTest):
272    # Return value arrays:
273    def makeIntArrayOf5(self):
274        return [100, 200, 300, 400, 500]
275
276    def makeStringArray(self):
277        return [ b"jaap", b"pieter", b"hans" ]
278
279    def makeIntArrayOf_(self, count):
280        return [ i + 20 for i in range(count) ]
281
282    def nullIntArrayOf5(self):
283        return objc.NULL
284
285    def nullStringArray(self):
286        return objc.NULL
287
288    def nullIntArrayOf_(self, count):
289        return objc.NULL
290
291    # In arrays:
292    def makeIntArray_count_(self, data, count): return [data, count]
293    def makeIntArray_countPtr_(self, data, count): return [data, count]
294    def nullIntArray_count_(self, data, count): return [data, count]
295    def makeStringArray_(self, data): return [[ x.decode('latin1') for x in data]]
296    def makeObjectArray_(self, data): return [data]
297    def nullStringArray_(self, data): return [data]
298    def make4Tuple_(self, data): return [data]
299    def null4Tuple_(self, data): return [data]
300
301    # Out arrays:
302    def fillArray_count_(self, data, count):
303        if data is None:
304            return range(10, count+10)
305        else:
306            return range(20, count+20)
307
308    def nullfillArray_count_(self, data, count):
309        if data is objc.NULL:
310            return 1, None
311
312        elif data is None:
313            return 2, range(30, count+30)
314
315        else:
316            return 3, range(40, count+40)
317
318    def fill4Tuple_(self, data):
319        if data is None:
320            return range(9, 13)
321        else:
322            return range(14, 18)
323
324    def nullfill4Tuple_(self, data):
325        if data is None:
326            return 1, range(1, 5)
327        elif data is objc.NULL:
328            return 2, range(6, 10)
329        else:
330            return 3, range(100, 104)
331
332    def fillArray_uptoCount_(self, data, count):
333        if data is None:
334            return int(count/2), range(10, 10 + int(count/2))
335        else:
336            return int(count/2), range(15, 15 + int(count/2))
337
338    def maybeFillArray_(self, data):
339        if data is None:
340            return 2, range(2)
341        else:
342            return 2, range(2, 4)
343
344    def fillStringArray_(self, data):
345        raise RuntimeError("Should not reach this")
346
347    def nullfillStringArray_(self, data):
348        if data is objc.NULL:
349            return 9, objc.NULL
350
351        raise RuntimeError("Should not reach this")
352
353    # In/out arrays:
354    def reverseArray_count_(self, data, count):
355        if count == len(data):
356            x = 1
357        else:
358            x = 2
359
360        data = list(data)
361        for i in range(len(data)):
362            data[i] += x
363
364        return data
365
366    def nullreverseArray_count_(self, data, count):
367        if data is objc.NULL:
368            return 2, objc.NULL
369
370        else:
371            data = list(data)
372            for i in range(len(data)):
373                data[i] += 42
374            return 9, data
375
376    def reverseStrings_(self, data):
377        data = list(data)
378        for i in range(len(data)):
379            data[i] = data[i][::-1]
380        return data
381
382    def nullreverseStrings_(self, data):
383        if data is objc.NULL:
384            return 9, objc.NULL
385
386        else:
387            data = list(data)
388            for i in range(len(data)):
389                data[i] = data[i][::-1]
390            return 10, data
391
392    def reverse4Tuple_(self, data):
393        data = list(data)
394        for i in range(len(data)):
395            data[i] += 42
396        return data
397
398    def nullreverse4Tuple_(self, data):
399        if data is objc.NULL:
400            return -1, objc.NULL
401
402        else:
403            data = list(data)
404            for i in range(len(data)):
405                data[i] += 42
406            return 1, data
407
408    def reverseArray_uptoCount_(self, data, count):
409        data = list(data)
410        for i in range(int(len(data)/2)):
411            data[i] = data[i] * 10
412
413        return count/2, data
414
415    def maybeReverseArray_(self, data):
416        return 2, (data[0] + 44, data[1] + 49)
417
418    # pass-by-reference
419    def sumX_andY_(self, x, y):
420        return x ** 2 + y ** 2
421
422    def divBy5_remainder_(self, v, r):
423        if r is None:
424            return v / 7, v % 7
425        else:
426            return v / 9, v % 9
427
428    def swapX_andY_(self, x, y):
429        return y * 2, x * 2
430
431    def input_output_inputAndOutput_(self, x, y, z):
432        if x is not objc.NULL and y is not objc.NULL and z is not objc.NULL:
433            return [x, y, z], 9, 10
434
435        elif x is objc.NULL:
436            return [x, y, z], 11, 12
437
438        elif y is objc.NULL:
439            return [x, y, z], 13, 14
440
441        elif z is objc.NULL:
442            return [x, y, z], 15, 16
443
444class TestArrayDefault (TestCase):
445    # TODO: what is the default anyway?
446    pass
447
448class TestArraysOut (TestCase):
449    def testFixedSize(self):
450        o = Py_MetaDataTest_AllArgs.new()
451
452        v = OC_MetaDataTest.fill4Tuple_on_(None, o)
453        self.assertEqual(list(v), list(range(9, 13)))
454
455        v = OC_MetaDataTest.fill4Tuple_on_(None, o)
456        self.assertEqual(list(v), list(range(9, 13)))
457
458        self.assertRaises(ValueError, OC_MetaDataTest.fill4Tuple_on_, objc.NULL, o)
459
460        n, v = OC_MetaDataTest.nullfill4Tuple_on_(None, o)
461        self.assertEqual(n, 1)
462        self.assertEqual(list(v), list(range(1, 5)))
463
464        n, v = OC_MetaDataTest.nullfill4Tuple_on_(None, o)
465        self.assertEqual(n, 1)
466        self.assertEqual(list(v), list(range(1, 5)))
467
468        n, v = OC_MetaDataTest.nullfill4Tuple_on_(objc.NULL, o)
469        self.assertEqual(n, 2)
470        self.assertIs(v, objc.NULL)
471
472    def testNullTerminated(self):
473        o = Py_MetaDataTest_AllArgs.new()
474
475        # Output only arrays of null-terminated arrays cannot be
476        # wrapped automaticly. How is the bridge supposed to know
477        # how much memory it should allocate for the C-array?
478        self.assertRaises(TypeError, OC_MetaDataTest.fillStringArray_on_, None, o)
479        self.assertRaises(ValueError, OC_MetaDataTest.fillStringArray_on_, objc.NULL, o)
480
481        self.assertRaises(TypeError, OC_MetaDataTest.nullfillStringArray_on_, o)
482        self.assertRaises(TypeError, OC_MetaDataTest.nullfillStringArray_on_, None, o)
483        n, v = OC_MetaDataTest.nullfillStringArray_on_(objc.NULL, o)
484        self.assertEqual(n, 9)
485        self.assertIs(v, objc.NULL)
486
487    def testWithCount(self):
488        o = Py_MetaDataTest_AllArgs.new()
489
490        v = OC_MetaDataTest.fillArray_count_on_(None, 3, o)
491        self.assertEqual(list(v),  [10, 11, 12])
492
493        v = OC_MetaDataTest.fillArray_count_on_(None, 5, o)
494        self.assertEqual(list(v),  [10, 11, 12, 13, 14])
495
496        v = OC_MetaDataTest.fillArray_count_on_(None, 0, o)
497        self.assertEqual(list(v),  [])
498
499        self.assertRaises(ValueError, OC_MetaDataTest.fillArray_count_on_, objc.NULL, 0, o)
500
501        n, v = OC_MetaDataTest.nullfillArray_count_on_(None, 3, o)
502        self.assertEqual(n, 2)
503        self.assertEqual(list(v),  [30,31,32])
504
505
506        n, v = OC_MetaDataTest.nullfillArray_count_on_(objc.NULL, 3, o)
507        self.assertEqual(n, 1)
508        self.assertIs(v, objc.NULL)
509
510    def testWithCountInResult(self):
511        o = Py_MetaDataTest_AllArgs.new()
512
513        c, v = OC_MetaDataTest.fillArray_uptoCount_on_(None, 20, o)
514        self.assertEqual(c, 10)
515        self.assertEqual(list(v),  [i+10 for i in range(10)])
516
517        c, v = OC_MetaDataTest.maybeFillArray_on_(None, o)
518        self.assertEqual(c, 2)
519        self.assertEqual(list(v),  [0, 1])
520
521class TestArraysInOut (TestCase):
522    def testFixedSize(self):
523        o = Py_MetaDataTest_AllArgs.new()
524
525        a = (1,2,3,4)
526        v = OC_MetaDataTest.reverse4Tuple_on_(a, o)
527        self.assertEqual(a, (1,2,3,4))
528        self.assertEqual(v, (43, 44, 45, 46))
529
530        self.assertRaises(ValueError, OC_MetaDataTest.reverse4Tuple_on_, (1,2,3), o)
531        self.assertRaises(ValueError, OC_MetaDataTest.reverse4Tuple_on_, (1,2,3,4,5), o)
532        self.assertRaises(ValueError, OC_MetaDataTest.reverse4Tuple_on_, objc.NULL, o)
533
534        a = (1,2,3,4)
535        n, v = OC_MetaDataTest.nullreverse4Tuple_on_(a, o)
536        self.assertEqual(n, 1)
537        self.assertEqual(a, (1,2,3,4))
538        self.assertEqual(v, (43, 44, 45, 46))
539
540        n, v = OC_MetaDataTest.nullreverse4Tuple_on_(objc.NULL, o)
541        self.assertEqual(n, -1)
542        self.assertIs(v, objc.NULL)
543
544    def testNullTerminated(self):
545        o = Py_MetaDataTest_AllArgs.new()
546
547        a = (b'aap', b'boot', b'cello')
548        v = OC_MetaDataTest.reverseStrings_on_(a, o)
549        self.assertEqual(a, (b'aap', b'boot', b'cello'))
550        self.assertEqual(v, (b'paa', b'toob', b'ollec'))
551
552        self.assertRaises(ValueError, OC_MetaDataTest.reverseStrings_on_, (1,2), o)
553        self.assertRaises(ValueError, OC_MetaDataTest.reverseStrings_on_, objc.NULL, o)
554
555        a = (b'aap', b'boot', b'cello')
556        n, v = OC_MetaDataTest.nullreverseStrings_on_(a, o)
557        self.assertEqual(n, 10)
558        self.assertEqual(a, (b'aap', b'boot', b'cello'))
559        self.assertEqual(v, (b'paa', b'toob', b'ollec'))
560
561        n, v = OC_MetaDataTest.nullreverseStrings_on_(objc.NULL, o)
562        self.assertEqual(n, 9)
563        self.assertIs(v, objc.NULL)
564
565    def testWithCount(self):
566        o = Py_MetaDataTest_AllArgs.new()
567
568        a = (1.0, 2.0, 3.0, 4.0, 5.0)
569        v = OC_MetaDataTest.reverseArray_count_on_(a, 4, o)
570        self.assertEqual(a, (1.0, 2.0, 3.0, 4.0, 5.0))
571        self.assertEqual(v, (2.0, 3.0, 4.0, 5.0))
572
573        a = (1.0, 2.0, 3.0, 4.0, 5.0)
574        v = OC_MetaDataTest.reverseArray_count_on_(a, 5, o)
575        self.assertEqual(a, (1.0, 2.0, 3.0, 4.0, 5.0))
576        self.assertEqual(v, (2.0, 3.0, 4.0, 5.0, 6.0))
577
578        # Nice to have, but doesn't work without major
579        # surgery:
580        #a = (1.0, 2.0, 3.0, 4.0, 5.0)
581        #v = OC_MetaDataTest.reverseArray_count_on_(a, None, o)
582        #self.assertEqual(a, (1.0, 2.0, 3.0, 4.0, 5.0))
583        #self.assertEqual(v, (2.0, 3.0, 4.0, 5.0, 6.0))
584
585        self.assertRaises(ValueError, OC_MetaDataTest.reverseArray_count_on_, (1.0, 2.0), 5, o)
586        self.assertRaises(ValueError, OC_MetaDataTest.reverseArray_count_on_, objc.NULL, 0, o)
587
588        a = (1.0, 2.0, 3.0, 4.0, 5.0)
589        n, v = OC_MetaDataTest.nullreverseArray_count_on_(a, 5, o)
590        self.assertEqual(n, 9)
591        self.assertEqual(a, (1.0, 2.0, 3.0, 4.0, 5.0))
592        self.assertEqual(v, (43.0, 44.0, 45.0, 46.0, 47.0))
593
594        n, v = OC_MetaDataTest.nullreverseArray_count_on_(objc.NULL, 0, o)
595        self.assertEqual(n, 2)
596        self.assertIs(v, objc.NULL)
597
598    def testWithCountInResult(self):
599        o = Py_MetaDataTest_AllArgs.new()
600
601        c, v = OC_MetaDataTest.reverseArray_uptoCount_on_(range(10), 10, o)
602        self.assertEqual(c, 5)
603        self.assertEqual(len(v), 5)
604        self.assertEqual(list(v),  [0, 10, 20, 30, 40])
605
606        c, v = OC_MetaDataTest.maybeReverseArray_on_([1,2,3,4], o)
607        self.assertEqual(c, 2)
608        self.assertEqual(len(v), 2)
609        self.assertEqual(list(v),  [45, 51])
610
611class TestArraysIn (TestCase):
612    def testFixedSize(self):
613        o = Py_MetaDataTest_AllArgs.new()
614
615        v,  = OC_MetaDataTest.make4Tuple_on_((1.0, 4.0, 8.0, 12.5), o)
616        self.assertEqual(len(v), 4)
617        self.assertEqual(list(v), [1.0, 4.0, 8.0, 12.5])
618
619        v, = OC_MetaDataTest.make4Tuple_on_((1, 2, 3, 4), o)
620        self.assertEqual(len(v), 4)
621        self.assertEqual(list(v), [1.0, 2.0, 3.0, 4.0])
622
623        self.assertRaises(ValueError, OC_MetaDataTest.make4Tuple_on_, (1, 2, 3), o)
624        self.assertRaises(ValueError, OC_MetaDataTest.make4Tuple_on_, (1, 2, 3, 4, 5), o)
625        self.assertRaises(ValueError, OC_MetaDataTest.make4Tuple_on_, objc.NULL, o)
626
627        v, = OC_MetaDataTest.null4Tuple_on_(objc.NULL, o)
628        self.assertIs(v, objc.NULL)
629
630    def testNullTerminated(self):
631        o = Py_MetaDataTest_AllArgs.new()
632
633        v, = OC_MetaDataTest.makeStringArray_on_((b"hello", b"world", b"there"), o)
634        self.assertEqual(len(v), 3)
635        self.assertEqual(list(v), ["hello", "world", "there"])
636
637        NSObject = objc.lookUpClass('NSObject')
638        p, q = NSObject.new(), NSObject.new()
639        v, = o.makeObjectArray_((p, q))
640        self.assertEqual(len(v), 2)
641        self.assertIs(v[0], p)
642        self.assertIs(v[1], q)
643
644        v, = OC_MetaDataTest.makeStringArray_on_((), o)
645        self.assertEqual(len(v), 0)
646
647        self.assertRaises(ValueError, OC_MetaDataTest.makeStringArray_on_, [1,2], o)
648        self.assertRaises(ValueError, OC_MetaDataTest.makeStringArray_on_, objc.NULL, o)
649
650        v, = OC_MetaDataTest.nullStringArray_on_(objc.NULL, o)
651        self.assertEqual(v, objc.NULL)
652
653    def testWithCount(self):
654        o = Py_MetaDataTest_AllArgs.new()
655
656        v, c = OC_MetaDataTest.makeIntArray_count_on_((1,2,3,4), 3, o)
657        self.assertEqual(c, 3)
658        self.assertEqual(len(v), 3)
659        self.assertEqual(list(v), [1,2,3])
660
661        # XXX: This one would be nice to have, but not entirely trivial
662        #v, c = OC_MetaDataTest.makeIntArray_count_on_((1,2,3,4), None, o)
663        #self.assertEqual(c, 3)
664        #self.assertEqual(len(v), 3)
665        #self.assertEqual(list(v), [1,2,3,4])
666
667        self.assertRaises(ValueError, OC_MetaDataTest.makeIntArray_count_on_, [1,2,3], 4, o)
668        self.assertRaises(ValueError, OC_MetaDataTest.makeIntArray_count_on_, objc.NULL, 0, o)
669        self.assertRaises(ValueError, OC_MetaDataTest.makeIntArray_count_on_, objc.NULL, 1, o)
670
671        v, c = OC_MetaDataTest.nullIntArray_count_on_(objc.NULL, 0, o)
672        self.assertEqual(c, 0)
673        self.assertEqual(v, objc.NULL)
674
675        self.assertRaises(ValueError, OC_MetaDataTest.makeIntArray_count_on_, objc.NULL, 1, o)
676
677        # Make sure this also works when the length is in a pass-by-reference argument
678        v, c = OC_MetaDataTest.makeIntArray_countPtr_on_((1,2,3,4), 4, o)
679        self.assertEqual(c, 4)
680        self.assertEqual(len(v), 4)
681        self.assertEqual(list(v), [1,2,3,4])
682
683class TestArrayReturns (TestCase):
684    # TODO:
685    # - Add null-terminated arrays of various supported types:
686    #   -> integers
687    #   -> CF-types
688    def testFixedSize(self):
689        o = Py_MetaDataTest_AllArgs.new()
690
691        v = OC_MetaDataTest.makeIntArrayOf5On_(o)
692        self.assertEqual( len(v), 5 )
693        self.assertEqual( list(v), [100, 200, 300, 400, 500] )
694
695        v = OC_MetaDataTest.nullIntArrayOf5On_(o)
696        self.assertEqual(v, objc.NULL)
697
698    def testSizeInArgument(self):
699        o = Py_MetaDataTest_AllArgs.new()
700        v = OC_MetaDataTest.makeIntArrayOf_on_(3, o)
701        self.assertEqual(len(v), 3)
702        self.assertEqual(list(v), [20, 21, 22])
703
704        v = OC_MetaDataTest.makeIntArrayOf_on_(10, o)
705        self.assertEqual(len(v), 10)
706        self.assertEqual(list(v), list(range(20, 30)))
707
708        v = OC_MetaDataTest.nullIntArrayOf_on_(100, o)
709        self.assertEqual(v, objc.NULL)
710
711    def testNULLterminated(self):
712        o  = Py_MetaDataTest_AllArgs.new()
713
714        v = OC_MetaDataTest.makeStringArrayOn_(o)
715        self.assertEqual(len(v), 3)
716        self.assertEqual(list(v), [ b"jaap", b"pieter", b"hans" ])
717
718        v = OC_MetaDataTest.nullStringArrayOn_(o)
719        self.assertEqual(v, objc.NULL)
720
721class TestByReference (TestCase):
722    # Pass by reference arguments.
723    # Note that these tests aren't exhaustive, we have test_methods and
724    # test_methods2 for that :-)
725
726    def testInput(self):
727        o = Py_MetaDataTest_AllArgs.new()
728
729        r = OC_MetaDataTest.sumX_andY_on_(1, 2, o)
730        self.assertEqual(r, 1**2+2**2)
731
732        r = OC_MetaDataTest.sumX_andY_on_(2535, 5325, o)
733        self.assertEqual(r, 2535**2 + 5325**2)
734
735        self.assertRaises(ValueError, OC_MetaDataTest.sumX_andY_on_, 42, objc.NULL, o)
736
737    def testOutput(self):
738        o = Py_MetaDataTest_AllArgs.new()
739
740        div, rem = OC_MetaDataTest.divBy5_remainder_on_(55, None, o)
741        self.assertEqual(div, int(55 / 7))
742        self.assertEqual(rem, int(55 % 7))
743
744        div, rem = OC_MetaDataTest.divBy5_remainder_on_(13, None, o)
745        self.assertEqual(div, int(13 / 7))
746        self.assertEqual(rem, int(13 % 7))
747
748        self.assertRaises(ValueError, OC_MetaDataTest.divBy5_remainder_on_, 42, objc.NULL, o)
749
750    def testInputOutput(self):
751        o = Py_MetaDataTest_AllArgs.new()
752        x, y = OC_MetaDataTest.swapX_andY_on_(42, 284, o)
753        self.assertEqual(x, 284*2)
754        self.assertEqual(y, 42*2)
755
756        self.assertRaises(ValueError, OC_MetaDataTest.swapX_andY_on_, 42, objc.NULL, o)
757
758    def testNullAccepted(self):
759        # Note: the commented-out test-cases require a change in the pyobjc-core
760        o = Py_MetaDataTest_AllArgs.new();
761
762        def makeNum(value):
763            return int(value, 0)
764
765        # All arguments present
766        r, y, z = OC_MetaDataTest.input_output_inputAndOutput_on_(1, None, 2, o)
767        self.assertEqual(len(r), 3)
768        self.assertEqual(len(list(filter(lambda x: x is not objc.NULL, r))), 3)
769        self.assertEqual(y, 9)
770        self.assertEqual(z, 10)
771
772        r, y, z = OC_MetaDataTest.input_output_inputAndOutput_on_(1, None, 2, o)
773        self.assertEqual(len(r), 3)
774        self.assertEqual(len(list(filter(lambda x: x is not objc.NULL, r))), 3)
775        self.assertEqual(y, 9)
776        self.assertEqual(z, 10)
777
778        # Argument 1 is NULL
779        r, y, z = OC_MetaDataTest.input_output_inputAndOutput_on_(objc.NULL, None, 2, o)
780        self.assertEqual(len(r), 3)
781        self.assertEqual(len(list(filter(lambda x: x is not objc.NULL, r))), 2)
782        self.assertEqual(y, 11)
783        self.assertEqual(z, 12)
784
785        r, y, z = OC_MetaDataTest.input_output_inputAndOutput_on_(objc.NULL, None, 2, o)
786        self.assertEqual(len(r), 3)
787        self.assertEqual(len(list(filter(lambda x: x is not objc.NULL, r))), 2)
788        self.assertEqual(y, 11)
789        self.assertEqual(z, 12)
790
791        # Argument 2 is NULL
792        r, y, z = OC_MetaDataTest.input_output_inputAndOutput_on_(1, objc.NULL, 2, o)
793        self.assertEqual(len(r), 3)
794        self.assertEqual(len(list(filter(lambda x: x is not objc.NULL, r))), 2)
795        self.assertEqual(y, objc.NULL)
796        self.assertEqual(z, 14)
797
798        # Argument 3 is NULL
799        r, y, z = OC_MetaDataTest.input_output_inputAndOutput_on_(1, None, objc.NULL, o)
800        self.assertEqual(len(r), 3)
801        self.assertEqual(len(list(filter(lambda x: x is not objc.NULL, r))), 2)
802        self.assertEqual(y, 15)
803        self.assertEqual(z, objc.NULL)
804
805        r, y, z = OC_MetaDataTest.input_output_inputAndOutput_on_(1, None, objc.NULL, o)
806        self.assertEqual(len(r), 3)
807        self.assertEqual(len(list(filter(lambda x: x is not objc.NULL, r))), 2)
808        self.assertEqual(y, 15)
809        self.assertEqual(z, objc.NULL)
810
811if __name__ == "__main__":
812    main()
813