1"""
2Tests for the new-style metadata format interface.
3
4These tests are for global function
5"""
6from __future__ import unicode_literals
7import objc
8from PyObjCTools.TestSupport import *
9import warnings
10
11from PyObjCTest.metadatafunction import *
12
13import sys
14if sys.version_info[0] == 3:
15    unicode = str
16
17_FunctionTable = [
18    ("makeArrayWithFormat_", b'@@', '',
19            dict(
20                variadic=True,
21                arguments={
22                    0: dict(printf_format=True),
23                }
24            )),
25
26    ("makeArrayWithCFormat_", b'@*', '',
27            dict(
28                variadic=True,
29                arguments={
30                    0: dict(printf_format=True),
31                }
32            )),
33
34    ("make4Tuple_", b'@^d', '',
35            dict(
36                arguments={
37                  0:  dict(type_modifier=objc._C_IN, c_array_of_fixed_length=4, null_accepted=False),
38                }
39            )),
40
41    ("null4Tuple_", b'@^d', '',
42            dict(
43                arguments={
44                  0:  dict(type_modifier=objc._C_IN, c_array_of_fixed_length=4, null_accepted=True),
45                }
46            )),
47
48    ("makeObjectArray_", b'@^@', '',
49            dict(
50                arguments={
51                  0:  dict(type_modifier=objc._C_IN, c_array_delimited_by_null=True, null_accepted=False),
52                }
53            )),
54
55    ("makeStringArray_", b'@^*', '',
56            dict(
57                arguments={
58                  0:  dict(type_modifier=objc._C_IN, c_array_delimited_by_null=True, null_accepted=False),
59                }
60            )),
61
62    ("nullStringArray_", b'@^*', '',
63            dict(
64                arguments={
65                  0:  dict(type_modifier=objc._C_IN, c_array_delimited_by_null=True, null_accepted=True),
66                }
67            )),
68
69    ("makeIntArray_count_", b'@^iI', '',
70            dict(
71                arguments={
72                  0:  dict(type_modifier=objc._C_IN, c_array_length_in_arg=1, null_accepted=False),
73                }
74            )),
75
76    ("makeIntArray_countPtr_", b'@^i^I', '',
77            dict(
78                arguments={
79                  0:  dict(type_modifier=objc._C_IN, c_array_length_in_arg=1, null_accepted=False),
80                  1:  dict(type_modifier=objc._C_IN),
81                }
82            )),
83
84    ("nullIntArray_count_", b'@^iI', '',
85            dict(
86                arguments={
87                  0:  dict(type_modifier=objc._C_IN, c_array_length_in_arg=1, null_accepted=True),
88                }
89            )),
90
91    ("fillArray_uptoCount_", b'i^ii', '',
92            dict(
93                arguments={
94                    0: dict(type_modifier=objc._C_OUT, c_array_length_in_arg=1, c_array_length_in_result=True, null_accepted=False),
95                }
96            )),
97
98    ("fillArray_count_", b'v^ii', '',
99            dict(
100                arguments={
101                    0: dict(type_modifier=objc._C_OUT, c_array_length_in_arg=1, null_accepted=False),
102                }
103            )),
104
105    ("nullfillArray_count_", b'i^ii', '',
106            dict(
107                arguments={
108                    0: dict(type_modifier=objc._C_OUT, c_array_length_in_arg=1, null_accepted=True),
109                }
110            )),
111
112    ("maybeFillArray_", b'i^i', '',
113            dict(
114                arguments={
115                    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    ("fill4Tuple_", b'v^i', '',
120            dict(
121                arguments={
122                    0: dict(type_modifier=objc._C_OUT, c_array_of_fixed_length=4, null_accepted=False),
123                }
124            )),
125
126    ("nullfill4Tuple_", b'i^i', '',
127            dict(
128                arguments={
129                    0: dict(type_modifier=objc._C_OUT, c_array_of_fixed_length=4, null_accepted=True),
130                }
131            )),
132
133    ("fillStringArray_", b'i^*', '',
134            dict(
135                arguments={
136                    0: dict(type_modifier=objc._C_OUT, c_array_delimited_by_null=True, null_accepted=False),
137                }
138            )),
139
140    ("nullfillStringArray_", b'i^*', '',
141            dict(
142                arguments={
143                    0: dict(type_modifier=objc._C_OUT, c_array_delimited_by_null=True, null_accepted=True),
144                }
145            )),
146
147    ("reverseArray_uptoCount_", b'i^fi', '',
148            dict(
149                arguments={
150                    0: dict(type_modifier=objc._C_INOUT, c_array_length_in_arg=1, c_array_length_in_result=True, null_accepted=False),
151                }
152            )),
153
154    ("reverseArray_count_", b'v^fi', '',
155            dict(
156                arguments={
157                    0: dict(type_modifier=objc._C_INOUT, c_array_length_in_arg=1, null_accepted=False),
158                }
159            )),
160
161    ("nullreverseArray_count_", b'i^fi', '',
162            dict(
163                arguments={
164                    0: dict(type_modifier=objc._C_INOUT, c_array_length_in_arg=1, null_accepted=True),
165                }
166            )),
167
168    ("reverseStrings_", b'v^*', '',
169            dict(
170                arguments={
171                    0: dict(type_modifier=objc._C_INOUT, c_array_delimited_by_null=True, null_accepted=False),
172                }
173            )),
174
175    ("nullreverseStrings_", b'i^*', '',
176            dict(
177                arguments={
178                    0: dict(type_modifier=objc._C_INOUT, c_array_delimited_by_null=True, null_accepted=True),
179                }
180            )),
181
182    ("maybeReverseArray_", b'i^s', '',
183            dict(
184                arguments={
185                    0: dict(type_modifier=objc._C_INOUT, c_array_of_fixed_length=4, c_array_length_in_result=True, null_accepted=False),
186                }
187            )),
188
189    ("reverse4Tuple_", b'v^s', '',
190            dict(
191                arguments={
192                    0: dict(type_modifier=objc._C_INOUT, c_array_of_fixed_length=4, null_accepted=False),
193                }
194            )),
195
196    ("nullreverse4Tuple_", b'i^s', '',
197            dict(
198                arguments={
199                    0: dict(type_modifier=objc._C_INOUT, c_array_of_fixed_length=4, null_accepted=True),
200                }
201            )),
202
203    ("makeIntArrayOf5", b'^i', '',
204            dict(
205                retval=dict(c_array_of_fixed_length=5)
206            )),
207
208    ("makeStringArray", b'^*', '',
209            dict(
210                retval=dict(c_array_delimited_by_null=True),
211            )),
212
213    ("makeIntArrayOf_", b'^ii', '',
214            dict(
215                retval=dict(c_array_length_in_arg=0)
216            )),
217
218    ("nullIntArrayOf5", b'^i', '',
219            dict(
220                retval=dict(c_array_of_fixed_length=5)
221            )),
222
223    ("nullStringArray", b'^*', '',
224            dict(
225                retval=dict(c_array_delimited_by_null=True),
226            )),
227
228    ("nullIntArrayOf_", b'^ii',  '',
229            dict(
230                retval=dict(c_array_length_in_arg=0)
231            )),
232
233
234    ("sumX_andY_",  b'i^i^i', '',
235            dict(arguments={
236                    0: dict(type_modifier=objc._C_IN, null_accepted=False),
237                    1: dict(type_modifier=objc._C_IN, null_accepted=False),
238                })),
239
240    ("divBy5_remainder_",  b'ii^i', '',
241            dict(arguments={
242                    1: dict(type_modifier=objc._C_OUT, null_accepted=False),
243                })),
244
245    ("swapX_andY_", b'v^d^d', '',
246            dict(arguments={
247                    0: dict(type_modifier=objc._C_INOUT, null_accepted=False),
248                    1: dict(type_modifier=objc._C_INOUT, null_accepted=False),
249                })),
250
251    ("input_output_inputAndOutput_", b'@^i^i^i', '',
252            dict(arguments={
253                    0: dict(type_modifier=objc._C_IN, null_accepted=True),
254                    1: dict(type_modifier=objc._C_OUT, null_accepted=True),
255                    2: dict(type_modifier=objc._C_INOUT, null_accepted=True),
256            })),
257]
258
259objc.loadFunctionList(function_list, globals(),  _FunctionTable, False)
260
261class TestExists (TestCase):
262    def testFunctionsExists(self):
263        for item in _FunctionTable:
264            self.assertIn(item[0], globals())
265
266class TestArrayDefault (TestCase):
267    # TODO: what is the default anyway?
268    pass
269
270class TestArraysOut (TestCase):
271    def testFixedSize(self):
272        v = fill4Tuple_(None)
273        self.assertEqual(list(v), [0, -1, -8, -27])
274
275        self.assertRaises(ValueError, fill4Tuple_, objc.NULL)
276
277        n, v = nullfill4Tuple_(None)
278        self.assertEqual(n, 1)
279        self.assertEqual(list(v), [0, -1, -8, -27])
280
281        n, v = nullfill4Tuple_(objc.NULL)
282        self.assertEqual(n, 0)
283        self.assertIs(v, objc.NULL)
284
285    def testNullTerminated(self):
286
287        # Output only arrays of null-terminated arrays cannot be
288        # wrapped automaticly. How is the bridge supposed to know
289        # how much memory it should allocate for the C-array?
290        self.assertRaises(TypeError, fillStringArray_, None)
291        self.assertRaises(ValueError, fillStringArray_, objc.NULL)
292
293        self.assertRaises(TypeError, nullfillStringArray_)
294        self.assertRaises(TypeError, nullfillStringArray_, None)
295        n, v = nullfillStringArray_(objc.NULL)
296        self.assertEqual(n, 0)
297        self.assertIs(v, objc.NULL)
298
299    def testWithCount(self):
300
301        v = fillArray_count_(None, 3)
302        self.assertEqual(list(v),  [0,1,4])
303
304        v = fillArray_count_(None, 3)
305        self.assertEqual(list(v),  [0,1,4])
306
307        v = fillArray_count_(None, 5)
308        self.assertEqual(list(v),  [0,1,4,9,16])
309
310        v = fillArray_count_(None, 0)
311        self.assertEqual(list(v),  [])
312
313        self.assertRaises(ValueError, fillArray_count_, objc.NULL, 0)
314
315        n, v = nullfillArray_count_(None, 3)
316        self.assertEqual(n, 1)
317        self.assertEqual(list(v),  [0,1,4])
318        n, v = nullfillArray_count_(None, 3)
319        self.assertEqual(n, 1)
320        self.assertEqual(list(v),  [0,1,4])
321
322        n, v = nullfillArray_count_(objc.NULL, 3)
323        self.assertEqual(n, 0)
324        self.assertIs(v, objc.NULL )
325
326    def testWithCountInResult(self):
327
328        c, v = fillArray_uptoCount_(None, 20)
329        self.assertEqual(c, 10)
330        self.assertEqual(list(v),  [i+2 for i in range(10)])
331
332        c, v = maybeFillArray_(None)
333        self.assertEqual(c, 2)
334        self.assertEqual(list(v),  [10, 11])
335
336
337class TestArraysInOut (TestCase):
338    def testFixedSize(self):
339
340        a = (1,2,3,4)
341        v = reverse4Tuple_(a)
342        self.assertEqual(a, (1,2,3,4))
343        self.assertEqual(v, (4,3,2,1))
344
345        self.assertRaises(ValueError, reverse4Tuple_, (1,2,3))
346        self.assertRaises(ValueError, reverse4Tuple_, (1,2,3,4,5))
347        self.assertRaises(ValueError, reverse4Tuple_, objc.NULL)
348
349        a = (1,2,3,4)
350        n, v = nullreverse4Tuple_(a)
351        self.assertEqual(n, 1)
352        self.assertEqual(a, (1,2,3,4))
353        self.assertEqual(v, (4,3,2,1))
354
355        n, v = nullreverse4Tuple_(objc.NULL)
356        self.assertEqual(n, 0)
357        self.assertIs(v, objc.NULL)
358
359    def testNullTerminated(self):
360
361        a = (b'a', b'b', b'c')
362        v = reverseStrings_(a)
363        self.assertEqual(a, (b'a', b'b', b'c'))
364        self.assertEqual(v, (b'c', b'b', b'a'))
365
366        self.assertRaises(ValueError, reverseStrings_, (1,2))
367        self.assertRaises(ValueError, reverseStrings_, objc.NULL)
368
369        a = (b'a', b'b', b'c')
370        n, v = nullreverseStrings_(a)
371        self.assertEqual(n, 1)
372        self.assertEqual(a, (b'a', b'b', b'c'))
373        self.assertEqual(v, (b'c', b'b', b'a'))
374
375        n, v = nullreverseStrings_(objc.NULL)
376        self.assertEqual(n, 0)
377        self.assertIs(v, objc.NULL)
378
379    def testWithCount(self):
380
381        a = (1.0, 2.0, 3.0, 4.0, 5.0)
382        v = reverseArray_count_(a, 4)
383        self.assertEqual(a, (1.0, 2.0, 3.0, 4.0, 5.0))
384        self.assertEqual(v, (4.0, 3.0, 2.0, 1.0))
385
386        a = (1.0, 2.0, 3.0, 4.0, 5.0)
387        v = reverseArray_count_(a, 5)
388        self.assertEqual(a, (1.0, 2.0, 3.0, 4.0, 5.0))
389        self.assertEqual(v, (5.0, 4.0, 3.0, 2.0, 1.0))
390
391        # Nice to have, but doesn't work without major
392        # surgery:
393        #a = (1.0, 2.0, 3.0, 4.0, 5.0)
394        #v = reverseArray_count_(a, None)
395        #self.assertEqual(a, (1.0, 2.0, 3.0, 4.0, 5.0))
396        #self.assertEqual(v, (5.0, 4.0, 3.0, 2.0, 1.0))
397
398        self.assertRaises(ValueError, reverseArray_count_, (1.0, 2.0), 5)
399        self.assertRaises(ValueError, reverseArray_count_, objc.NULL, 0)
400
401        a = (1.0, 2.0, 3.0, 4.0, 5.0)
402        n, v = nullreverseArray_count_(a, 5)
403        self.assertEqual(n, 1)
404        self.assertEqual(a, (1.0, 2.0, 3.0, 4.0, 5.0))
405        self.assertEqual(v, (5.0, 4.0, 3.0, 2.0, 1.0))
406
407        n, v = nullreverseArray_count_(objc.NULL, 0)
408        self.assertEqual(n, 0)
409        self.assertIs(v, objc.NULL)
410
411    def testWithCountInResult(self):
412
413        c, v = reverseArray_uptoCount_(range(10), 10)
414        self.assertEqual(c, 5)
415        self.assertEqual(len(v), 5)
416        self.assertEqual(list(v),  [9, 8, 7, 6, 5])
417
418        c, v = maybeReverseArray_([1,2,3,4])
419        self.assertEqual(c, 2)
420        self.assertEqual(len(v), 2)
421        self.assertEqual(list(v),  [4, 3])
422
423class TestArraysIn (TestCase):
424    def testFixedSize(self):
425
426        v = make4Tuple_((1.0, 4.0, 8.0, 12.5))
427        self.assertEqual(len(v), 4)
428        self.assertEqual(list(v), [1.0, 4.0, 8.0, 12.5])
429
430        v = make4Tuple_((1, 2, 3, 4))
431        self.assertEqual(len(v), 4)
432        self.assertEqual(list(v), [1.0, 2.0, 3.0, 4.0])
433
434        self.assertRaises(ValueError, make4Tuple_, (1, 2, 3))
435        self.assertRaises(ValueError, make4Tuple_, (1, 2, 3, 4, 5))
436        self.assertRaises(ValueError, make4Tuple_, objc.NULL)
437
438        v = null4Tuple_(objc.NULL)
439        self.assertIs(v, None)
440
441    def testNullTerminated(self):
442
443        v = makeStringArray_((b"hello", b"world", b"there"))
444        self.assertEqual(len(v), 3)
445        self.assertEqual(list(v), ["hello", "world", "there"])
446        self.assertIsInstance(v, objc.lookUpClass("NSArray"))
447        self.assertIsInstance(v[0], unicode)
448
449        NSObject = objc.lookUpClass('NSObject')
450        p, q, r = NSObject.new(), NSObject.new(), NSObject.new()
451        v = makeObjectArray_((p, q, r))
452        self.assertEqual(len(v), 3)
453        self.assertIs(v[0], p)
454        self.assertIs(v[1], q)
455        self.assertIs(v[2], r)
456
457        v = makeStringArray_(())
458        self.assertEqual(len(v), 0)
459
460        self.assertRaises(ValueError, makeStringArray_, [1,2])
461        self.assertRaises(ValueError, makeStringArray_, objc.NULL)
462
463        v = nullStringArray_(objc.NULL)
464        self.assertEqual(v, None)
465
466    def testWithCount(self):
467
468        v = makeIntArray_count_((1,2,3,4), 3)
469        self.assertEqual(len(v), 3)
470        self.assertEqual(list(v), [1,2,3])
471
472        # XXX: This one would be nice to have, but not entirely trivial
473        #v = makeIntArray_count_((1,2,3,4), None)
474        #self.assertEqual(len(v), 3)
475        #self.assertEqual(list(v), [1,2,3,4])
476
477        self.assertRaises(ValueError, makeIntArray_count_, [1,2,3], 4)
478        self.assertRaises(ValueError, makeIntArray_count_, objc.NULL, 0)
479        self.assertRaises(ValueError, makeIntArray_count_, objc.NULL, 1)
480
481        v = nullIntArray_count_(objc.NULL, 0)
482        self.assertEqual(v, None)
483
484        self.assertRaises(ValueError, makeIntArray_count_, objc.NULL, 1)
485
486        # Make sure this also works when the length is in a pass-by-reference argument
487        v = makeIntArray_countPtr_((1,2,3,4), 4)
488        self.assertEqual(len(v), 4)
489        self.assertEqual(list(v), [1,2,3,4])
490
491class TestArrayReturns (TestCase):
492    # TODO:
493    # - Add null-terminated arrays of various supported types:
494    #   -> integers
495    #   -> CF-types
496    def testFixedSize(self):
497
498        v = makeIntArrayOf5()
499        self.assertEqual( len(v), 5 )
500        self.assertEqual( v[0], 0 )
501        self.assertEqual( v[1], 1 )
502        self.assertEqual( v[2], 4 )
503        self.assertEqual( v[3], 9 )
504        self.assertEqual( v[4], 16 )
505
506        v = nullIntArrayOf5()
507        self.assertEqual(v, objc.NULL)
508
509    def testSizeInArgument(self):
510        v = makeIntArrayOf_(3)
511        self.assertEqual(len(v), 3)
512        self.assertEqual(v[0], 0)
513        self.assertEqual(v[1], 1)
514        self.assertEqual(v[2], 8)
515
516        v = makeIntArrayOf_(10)
517        self.assertEqual(len(v), 10)
518        for i in range(10):
519            self.assertEqual(v[i], i**3)
520
521        v = nullIntArrayOf_(100)
522        self.assertEqual(v, objc.NULL)
523
524    def testNULLterminated(self):
525
526        v = makeStringArray()
527        self.assertEqual(len(v), 4)
528        self.assertEqual(list(v), [b"hello", b"world", b"out", b"there"])
529
530        v = nullStringArray()
531        self.assertEqual(v, objc.NULL)
532
533class TestByReference (TestCase):
534    # Pass by reference arguments.
535    # Note that these tests aren't exhaustive, we have test_methods and
536    # test_methods2 for that :-)
537
538    def testInput(self):
539
540        r = sumX_andY_(1, 2)
541        self.assertEqual(r, 1+2)
542
543        r = sumX_andY_(2535, 5325)
544        self.assertEqual(r, 2535 + 5325)
545
546        self.assertRaises(ValueError, sumX_andY_, 42, objc.NULL)
547
548    def testOutput(self):
549
550        div, rem = divBy5_remainder_(55, None)
551        self.assertEqual(div, 11)
552        self.assertEqual(rem, 0)
553
554        div, rem = divBy5_remainder_(13, None)
555        self.assertEqual(div, 2)
556        self.assertEqual(rem, 3)
557
558        self.assertRaises(ValueError, divBy5_remainder_, 42, objc.NULL)
559
560    def testInputOutput(self):
561        x, y = swapX_andY_(42, 284)
562        self.assertEqual(x, 284)
563        self.assertEqual(y, 42)
564
565        self.assertRaises(ValueError, swapX_andY_, 42, objc.NULL)
566
567    def testNullAccepted(self):
568        # Note: the commented-out test-cases require a change in the pyobjc-core
569
570        def makeNum(value):
571            return int(value, 0)
572
573        r, y, z = input_output_inputAndOutput_(1, None, 2)
574        self.assertEqual(len(r), 3)
575        self.assertEqual(len(list(filter(None, map(makeNum, r)))), 3)
576        self.assertEqual(y, 3)
577        self.assertEqual(z, -1)
578
579        # Argument 1 is NULL
580        r, y, z = input_output_inputAndOutput_(objc.NULL, None, 2)
581        self.assertEqual(len(r), 3)
582        self.assertEqual(len(list(filter(None, map(makeNum, r)))), 2)
583        self.assertEqual(y, 40)
584        self.assertEqual(z, -2)
585
586        r, y, z = input_output_inputAndOutput_(objc.NULL, None, 2)
587        self.assertEqual(len(r), 3)
588        self.assertEqual(len(list(filter(None, map(makeNum, r)))), 2)
589        self.assertEqual(y, 40)
590        self.assertEqual(z, -2)
591
592        # Argument 2 is NULL
593        r, y, z = input_output_inputAndOutput_(1, objc.NULL, 2)
594        self.assertEqual(len(r), 3)
595        self.assertEqual(len(list(filter(None, map(makeNum, r)))), 2)
596        self.assertEqual(y, objc.NULL)
597        self.assertEqual(z, -1)
598
599        # Argument 3 is NULL
600        r, y, z = input_output_inputAndOutput_(1, None, objc.NULL)
601        self.assertEqual(len(r), 3)
602        self.assertEqual(len(list(filter(None, map(makeNum, r)))), 2)
603        self.assertEqual(y, 43)
604        self.assertEqual(z, objc.NULL)
605
606        r, y, z = input_output_inputAndOutput_(1, None, objc.NULL)
607        self.assertEqual(len(r), 3)
608        self.assertEqual(len(list(filter(None, map(makeNum, r)))), 2)
609        self.assertEqual(y, 43)
610        self.assertEqual(z, objc.NULL)
611
612class TestPrintfFormat (TestCase):
613    def test_nsformat(self):
614
615        v = makeArrayWithFormat_("%3d", 10)
616        self.assertEqual(list(v), [ "%3d", " 10"])
617
618        v = makeArrayWithFormat_("hello %s", b"world")
619        self.assertEqual(list(v), [ "hello %s", "hello world"])
620
621        v = makeArrayWithFormat_("\xf1")
622        self.assertEqual(list(v), [ "\xf1", "\xf1"])
623
624
625    def test_cformat(self):
626
627        v = makeArrayWithCFormat_(b"%3d", 10)
628        self.assertEqual(list(v), [ "%3d", " 10"])
629
630        v = makeArrayWithCFormat_(b"hello %s", b"world")
631        self.assertEqual(list(v), [ "hello %s", "hello world"])
632
633if __name__ == "__main__":
634    main()
635