1from PyObjCTools.TestSupport import *
2
3import objc._bridgesupport as bridgesupport
4import os
5import re
6import sys
7import imp
8import ctypes
9import objc
10import subprocess
11
12import xml.etree.ElementTree as ET
13
14
15try:
16    basestring
17except NameError:
18    basestring = str
19
20try:
21    long
22except NameError:
23    long = int
24
25IDENTIFIER=re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
26
27TEST_XML=b"""\
28<signatures>
29  <unknown><!-- ignore -->
30    <enum name='nested1' value='4' /><!-- should be ignored -->
31  </unknown>
32  <opaque name='opaque1' type='^{opaque1}'/>
33  <opaque name='opaque2' type='^{opaque2=f}' type64='^{opaque2=d}' />
34  <opaque type='^{opaque2=f}' type64='^{opaque2=d}' /><!-- ignore -->
35  <opaque name='opaque3' /><!-- ignore -->
36  <opaque name='opaque4' type64='^{opaque4=d}' /><!-- ignore 32-bit -->
37  <opaque /><!-- ignore -->
38  <constant name='constant1' type='@'/>
39  <constant name='constant2' type='I' type64='Q' />
40  <constant type='@'/><!-- ignore -->
41  <constant name='constant3' /><!-- ignore -->
42  <constant name='constant4' type64='Q' /><!-- ignore 32-bit -->
43  <constant name='constant5' type='B' /><!-- NSBOOL -->
44  <constant name='constant6' type='Z' /><!-- bool -->
45  <constant name='constant7' type='@' magic_cookie='false' />
46  <constant name='constant8' type='@' magic_cookie='true' />
47  <constant /><!-- ignore -->
48  <constant name='constant9' type='{foo=i?i}' /><!-- ignore: embedded ? -->
49  <constant name='constant10' type='{foo={x=?}}' /><!-- ignore: embedded ? -->
50  <string_constant name='strconst1' value='string constant1' />
51  <string_constant name='strconst2' value='string constant 2' value64='string constant two' />
52  <string_constant name='strconst1u' value='string constant1 unicode' nsstring='true' />
53  <string_constant name='strconst2u' value='string constant 2 unicode' value64='string constant two unicode' nsstring='true' />
54  <string_constant name='strconst3' /><!-- ignore -->
55  <string_constant name='strconst4' nsstring='true' /><!-- ignore -->
56  <string_constant name='strconst5' value64='string five' /><!-- ignore 32-bit -->
57  <string_constant name='strconst6' value64='string five unicode' nsstring='true' /><!-- ignore 32-bit -->
58  <string_constant /><!-- ignore -->
59  <enum name='enum1' value='1' />
60  <enum name='enum2' value='3' value64='4'/>
61  <enum name='enum3' le_value='5' be_value='6'/>
62  <enum name='enum4' value='7' be_value='8' /><!-- should have value 7 -->
63  <enum name='enum5' /><!-- ignore -->
64  <enum value='3' value64='4'/><!-- ignore -->
65  <enum name='enum6' value64='4'/><!-- ignore 32-bit -->
66  <enum name='enum7' be_value='6' /><!-- ignore intel -->
67  <enum name='enum8' le_value='5' /><!-- ignore ppc -->
68  <enum name='enum9' value='2.5' />
69  <enum name='enum10' value='0x1.5p+3' />
70  <enum /><!-- ignore -->
71  <null_const name='null1' />
72  <null_const /><!-- ignore -->
73  <function_pointer name='func1' original='orig_function' />
74  <function_pointer name='func2' /><!-- ignore -->
75  <function_pointer original='func3' /><!-- ignore -->
76  <function_pointer /><!-- ignore -->
77  <cftype name='CFProxy1Ref' type='^{CFProxy}' gettypeid_func='CFArrayGetTypeID' />
78  <cftype name='CFProxy2Ref' type='^{CFProxy32}' type64='^{CFProxy64}' gettypeid_func='CFArrayGetTypeID' />
79  <cftype name='CFProxy3Ref' type='^{CFProxy3}' tollfree='NSProxy' gettypeid_func='CFArrayGetTypeID' />
80  <cftype name='CFProxy4Ref' type='^{CFProxy4}' tollfree='NSProxy2' />
81  <cftype name='CFProxy5Ref' type='^{CFProxy}' gettypeid_func='NoSuchFunction' /><!-- tollfree to CFTypeRef -->
82  <cftype name='CFProxy6Ref' type='^{CFProxy}' />
83  <cftype name='CFProxy7Ref' type='^{CFProxy32}' type64='^{CFProxy64}' />
84  <cftype type='^{CFProxy}' gettypeid_func='CFArrayGetTypeID' /><!-- ignore -->
85  <cftype name='CFProxy8Ref' gettypeid_func='CFArrayGetTypeID' /><!-- ignore -->
86  <cftype name='CFProxy9Ref' type64='^{CFProxy64}' gettypeid_func='CFArrayGetTypeID' /><!-- ignore 32-bit -->
87  <cftype/><!-- ignore -->
88  <class name='MyClass1'></class>
89  <class /><!-- ignore -->
90  <class name='MyClass2'>
91    <function name='method.function'></function><!-- ignore, not at toplevel -->
92    <method selector='method1' classmethod='true' ></method>
93    <method selector='method2' variadic='true' ></method>
94    <method selector='method3' variadic='true' c_array_delimited_by_null='true'></method>
95    <method selector='method4' variadic='true' c_array_length_in_arg='4'></method>
96    <method selector='method5' c_array_delimited_by_null='true'><retval type='d'/></method><!-- c_array... ignored -->
97    <method selector='method6' c_array_length_in_arg='4'><retval type='d' /></method><!-- c_array... ignored -->
98    <method selector='method7' ignore='true'></method>
99    <method selector='method8' ignore='true' suggestion='ignore me'></method>
100    <method selector='method9' suggestion='ignore me'><retval type='d'/></method><!-- suggestion ignored -->
101    <method selector='method10'>
102       <retval type='d'/>
103    </method>
104    <method selector='method11' classmethod='false'>
105       <retval type='f' type64='d' />
106    </method>
107    <method selector='method12' classmethod='true'>
108       <retval/><!-- ignore -->
109    </method>
110    <method selector='method13' class_method='true'>
111       <retval type_modifier='n' />
112    </method>
113    <method selector='method13b' class_method='false'>
114       <retval sel_of_type='v@:f' c_array_of_fixed_length='4'/>
115    </method>
116    <method selector='method14'>
117       <retval sel_of_type='v@:f' sel_of_type64='v@:d' />
118    </method>
119    <method selector='method15'>
120       <retval null_accepted='false' already_retained='true' c_array_length_in_result='true' />
121    </method>
122    <method selector='method16'>
123       <retval c_array_delimited_by_null='true' already_cfretained='true' c_array_of_variable_length='true' printf_format='true' free_result='true' />
124    </method>
125    <method selector='method17'>
126       <retval c_array_length_in_arg='1'/>
127    </method>
128    <method selector='method18'>
129       <retval c_array_length_in_arg='1,2'/>
130    </method>
131    <method selector='method19'>
132       <retval c_array_length_in_arg='4, 5'/>
133    </method>
134    <method selector='method20'>
135       <retval function_pointer_retained='false'/><!-- ignored, no function data -->
136    </method>
137    <method selector='method21'>
138       <retval function_pointer_retained='false' function_pointer='true'>
139          <retval type='v' />
140          <arg type='@' />
141          <arg type='d' />
142       </retval>
143    </method>
144    <method selector='method22'>
145       <retval function_pointer_retained='false' block='true'>
146          <retval type='v' />
147          <arg type='@' />
148          <arg type='d' />
149       </retval>
150    </method>
151    <method selector='method23'>
152       <retval function_pointer='true'>
153          <retval type='v' />
154          <arg type='@' />
155          <arg type='d' />
156       </retval>
157    </method>
158    <method selector='method24'>
159       <retval block='true'>
160          <retval type='v' />
161          <arg type='@' />
162          <arg type='d' />
163       </retval>
164    </method>
165    <method ></method><!-- ignore -->
166    <method variadic='true'></method><!-- ignore -->
167    <method ignore='true'></method><!-- ignore -->
168  </class>
169  <class name='MyClass3'>
170    <method selector='method7'>
171       <retval type='q' />
172       <arg index='1' type='f'/>
173       <arg index='2' type='d'/>
174    </method>
175    <method selector='method8'>
176       <arg index='1' type='d'/>
177       <arg index='2' type='d'/>
178    </method>
179    <method selector='method9'>
180       <arg type='d'/><!-- ignored: no index -->
181    </method>
182    <method selector='method10'>
183       <arg index='1' type='d'/>
184    </method>
185    <method selector='method11'>
186       <arg index='1' type='f' type64='d' />
187    </method>
188    <method selector='method12'>
189       <arg index='1'/><!-- ignore -->
190    </method>
191    <method selector='method13'>
192       <arg index='1' type_modifier='n' />
193    </method>
194    <method selector='method13b'>
195       <arg index='1' sel_of_type='v@:f' c_array_of_fixed_length='4'/>
196    </method>
197    <method selector='method14'>
198       <arg index='1' sel_of_type='v@:f' sel_of_type64='v@:d' />
199    </method>
200    <method selector='method15'>
201       <arg index='1' null_accepted='false' already_retained='true' c_array_length_in_result='true' />
202    </method>
203    <method selector='method16'>
204       <arg index='1' c_array_delimited_by_null='true' already_cfretained='true' c_array_of_variable_length='true' printf_format='true' free_result='true' />
205    </method>
206    <method selector='method17'>
207       <arg index='1' c_array_length_in_arg='1'/>
208    </method>
209    <method selector='method18'>
210       <arg index='1' c_array_length_in_arg='1,2'/>
211    </method>
212    <method selector='method19'>
213       <arg index='1' c_array_length_in_arg='4, 5'/>
214    </method>
215    <method selector='method20'>
216       <arg index='1' function_pointer_retained='false'/><!-- ignored, no function data -->
217    </method>
218    <method selector='method21'>
219       <arg index='1' function_pointer_retained='false' function_pointer='true'>
220          <retval type='v' />
221          <arg type='@' />
222          <arg type='d' />
223       </arg>
224    </method>
225    <method selector='method22'>
226       <arg index='1' function_pointer_retained='false' block='true'>
227          <retval type='v' />
228          <arg type='@' />
229          <arg type='d' />
230       </arg>
231    </method>
232    <method selector='method23'>
233       <arg index='1' function_pointer='true'>
234          <retval type='v' />
235          <arg type='@' />
236          <arg type='d' />
237       </arg>
238    </method>
239    <method selector='method24' classmethod='true'>
240       <arg index='1' block='true'>
241          <retval type='v' />
242          <arg type='@' />
243          <arg type='d' />
244       </arg>
245    </method>
246  </class>
247  <function /><!-- ignored -->
248  <function><!-- ignored -->
249    <arg type="f" />
250    <retval type="@" />
251  </function>
252  <function name='function1' ignore='true' ><!-- ignore -->
253    <arg type="f" />
254    <retval type="@" />
255  </function>
256  <function name='function2' variadic='true' ></function>
257  <function name='function3' variadic='true' c_array_delimited_by_null='true'></function>
258  <function name='function4' variadic='true' c_array_length_in_arg='4'></function>
259  <function name='function5' c_array_delimited_by_null='true'><retval type='d'/></function><!-- c_array... ignored -->
260  <function name='function6' c_array_length_in_arg='4'><retval type='d' /></function><!-- c_array... ignored -->
261  <function name='function7' ignore='true'></function>
262  <function name='function8' ignore='true' suggestion='ignore me'></function>
263  <function name='function9' suggestion='ignore me'><retval type='d'/></function><!-- suggestion ignored -->
264  <function name='function10'>
265    <retval type='d'/>
266  </function>
267  <function name='function11'>
268     <retval type='f' type64='d' />
269  </function>
270  <function name='function12'>
271     <retval/><!-- ignore -->
272  </function>
273  <function name='function13'>
274     <retval type='i' type_modifier='n' />
275  </function>
276  <function name='function14'>
277     <retval type=':' sel_of_type='v@:f' c_array_of_fixed_length='4'/>
278  </function>
279  <function name='function15'>
280     <retval type=':' sel_of_type='v@:f' sel_of_type64='v@:d' />
281  </function>
282  <function name='function16'>
283     <retval type='i' null_accepted='false' already_retained='true' c_array_length_in_result='true' />
284  </function>
285  <function name='function17'>
286     <retval type='i' already_cfretained='true' c_array_delimited_by_null='true' c_array_of_variable_length='true' printf_format='true' free_result='true' />
287  </function>
288  <function name='function18'>
289     <retval type='i' c_array_length_in_arg='1'/>
290  </function>
291  <function name='function19'>
292     <retval type='i' c_array_length_in_arg='1,2'/>
293  </function>
294  <function name='function20'>
295     <retval type='i' c_array_length_in_arg='4, 5'/>
296  </function>
297  <function name='function21'>
298     <retval type='?' function_pointer_retained='false'/><!-- ignored, no function data -->
299  </function>
300  <function name='function22'>
301     <retval type='?' function_pointer_retained='false' function_pointer='true'>
302        <retval type='v' />
303        <arg type='@' />
304        <arg type='d' />
305     </retval>
306  </function>
307  <function name='function23'>
308     <retval type='@?' function_pointer_retained='false' block='true'>
309        <retval type='v' />
310        <arg type='@' />
311        <arg type='d' />
312     </retval>
313  </function>
314  <function name='function24'>
315     <retval type='?' function_pointer='true'>
316        <retval type='v' />
317        <arg type='@' />
318        <arg type='d' />
319     </retval>
320  </function>
321  <function name='function25'>
322     <retval type='@?' block='true'>
323        <retval type='v' />
324        <arg type='@' />
325        <arg type='d' />
326     </retval>
327  </function>
328  <function name='function26'>
329     <retval type='q' />
330     <arg index='1' type='f'/>
331     <arg index='2' type='d'/>
332  </function>
333  <function name='function27'>
334     <arg index='1' type='d'/>
335     <arg index='2' type='d'/>
336  </function>
337  <function name='function29'>
338     <arg type='d'/>
339  </function>
340  <function name='function30'>
341     <arg type='f' type64='d' />
342  </function>
343  <function name='function32'>
344     <arg type='@' type_modifier='n' />
345  </function>
346  <function name='function33'>
347     <arg type=':' sel_of_type='v@:f' c_array_of_fixed_length='4'/>
348  </function>
349  <function name='function34'>
350     <arg type=':' sel_of_type='v@:f' sel_of_type64='v@:d' />
351  </function>
352  <function name='function35'>
353     <arg type='@' null_accepted='false' already_retained='true' c_array_length_in_result='true' />
354  </function>
355  <function name='function36'>
356     <arg type='@' c_array_delimited_by_null='true' already_cfretained='true' c_array_of_variable_length='true' printf_format='true' free_result='true' />
357  </function>
358  <function name='function37'>
359     <arg type='@' c_array_length_in_arg='1'/>
360  </function>
361  <function name='function38'>
362     <arg type='@' c_array_length_in_arg='1,2'/>
363  </function>
364  <function name='function39'>
365     <arg type='@' c_array_length_in_arg='4, 5'/>
366  </function>
367  <function name='function40'>
368     <arg type='?' function_pointer_retained='false'/><!-- ignored, no function data -->
369  </function>
370  <function name='function41'>
371     <arg type='?' function_pointer_retained='false' function_pointer='true'>
372        <retval type='v' />
373        <arg type='@' />
374        <arg type='d' />
375     </arg>
376  </function>
377  <function name='function42'>
378     <arg type='@?' function_pointer_retained='false' block='true'>
379        <retval type='v' />
380        <arg type='@' />
381        <arg type='d' />
382     </arg>
383  </function>
384  <function name='function43'>
385     <arg type='?' function_pointer='true'>
386        <retval type='v' />
387        <arg type='@' />
388        <arg type='d' />
389     </arg>
390  </function>
391  <function name='function44'>
392     <arg type='@?' block='true'>
393        <retval type='v' />
394        <arg type='@' />
395        <arg type='d' />
396     </arg>
397  </function>
398  <function name='function45'><!-- ignore: arg node without type-->
399    <arg type_modifier='n' />
400  </function>
401  <function name='function46'><!-- ignore: arg node without type-->
402    <arg type='@' />
403    <arg type_modifier='n' />
404  </function>
405  <function name='function47'><!-- ignore: retval node without type-->
406    <retval type_modifier='n'/>
407    <arg type='@' />
408    <arg type='@' />
409  </function>
410  <!-- TODO: type rewriting (_C_BOOL, _C_NSBOOL)-->
411  <informal_protocol/><!-- ignore -->
412  <informal_protocol name='protocol1' />
413  <informal_protocol name='protocol2' >
414    <method selector='selector1' type='v@:f' type64='v@:d' />
415    <method selector='selector2' type='v@:f' type64='v@:d' classmethod='false' />
416    <method selector='selector3' type='v@:f' type64='v@:d' classmethod='true' />
417    <method selector='selector4' type='v@:f' type64='v@:d' variadic='true' /><!-- 'variadic' is ignored -->
418    <method selector='selector5' /><!-- ignore: no type -->
419    <method selector='selector6' type64='v@:@' /><!-- ignore 32-bit -->
420    <method selector='selector7' type='v@:f' class_method='false' /><!-- manpage: class_method, pyobjc 2.3: classmethod -->
421    <method selector='selector8' type='v@:f' class_method='true' />
422  </informal_protocol>
423  <struct/><!-- ignore -->
424  <struct type='{foo=dd}' /><!--ignore-->
425  <struct name='struct1' /><!-- ignore -->
426  <struct name='struct2' alias='module.struct3' /><!-- ignore -->
427  <struct name='struct3' type='{struct3=@@}' />
428  <struct name='struct4' type='{struct3=ff}' type64='{struct4=dd}' />
429  <struct name='struct5' type='{struct3=@@}' alias='module2.struct'/>
430  <struct name='struct6' type='{struct6=BB}' /><!-- _C_NSBOOL -->
431  <struct name='struct7' type='{struct7=Z@}' /><!-- _C_BOOL -->
432  <struct name='struct8' type='{struct8=@@}' alias='sys.maxsize'/>
433  <struct name='struct9' type='{struct9=ii}' alias='sys.does_not_exist'/>
434</signatures>
435"""
436
437class TestBridgeSupportParser (TestCase):
438    def testInvalidToplevel(self):
439        self.assertRaises(objc.error, bridgesupport._BridgeSupportParser, b'<signatures2></signatures2>', 'Cocoa')
440        self.assertRaises(ET.ParseError, bridgesupport._BridgeSupportParser, b'<signatures2></signatures>', 'Cocoa')
441
442    def iter_framework_dir(self, framework_dir):
443        for dn in os.listdir(framework_dir):
444            fn = os.path.join(framework_dir, dn, 'Resources', 'BridgeSupport', dn.replace('.framework', '.bridgesupport'))
445            if os.path.exists(fn):
446                yield fn
447
448            fn = os.path.join(framework_dir, dn, 'Frameworks')
449            if os.path.exists(fn):
450                for item in self.iter_framework_dir(fn):
451                    yield item
452
453    def iter_system_bridgesupport_files(self):
454        for item in self.iter_framework_dir('/System/Library/Frameworks'):
455            yield item
456
457    def test_system_bridgesupport(self):
458        with filterWarnings("ignore", RuntimeWarning):
459            # Check that all system bridgesupport files can be processed correctly
460            for fn in self.iter_system_bridgesupport_files():
461                with open(fn, 'r') as fp:
462                    xmldata = fp.read()
463
464                self.assert_valid_bridgesupport(os.path.basename(fn).split('.')[0], xmldata)
465
466    def test_xml_structure_variants(self):
467        # Run 'verify_xml_structure' for all cpu variant
468        # (big/little endian,  32- en 64-bit)
469        orig_byteorder = sys.byteorder
470        orig_maxsize = sys.maxsize
471        orig_createStructType = bridgesupport._orig_createStructType
472        orig_registerStructAlias = bridgesupport._orig_registerStructAlias
473
474        try:
475            for is32bit in (True, False):
476                for endian in ('little', 'big'):
477                    sys.maxsize = 2**63-1 if endian == 'big' else 2**32-1
478                    sys.byteorder = endian
479
480                    # Reload the bridgesupport module because
481                    # it contains conditional definitions
482                    objc.createStructType = orig_createStructType
483                    objc.registerStructAlias = orig_registerStructAlias
484                    imp.reload(bridgesupport)
485
486                    self.verify_xml_structure()
487
488        finally:
489            sys.byteorder = orig_byteorder
490            sys.maxsize = orig_maxsize
491
492            # See above
493            objc.createStructType = orig_createStructType
494            objc.registerStructAlias = orig_registerStructAlias
495            imp.reload(bridgesupport)
496
497    def verify_xml_structure(self):
498        prs = self.assert_valid_bridgesupport('TestXML', TEST_XML)
499
500        all_constants = [
501            ('constant1', b'@', False,),
502            ('constant2', b'I' if sys.maxsize < 2**32 else b'Q' , False,),
503            ('constant5', objc._C_NSBOOL, False,),
504            ('constant6', objc._C_BOOL, False,),
505            ('constant7', b'@', False,),
506            ('constant8', b'@', True,),
507        ]
508        if sys.maxsize > 2**32:
509            all_constants.append(
510                ('constant4', b'Q', False)
511            )
512
513        all_values = {
514            'strconst1': b'string constant1',
515            'strconst2': b'string constant 2' if sys.maxsize < 2**32 else b'string constant two',
516            'strconst1u': b'string constant1 unicode'.decode('ascii'),
517            'strconst2u': b'string constant 2 unicode'.decode('ascii') if sys.maxsize < 2**32 else b'string constant two unicode'.decode('ascii'),
518            'enum1': 1,
519            'enum2': 3 if sys.maxsize < 2**32 else 4,
520            'enum3': 5 if sys.byteorder == 'little' else 6,
521            'enum4': 7,
522            'enum9': 2.5,
523            'enum10': 10.5,
524            'null1': None,
525        }
526        if sys.maxsize > 2**32:
527            all_values['strconst5'] = b'string five'
528            all_values['strconst6'] = b'string five unicode'.decode('ascii')
529            all_values['enum6'] = 4
530
531        if sys.byteorder == 'little':
532            all_values['enum8'] = 5
533        else:
534            all_values['enum7'] = 6
535
536        all_opaque = [
537            ('opaque1', b'^{opaque1}'),
538            ('opaque2', b'^{opaque2=f}' if sys.maxsize < 2**32 else b'^{opaque2=d}'),
539        ]
540        if sys.maxsize > 2**32:
541            all_opaque.append(
542                ('opaque4', b'^{opaque4=d}'),
543            )
544
545        all_func_aliases = [
546            ('func1', 'orig_function'),
547        ]
548
549        a = objc.lookUpClass('NSArray').alloc().init()
550        CFArrayTypeID = a._cfTypeID()
551        all_cftypes = [
552            ( 'CFProxy1Ref', b'^{CFProxy}', CFArrayTypeID),
553            ( 'CFProxy2Ref', b'^{CFProxy32}' if sys.maxsize < 2**32 else b'^{CFProxy64}', CFArrayTypeID),
554            ( 'CFProxy3Ref', b'^{CFProxy3}', None, 'NSProxy'),
555            ( 'CFProxy4Ref', b'^{CFProxy4}', None, 'NSProxy2'),
556            ( 'CFProxy5Ref', b'^{CFProxy}', None, 'NSCFType'),
557            ( 'CFProxy6Ref', b'^{CFProxy}', None, 'NSCFType'),
558            ( 'CFProxy7Ref',  b'^{CFProxy32}' if sys.maxsize < 2**32 else b'^{CFProxy64}', None, 'NSCFType' ),
559        ]
560        if sys.maxsize > 2**32:
561            all_cftypes.append(
562                ( 'CFProxy9Ref',  b'^{CFProxy64}', CFArrayTypeID),
563            )
564
565
566        all_methods = {
567            (b'MyClass2', b'method2', False): { 'variadic': True },
568            (b'MyClass2', b'method3', False): { 'variadic': True,  'c_array_delimited_by_null': True },
569            (b'MyClass2', b'method4', False): { 'variadic': True,  'c_array_length_in_arg': 4+2 },
570            (b'MyClass2', b'method5', False): { 'retval': { 'type': b'd' } },
571            (b'MyClass2', b'method6', False): { 'retval': { 'type': b'd' } },
572            (b'MyClass2', b'method7', False): { 'suggestion': "don't use this method" },
573            (b'MyClass2', b'method8', False): { 'suggestion': 'ignore me' },
574            (b'MyClass2', b'method9', False): { 'retval': { 'type': b'd' } },
575            (b'MyClass2', b'method10', False ): { 'retval': { 'type': b'd' } },
576            (b'MyClass2', b'method11', False ): {
577                'retval': {
578                    'type': b'f' if sys.maxsize < 2**32 else b'd'
579                }
580            },
581            (b'MyClass2', b'method13', True): { 'retval': { 'type_modifier': b'n' } },
582            (b'MyClass2', b'method13b', False): {
583                'retval': { 'sel_of_type': b'v@:f', 'c_array_of_fixed_length': 4 },
584            },
585            (b'MyClass2', b'method14', False): {
586                'retval': { 'sel_of_type': b'v@:f' if sys.maxsize < 2**32 else b'v@:d' }
587            },
588            (b'MyClass2', b'method15', False): {
589                'retval': { 'null_accepted': False, 'already_retained': True,  }
590            },
591            (b'MyClass2', b'method16', False): {
592                'retval': { 'c_array_delimited_by_null': True, 'c_array_of_variable_length': True, 'printf_format': True,
593                            'free_result': True, 'already_cfretained': True }
594            },
595            (b'MyClass2', b'method17', False): {
596                'retval': { 'c_array_length_in_arg': 1+2 }
597            },
598            (b'MyClass2', b'method18', False): {
599                'retval': { 'c_array_length_in_arg': (1+2, 2+2) }
600            },
601            (b'MyClass2', b'method19', False): {
602                'retval': { 'c_array_length_in_arg': (4+2, 5+2) }
603            },
604            (b'MyClass2', b'method21', False): {
605                'retval': { 'callable_retained': False, 'callable': {
606                    'retval': { 'type': b'v' },
607                    'arguments': {
608                        0: { 'type': b'@' },
609                        1: { 'type': b'd' },
610                    }
611                }}
612            },
613            (b'MyClass2', b'method22', False): {
614                'retval': { 'callable_retained': False, 'callable': {
615                    'retval': { 'type': b'v' },
616                    'arguments': {
617                        0: { 'type': b'^v' },
618                        1: { 'type': b'@' },
619                        2: { 'type': b'd' },
620                    }
621                }}
622            },
623            (b'MyClass2', b'method23', False): {
624                'retval': { 'callable_retained': True, 'callable': {
625                    'retval': { 'type': b'v' },
626                    'arguments': {
627                        0: { 'type': b'@' },
628                        1: { 'type': b'd' },
629                    }
630                }}
631            },
632            (b'MyClass2', b'method24', False): {
633                'retval': { 'callable_retained': True, 'callable': {
634                    'retval': { 'type': b'v' },
635                    'arguments': {
636                        0: { 'type': b'^v' },
637                        1: { 'type': b'@' },
638                        2: { 'type': b'd' },
639                    }
640                }}
641            },
642            (b'MyClass3', b'method7', False): {
643                'retval': { 'type': b'q' },
644                'arguments': {
645                    2+1: { 'type': b'f' },
646                    2+2: { 'type': b'd' },
647                }
648            },
649            (b'MyClass3', b'method8', False): {
650                'arguments': {
651                    2+1: { 'type': b'd' },
652                    2+2: { 'type': b'd' },
653                }
654            },
655            (b'MyClass3', b'method10', False): {
656                'arguments': {
657                    2+1: { 'type': b'd' }
658                }
659            },
660            (b'MyClass3', b'method11', False): {
661                'arguments': {
662                    2+1: { 'type': b'f' if sys.maxsize < 2**32 else b'd' }
663                }
664            },
665            (b'MyClass3', b'method13', False): {
666                'arguments': {
667                    2+1: { 'type_modifier': b'n' },
668                }
669            },
670            (b'MyClass3', b'method13b', False): {
671                'arguments': {
672                    2+1: { 'sel_of_type': b'v@:f', 'c_array_of_fixed_length': 4 }
673                }
674            },
675            (b'MyClass3', b'method14', False): {
676                'arguments': {
677                    2+1: { 'sel_of_type': b'v@:f' if sys.maxsize < 2**32 else b'v@:d' }
678                }
679            },
680            (b'MyClass3', b'method15', False): {
681                'arguments': {
682                    2+1: { 'null_accepted': False, 'already_retained': True,
683                           'c_array_length_in_result': True }
684                }
685            },
686            (b'MyClass3', b'method16', False): {
687                'arguments': {
688                    2+1: { 'c_array_delimited_by_null': True, 'c_array_of_variable_length': True,
689                           'printf_format': True, 'free_result': True, 'already_cfretained': True }
690                }
691            },
692            (b'MyClass3', b'method17', False): {
693                'arguments': {
694                    2+1:  { 'c_array_length_in_arg': 1+2 },
695                }
696            },
697            (b'MyClass3', b'method18', False): {
698                'arguments': {
699                    2+1:  { 'c_array_length_in_arg': (1+2, 2+2) }
700                }
701            },
702            (b'MyClass3', b'method19', False): {
703                'arguments': {
704                    2+1:  { 'c_array_length_in_arg': (4+2, 5+2) }
705                }
706            },
707            (b'MyClass3', b'method21', False): {
708                'arguments': {
709                    2+1: { 'callable_retained': False, 'callable': {
710                        'retval': { 'type': b'v' },
711                        'arguments': {
712                            0:  { 'type': b'@' },
713                            1:  { 'type': b'd' },
714                        }
715                    }}
716                }
717            },
718            (b'MyClass3', b'method22', False): {
719                'arguments': {
720                    2+1: { 'callable_retained': False, 'callable': {
721                        'retval': { 'type': b'v' },
722                        'arguments': {
723                            0:  { 'type': b'^v' },
724                            1:  { 'type': b'@' },
725                            2:  { 'type': b'd' },
726                        }
727                    }}
728                }
729            },
730            (b'MyClass3', b'method23', False): {
731                'arguments': {
732                    2+1: { 'callable_retained': True, 'callable': {
733                        'retval': { 'type': b'v' },
734                        'arguments': {
735                            0:  { 'type': b'@' },
736                            1:  { 'type': b'd' },
737                        }
738                    }}
739                }
740            },
741            (b'MyClass3', b'method24', True): {
742                'arguments': {
743                    2+1: { 'callable_retained': True, 'callable': {
744                        'retval': { 'type': b'v' },
745                        'arguments': {
746                            0:  { 'type': b'^v' },
747                            1:  { 'type': b'@' },
748                            2:  { 'type': b'd' },
749                        }
750                    }}
751                }
752            },
753        }
754
755
756
757        all_functions = [
758            ('function2', b'v', '', { 'variadic': True }),
759            ('function3', b'v', '', { 'variadic': True, 'c_array_delimited_by_null': True }),
760            ('function4', b'v', '', { 'variadic': True, 'c_array_length_in_arg': 4 }),
761            ('function5', b'd', '', { 'retval': { 'type': b'd' }}),
762            ('function6', b'd', '', { 'retval': { 'type': b'd' }}),
763            ('function9', b'd', '', { 'retval': { 'type': b'd' }}),
764            ('function10', b'd', '', { 'retval': { 'type': b'd' }}),
765            ('function11', b'f' if sys.maxsize < 2**32 else b'd' , '', { 'retval': { 'type': b'f' if sys.maxsize < 2**32 else b'd' }}),
766            ('function13', b'i', '', { 'retval': {'type': b'i', 'type_modifier': b'n' }}),
767            ('function14', b':', '', { 'retval': {'type': b':', 'sel_of_type': b'v@:f', 'c_array_of_fixed_length': 4 }}),
768            ('function15', b':', '', { 'retval': {'type': b':', 'sel_of_type': b'v@:f' if sys.maxsize < 2**32 else b'v@:d' }}),
769            ('function16', b'i', '', { 'retval': {'type': b'i',
770                'null_accepted': False, 'already_retained': True, }}),
771            ('function17', b'i', '', { 'retval': {'type': b'i',
772                'already_cfretained': True, 'c_array_delimited_by_null': True, 'c_array_of_variable_length': True,
773                'printf_format': True, 'free_result': True }}),
774            ('function18', b'i', '', { 'retval': { 'type': b'i', 'c_array_length_in_arg': 1 }}),
775            ('function19', b'i', '', { 'retval': { 'type': b'i', 'c_array_length_in_arg': (1,2) }}),
776            ('function20', b'i', '', { 'retval': { 'type': b'i', 'c_array_length_in_arg': (4,5) }}),
777            ('function21', b'?', '', { 'retval': { 'type': b'?', }}),
778            ('function22', b'?', '', { 'retval': { 'type': b'?', 'callable_retained': False, 'callable': {
779                'retval': { 'type': b'v' },
780                'arguments': {
781                    0: { 'type': b'@' },
782                    1: { 'type': b'd' },
783                }
784            }}}),
785            ('function23', b'@?', '', { 'retval': { 'type': b'@?', 'callable_retained': False, 'callable': {
786                'retval': { 'type': b'v' },
787                'arguments': {
788                    0: { 'type': b'^v' },
789                    1: { 'type': b'@' },
790                    2: { 'type': b'd' },
791                }
792            }}}),
793            ('function24', b'?', '', { 'retval': { 'type': b'?', 'callable_retained': True, 'callable': {
794                'retval': { 'type': b'v' },
795                'arguments': {
796                    0: { 'type': b'@' },
797                    1: { 'type': b'd' },
798                }
799            }}}),
800            ('function25', b'@?', '', { 'retval': { 'type': b'@?', 'callable_retained': True, 'callable': {
801                'retval': { 'type': b'v' },
802                'arguments': {
803                    0: { 'type': b'^v' },
804                    1: { 'type': b'@' },
805                    2: { 'type': b'd' },
806                }
807            }}}),
808            ('function26', b'qfd', '', {
809                'retval': { 'type': b'q' },
810                'arguments': {
811                    0:  { 'type': b'f' },
812                    1:  { 'type': b'd' }
813                }
814            }),
815            ('function27', b'vdd', '', {
816                'arguments': {
817                    0:  { 'type': b'd' },
818                    1:  { 'type': b'd' }
819                }
820            }),
821            ('function29', b'vd', '', {
822                'arguments': {
823                    0:  { 'type': b'd' },
824                }
825            }),
826            ('function30', b'vf' if sys.maxsize < 2**32 else b'vd' , '', {
827                'arguments': {
828                    0:  { 'type': b'f' if sys.maxsize < 2**32 else b'd'  },
829                }
830            }),
831            ('function32', b'v@', '', {
832                'arguments': {
833                    0:  { 'type': b'@', 'type_modifier': b'n' }
834                }
835            }),
836            ('function33', b'v:', '', {
837                'arguments': {
838                    0:  { 'type': b':', 'sel_of_type': b'v@:f', 'c_array_of_fixed_length': 4 }
839                }
840            }),
841            ('function34', b'v:', '', {
842                'arguments': {
843                    0:  { 'type': b':', 'sel_of_type': b'v@:f' if sys.maxsize < 2**32 else b'v@:d' }
844                }
845            }),
846            ('function35', b'v@', '', {
847                'arguments': {
848                    0:  { 'type': b'@', 'null_accepted': False, 'already_retained': True, 'c_array_length_in_result': True }
849                }
850            }),
851            ('function36', b'v@', '', {
852                'arguments': {
853                    0:  { 'type': b'@', 'c_array_delimited_by_null': True, 'already_cfretained': True, 'c_array_of_variable_length': True,
854                        'printf_format': True, 'free_result': True }
855                }
856            }),
857            ('function37', b'v@', '', {
858                'arguments': {
859                    0:  { 'type': b'@', 'c_array_length_in_arg': 1 }
860                }
861            }),
862            ('function38', b'v@', '', {
863                'arguments': {
864                    0:  { 'type': b'@', 'c_array_length_in_arg': (1,2) }
865                }
866            }),
867            ('function39', b'v@', '', {
868                'arguments': {
869                    0:  { 'type': b'@', 'c_array_length_in_arg': (4,5) }
870                }
871            }),
872            ('function40', b'v?', '', {
873                'arguments': {
874                    0:  { 'type': b'?' }
875                }
876            }),
877            ('function41', b'v?', '', {
878                'arguments': {
879                    0: {
880                        'type': b'?',
881                        'callable_retained': False,
882                        'callable': {
883                            'retval': { 'type': b'v' },
884                            'arguments': {
885                                0: { 'type': b'@' },
886                                1: { 'type': b'd' }
887                            }
888                        }
889                    }
890                }
891            }),
892            ('function42', b'v@?', '', {
893                'arguments': {
894                    0: {
895                        'type': b'@?',
896                        'callable_retained': False,
897                        'callable': {
898                            'retval': { 'type': b'v' },
899                            'arguments': {
900                                0: { 'type': b'^v' },
901                                1: { 'type': b'@' },
902                                2: { 'type': b'd' }
903                            }
904                        }
905                    }
906                }
907            }),
908            ('function43', b'v?', '', {
909                'arguments': {
910                    0: {
911                        'type': b'?',
912                        'callable_retained': True,
913                        'callable': {
914                            'retval': { 'type': b'v' },
915                            'arguments': {
916                                0: { 'type': b'@' },
917                                1: { 'type': b'd' }
918                            }
919                        }
920                    }
921                }
922            }),
923            ('function44', b'v@?', '', {
924                'arguments': {
925                    0: {
926                        'type': b'@?',
927                        'callable_retained': True,
928                        'callable': {
929                            'retval': { 'type': b'v' },
930                            'arguments': {
931                                0: { 'type': b'^v' },
932                                1: { 'type': b'@' },
933                                2: { 'type': b'd' }
934                            }
935                        }
936                    }
937                }
938            }),
939        ]
940
941        self.maxDiff = None
942        if sys.maxsize <= 2**32:
943            all_protocols = [
944                ('protocol2', [
945                    objc.selector(None, b'selector1', b'v@:f' if sys.maxsize < 2**32 else b'v@:d'),
946                    objc.selector(None, b'selector2', b'v@:f' if sys.maxsize < 2 **32 else b'v@:d'),
947                    objc.selector(None, b'selector3', b'v@:f' if sys.maxsize < 2 **32 else b'v@:d', isClassMethod=True),
948                    objc.selector(None, b'selector4', b'v@:f' if sys.maxsize < 2 **32 else b'v@:d'),
949                    objc. selector(None, b'selector7', b'v@:f'),
950                    objc.selector(None, b'selector8', b'v@:f', isClassMethod=True),
951                ]),
952            ]
953        else:
954            all_protocols = [
955                ('protocol2', [
956                    objc.selector(None, b'selector1', b'v@:f' if sys.maxsize < 2**32 else b'v@:d'),
957                    objc.selector(None, b'selector2', b'v@:f' if sys.maxsize < 2 **32 else b'v@:d'),
958                    objc.selector(None, b'selector3', b'v@:f' if sys.maxsize < 2 **32 else b'v@:d', isClassMethod=True),
959                    objc.selector(None, b'selector4', b'v@:f' if sys.maxsize < 2 **32 else b'v@:d'),
960                    objc.selector(None, b'selector6', b'v@:@'),
961                    objc. selector(None, b'selector7', b'v@:f'),
962                    objc.selector(None, b'selector8', b'v@:f', isClassMethod=True),
963                ]),
964            ]
965
966        all_structs = [
967            ('struct3', b'{struct3=@@}', None),
968            ('struct4', b'{struct3=ff}' if sys.maxsize < 2**32 else b'{struct4=dd}', None),
969            ('struct5', b'{struct3=@@}', None),
970            ('struct6', b'{struct6=ZZ}', None),
971            ('struct7', b'{struct7=B@}', None),
972            ('struct8', b'{struct8=@@}', sys.maxsize),
973            ('struct9', b'{struct9=ii}', None),
974        ]
975
976        self.maxDiff = None
977        self.assertItemsEqual(prs.constants,    all_constants)
978        self.assertEqual(prs.values,            all_values)
979        self.assertItemsEqual(prs.opaque,       all_opaque)
980        self.assertItemsEqual(prs.func_aliases, all_func_aliases)
981        self.assertItemsEqual(prs.cftypes,      all_cftypes)
982        self.assertItemsEqual(prs.functions,    all_functions)
983        self.assertEqual(prs.meta,              all_methods)
984        self.assertItemsEqual(prs.informal_protocols,    all_protocols)
985        self.assertItemsEqual(prs.structs,      all_structs)
986
987    def assertIsIdentifier(self, value):
988        m = IDENTIFIER.match(value)
989        if m is None:
990            self.fail("'%s' is not an identifier"%(value,))
991
992
993    def assert_valid_callable(self, meta, function):
994        if function:
995            if 'arguments' in meta:
996                indexes = list(sorted(meta["arguments"]))
997                self.assertEqual(indexes, list(range(len(indexes))))
998
999        valid_keys = {
1000            "type",
1001            "type_modifier",
1002            "already_retained",
1003            "already_cfretained",
1004            "c_array_length_in_result",
1005            "c_array_delimited_by_null",
1006            "printf_format",
1007            "null_accepted",
1008            "c_array_of_variable_length",
1009            "c_array_length_in_arg",
1010            "c_array_of_fixed_length",
1011            "sel_of_type",
1012            "callable",
1013            "callable_retained",
1014            "free_result",
1015        }
1016
1017        if "retval" in meta:
1018            if "type" in meta["retval"]:
1019                self.assertIsInstance(meta["retval"]["type"], bytes)
1020                self.assertEqual(len(objc.splitSignature(meta["retval"]["type"])), 1)
1021
1022            if "type_modifier" in meta["retval"]:
1023                self.assertIsInstance(meta["retval"]["type_modifier"], bytes)
1024                self.assertIn(meta["retval"]["type_modifier"], [b"o", b"n", b"N"])
1025
1026            if "callable" in meta["retval"]:
1027                self.assert_valid_callable(meta["retval"]["callable"], True)
1028                if "callable_retained" in meta["retval"]:
1029                    self.assertIsInstance(meta["retval"]["callable_retained"], bool)
1030            else:
1031                self.assertNotIn("callable_retained", meta["retval"])
1032
1033            if "sel_of_type" in meta["retval"]:
1034                split = objc.splitSignature(meta["retval"]["sel_of_type"])
1035                self.assertEqual(split[1], objc._C_ID)
1036                self.assertEqual(split[2], objc._C_SEL)
1037
1038            if "already_retained" in meta["retval"]:
1039                self.assertIsInstance(meta["retval"]["already_retained"], bool)
1040                self.assertNotIn("already_cfretained", meta["retval"])
1041
1042            if "already_cfretained" in meta["retval"]:
1043                self.assertIsInstance(meta["retval"]["already_cfretained"], bool)
1044
1045            if "free_result" in meta["retval"]:
1046                self.assertIsInstance(meta["retval"]["free_result"], bool)
1047
1048            key = "c_array_of_fixed_length"
1049            if key in meta["retval"]:
1050                self.assertIsInstance(meta["retval"][key], (int, long))
1051
1052            key = "c_array_length_in_arg"
1053            if key in meta["retval"]:
1054                if isinstance(meta["retval"][key], tuple):
1055                    self.assertEqual(len(meta["retval"][key]), 2)
1056                    self.assertTrue(all(isinstance(x, (int, long)) for x in meta["retval"][key]))
1057                else:
1058                    self.assertIsInstance(meta["retval"][key], (int, long))
1059
1060            for key in ("c_array_delimited_by_null", "printf_format", "c_array_of_variable_length", "null_accepted"):
1061                if key in meta["retval"]:
1062                    self.assertIsInstance(meta["retval"][key], bool)
1063
1064
1065            self.assertNotIn("c_array_length_in_result", meta["retval"])
1066            self.assertEqual(set(meta["retval"]) - valid_keys, set())
1067
1068        if 'arguments' in meta:
1069            for idx in meta["arguments"]:
1070                self.assertIsInstance(idx, (int, long))
1071                arg = meta["arguments"][idx]
1072
1073                if "type" in arg:
1074                    self.assertIsInstance(arg["type"], bytes)
1075                    self.assertEqual(len(objc.splitSignature(arg["type"])), 1)
1076
1077                if "type_modifier" in arg:
1078                    self.assertIsInstance(arg["type_modifier"], bytes)
1079                    self.assertIn(arg["type_modifier"], [b"o", b"n", b"N"])
1080
1081                if "callable" in arg:
1082                    self.assert_valid_callable(arg["callable"], True)
1083                    if "callable_retained" in arg:
1084                        self.assertIsInstance(arg["callable_retained"], bool)
1085                else:
1086                    self.assertNotIn("callable_retained", arg)
1087
1088                if "sel_of_type" in arg:
1089                    split = objc.splitSignature(arg["sel_of_type"])
1090                    self.assertEqual(split[1], objc._C_ID)
1091                    self.assertEqual(split[2], objc._C_SEL)
1092
1093                if "already_retained" in arg:
1094                    self.assertIsInstance(arg["already_retained"], bool)
1095                    self.assertNotIn("already_cfretained", arg)
1096
1097                if "already_cfretained" in arg:
1098                    self.assertIsInstance(arg["already_cfretained"], bool)
1099
1100                if "free_result" in arg:
1101                    self.assertIsInstance(arg["free_result"], bool)
1102
1103                if "c_array_of_fixed_length" in arg:
1104                    self.assertIsInstance(arg["c_array_of_fixed_length"], (int, long))
1105
1106                if "c_array_length_in_arg" in arg:
1107                    if isinstance(arg["c_array_length_in_arg"], (int, long)):
1108                        pass
1109
1110                    else:
1111                        self.assertIsInstance(arg["c_array_length_in_arg"], tuple)
1112                        self.assertEqual(len(arg["c_array_length_in_arg"]), 2)
1113                        for x in arg["c_array_length_in_arg"]:
1114                            self.assertIsInstance(x, (int, long))
1115
1116
1117                for key in ("c_array_delimited_by_null", "printf_format", "c_array_of_variable_length",
1118                            "null_accepted", "c_array_length_in_result"):
1119                    if key in arg:
1120                        self.assertIsInstance(arg[key], bool)
1121
1122                self.assertEqual(set(arg) - valid_keys, set())
1123
1124        if "suggestion" in meta:
1125            self.assertIsInstance(meta["suggestion"], basestring)
1126
1127        if "variadic" in meta:
1128            self.assertIsInstance(meta["variadic"], bool)
1129
1130        if "variadic" in meta and meta["variadic"]:
1131            self.assertEqual(set(meta.keys()) - {"arguments", "retval", "variadic", "classmethod",
1132                    "c_array_length_in_arg", "c_array_delimited_by_null", "suggestion"}, set())
1133
1134            found = False
1135            if "c_array_length_in_arg" in meta:
1136                self.assertIsInstance(meta["c_array_length_in_arg"], (int, long))
1137                self.assertNotIn("c_array_delimited_by_null", meta)
1138                found = True
1139
1140            if "c_array_delimited_by_null" in meta:
1141                self.assertIsInstance(meta["c_array_delimited_by_null"], bool)
1142                found = False
1143
1144            for idx in meta.get("arguments", {}):
1145                arg = meta["arguments"][idx]
1146                if "printf_format" in arg and arg["printf_format"]:
1147                    if found:
1148                        self.fail("meta for variadic with two ways to determine size: %s"%(meta,))
1149                    found = True
1150
1151            # NOTE: disabled because having unsupported variadic methods would be fine (e.g.
1152            #       metadata says the method is variadic, but it doesn't fall into one of the
1153            #       supported categories)
1154            #if not found:
1155            #    self.fail("meta for variadic without method for determining size: %s"%(meta,))
1156
1157        else:
1158            self.assertEqual(set(meta.keys()) - {"arguments", "retval", "variadic", "suggestion", "classmethod" }, set())
1159
1160    def assert_valid_bridgesupport(self, framework_name, xmldata):
1161        prs = bridgesupport._BridgeSupportParser(xmldata, framework_name)
1162
1163        for item in prs.cftypes:
1164            if len(item) == 3:
1165                name, encoding, typeId = item
1166                tollfreeName = None
1167
1168            elif len(item) == 4:
1169                name, encoding, typeId, tollfreeName = item
1170                self.assertIsNot(tollfreeName, None)
1171
1172
1173            else:
1174                self.fail("Wrong item length in cftypes: %s"%(item,))
1175
1176            self.assertIsInstance(name, basestring)
1177            self.assertIsInstance(encoding, bytes)
1178            self.assertEqual(len(objc.splitSignature(encoding)), 1)
1179            if tollfreeName is None:
1180                self.assertIsInstance(typeId, (int, long))
1181
1182            else:
1183                self.assertIs(typeId, None)
1184                self.assertIsInstance(tollfreeName, basestring)
1185
1186        for name, typestr, magic in prs.constants:
1187            self.assertIsInstance(name, basestring)
1188            self.assertIsInstance(typestr, bytes)
1189            self.assertIsInstance(magic, bool)
1190            self.assertEqual(len(objc.splitSignature(typestr)), 1)
1191
1192        for name, orig in prs.func_aliases:
1193            self.assertIsInstance(name, basestring)
1194            self.assertIsInstance(orig, basestring)
1195            self.assertIsIdentifier(name)
1196
1197        for name, encoding, doc, meta in prs.functions:
1198            self.assertIsInstance(name, basestring)
1199            self.assertIsInstance(encoding, bytes)
1200
1201            # check that signature string is well-formed:
1202            objc.splitSignature(encoding)
1203
1204            self.assertEqual(doc, "")
1205            self.assertIsInstance(meta, dict)
1206            self.assert_valid_callable(meta, function=True)
1207
1208        for name, method_list in prs.informal_protocols:
1209            self.assertIsInstance(name, basestring)
1210            self.assertIsInstance(method_list, list)
1211            for sel in method_list:
1212                self.assertIsInstance(sel, objc.selector)
1213                self.assertIs(sel.callable, None)
1214
1215        for clsname, selname, is_class in prs.meta:
1216            meta = prs.meta[(clsname, selname, is_class)]
1217            self.assertIsInstance(clsname, bytes)
1218            self.assertIsInstance(is_class, bool)
1219            self.assertIsInstance(selname, bytes)
1220            self.assertIsInstance(meta, dict)
1221            self.assert_valid_callable(meta, function=False)
1222
1223        for name, typestr in prs.opaque:
1224            self.assertIsInstance(name, basestring)
1225            self.assertIsInstance(typestr, bytes)
1226
1227            self.assertEqual(len(objc.splitSignature(typestr)), 1)
1228
1229        for name, typestr, alias in prs.structs:
1230            self.assertIsInstance(name, basestring)
1231            self.assertIsInstance(typestr, bytes)
1232            self.assertEqual(len(objc.splitSignature(typestr)), 1)
1233
1234        for name in prs.values:
1235            self.assertIsInstance(prs.values[name], (basestring, long, int, float, bytes, type(None)))
1236
1237        return prs
1238
1239class Patcher (object):
1240    def __init__(self):
1241        self._changes = {}
1242
1243    def patch(self, path, value):
1244        if path not in self._changes:
1245            self._changes[path] = self.get(path)
1246        self.set(path, value)
1247
1248    def get(self, path):
1249        module, name = path.rsplit('.', 1)
1250        m = __import__(module)
1251        for p in module.split('.')[1:]:
1252            m = getattr(m, p)
1253
1254        return getattr(m, name)
1255
1256    def set(self, path, value):
1257        module, name = path.rsplit('.', 1)
1258        m = __import__(module)
1259        for p in module.split('.')[1:]:
1260            m = getattr(m, p)
1261
1262        setattr(m, name, value)
1263
1264    def __enter__(self):
1265        return self
1266
1267    def __exit__(self, type, value, tp):
1268        for k in self._changes:
1269            self.set(k, self._changes[k])
1270
1271class TestParseBridgeSupport (TestCase):
1272    def test_calls(self):
1273        # - Minimal XML with all types of metadata
1274        # - Mock the APIs used by parseBridgeSupport
1275        #   (with strict verification of arguments, based on C code)
1276        # - Verify changes to globals where possible
1277        # - Verify 'updatingmetadata' state
1278
1279        class InlineTab (object): pass
1280        def loadConstant(name, typestr, magic):
1281            self.assertIsInstance(name, str)
1282            self.assertIsInstance(typestr, bytes)
1283            self.assertEqual(len(objc.splitSignature(typestr)), 1)
1284            self.assertIsInstance(magic, bool)
1285
1286            if 'raise' in name:
1287                raise AttributeError(name)
1288
1289            return '<constant %r>'%(name,)
1290
1291        SENTINEL=object()
1292        def registerCFSignature(name, encoding, typeId, tollfreeName=SENTINEL):
1293            self.assertIsInstance(name, str)
1294            self.assertIsInstance(encoding, bytes);
1295            self.assertIsInstance(typeId, (int, long, type(None)));
1296            if tollfreeName is not SENTINEL:
1297                self.assertIsInstance(tollfreeName, str)
1298
1299            if typeId is None:
1300                if tollfreeName is SENTINEL:
1301                    raise ValueError("Must specify a typeid when not toll-free")
1302
1303            return "<cftype %r>"%(name,)
1304
1305        metadata_registry = {}
1306        def registerMetaDataForSelector(class_, selector, metadata):
1307            self.assertIsInstance(class_, bytes)
1308            self.assertIsInstance(selector, bytes)
1309            self.assertIsInstance(metadata, dict)
1310
1311            # XXX: It might be nice to validate the contents of
1312            # metadata, but that can be added later.
1313
1314            metadata_registry[(class_, selector)] = metadata
1315
1316        def createOpaquePointerType(name, typestr, doc=None):
1317            self.assertIsInstance(name, str)
1318            self.assertIsInstance(typestr, bytes)
1319            self.assertIsInstance(doc, (str, type(None)))
1320            self.assertEqual(len(objc.splitSignature(typestr)), 1)
1321            self.assertTrue(typestr.startswith(objc._C_PTR))
1322            return '<pointer %r>'%(name,)
1323
1324        def createStructType(name, typestr, fieldnames, doc=None, pack=-1):
1325            self.assertIsInstance(name, str)
1326            self.assertIsInstance(typestr, bytes)
1327            self.assertIsInstance(fieldnames, (list, tuple, type(None)))
1328            self.assertIsInstance(doc, (str, type(None)))
1329            self.assertIsInstance(pack, (int, long))
1330            self.assertTrue(-1 <= pack <= 32)
1331            if fieldnames is not None:
1332                for nm in fieldnames:
1333                    self.assertIsInstance(nm, (str, bytes))
1334            return '<struct %r>'%(name,)
1335
1336        def createStructAlias(name, typestr, structType):
1337            self.assertIsInstance(name, str)
1338            self.assertIsInstance(typestr, bytes)
1339
1340        def informal_protocol(name, method_list):
1341            self.assertIsInstance(name, str)
1342            for item in method_list:
1343                self.assertIsInstance(item, objc.selector)
1344                self.assertIs(item.callable, None)
1345
1346            return '<informal_protocol %r>'%(name,)
1347
1348        def loadBundleFunctions(bundle, module_globals, functionInfo, skip_undefined=True):
1349            self.assertIs(bundle, None)
1350            self.assertIsInstance(module_globals, dict)
1351            self.assertIsInstance(functionInfo, (list, tuple))
1352            self.assertIsInstance(skip_undefined, bool)
1353            for item in functionInfo:
1354                self.assertIsInstance(item, (tuple, list))
1355                self.assertTrue(2 <= len(item) <= 4)
1356                self.assertIsInstance(item[0], str)
1357                self.assertIsInstance(item[1], bytes)
1358                if len(item) > 2:
1359                    self.assertIsInstance(item[2], (str, type(None)))
1360                if len(item) > 3:
1361                    self.assertIsInstance(item[3], dict)
1362
1363                if 'inline' in item[0]:
1364                    continue
1365
1366                module_globals[item[0]] = '<function %r>'%(item[0],)
1367
1368        def loadFunctionList(function_list, module_globals, functionInfo, skip_undefined=True):
1369            self.assertIsInstance(function_list, InlineTab)
1370            self.assertIsInstance(module_globals, dict)
1371            self.assertIsInstance(functionInfo, (list, tuple))
1372            self.assertIsInstance(skip_undefined, bool)
1373            for item in functionInfo:
1374                self.assertIsInstance(item, (tuple, list))
1375                self.assertTrue(2 <= len(item) <= 4)
1376                self.assertIsInstance(item[0], str)
1377                self.assertIsInstance(item[1], bytes)
1378                if len(item) > 2:
1379                    self.assertIsInstance(item[2], (str, type(None)))
1380                if len(item) > 3:
1381                    self.assertIsInstance(item[3], dict)
1382                if item[0] not in module_globals:
1383                    module_globals[item[0]] = '<inline_function %r>'%(item[0],)
1384
1385
1386        _meta_updates = []
1387        def _updatingMetadata(flag):
1388            self.assertIsInstance(flag, bool)
1389            _meta_updates.append(flag)
1390
1391        with Patcher() as p:
1392            p.patch('objc._updatingMetadata', _updatingMetadata)
1393            p.patch('objc._loadConstant', loadConstant)
1394            p.patch('objc.registerCFSignature', registerCFSignature)
1395            p.patch('objc.registerMetaDataForSelector', registerMetaDataForSelector)
1396            p.patch('objc.createOpaquePointerType', createOpaquePointerType)
1397            p.patch('objc.createStructType', createStructType)
1398            p.patch('objc.createStructAlias', createStructAlias)
1399            p.patch('objc.informal_protocol', informal_protocol)
1400            p.patch('objc.loadBundleFunctions', loadBundleFunctions)
1401            p.patch('objc.loadFunctionList', loadFunctionList)
1402            p.patch('sys.modules', sys.modules.copy())
1403
1404            # 1. Empty metadata
1405            _meta_updates  = []
1406            metadata_registry = {}
1407            module_globals = {}
1408
1409            objc.parseBridgeSupport(b'''\
1410            <signatures>
1411            </signatures>
1412            ''', module_globals, 'TestFramework')
1413
1414            self.assertEqual(_meta_updates, [True, False])
1415            self.assertEqual(metadata_registry, {})
1416            self.assertEqual(module_globals, {})
1417
1418            # 2. Various metadata
1419            _meta_updates  = []
1420            metadata_registry = {}
1421            module_globals = {}
1422            orig_libraries = list(bridgesupport._libraries)
1423
1424            xml = b'''\
1425            <signatures>
1426              <opaque name='opaque_type' type='^{opaque}'/>
1427              <struct name='OCPoint' type='{OCPoint=dd}' />
1428              <struct name='OCPoint2' type='{OCPoint2=dd}' alias='distutils.sysconfig.get_config_var'/>
1429              <enum name='enum_value' value='42' />
1430              <constant name='const_value' type='@' />
1431              <constant name='const_raise' type='@' />
1432              <cftype name='cftype_value' type='^{cftype}' />
1433              <class name='class1'>
1434                <method selector='sel1:' class_method='false'>
1435                  <arg index='0' null_accepted='false' type_modifier='o' />
1436                </method>
1437              </class>
1438              <function name='function1'>
1439                 <retval type='f' />
1440                 <arg type='@' />
1441                 <arg type='d' />
1442              </function>
1443              <function name='function2'>
1444                 <retval type='d' />
1445                 <arg type='d' />
1446              </function>
1447              <function name='inline_function'>
1448                <retval type='i' />
1449              </function>
1450              <function_pointer name='function3' original='function2'/>
1451              <function_pointer name='function4' original='no_such_function'/>
1452              <informal_protocol name='protocol1'>
1453                <method selector='sel1:' type='v@:@' />
1454              </informal_protocol>
1455              <informal_protocol name='protocol2'>
1456                <method selector='sel2:' type='v@:@' />
1457              </informal_protocol>
1458            </signatures>
1459            '''
1460            objc.parseBridgeSupport(xml, module_globals, 'TestFramework')
1461
1462            self.assertEqual(_meta_updates, [True, False])
1463            self.assertEqual(metadata_registry, {
1464                (b'class1', b'sel1:'): {
1465                    'arguments': {
1466                        2: { 'null_accepted': False, 'type_modifier': b'o' }
1467                    }
1468                },
1469            })
1470
1471            from distutils.sysconfig import get_config_var
1472
1473            self.assertIn('protocols', module_globals)
1474            m = module_globals.pop('protocols')
1475            self.assertIsInstance(m, type(objc))
1476            self.assertEqual(m.__name__, 'TestFramework.protocols')
1477            self.assertEqual(m.protocol1, "<informal_protocol 'protocol1'>")
1478            self.assertEqual(m.protocol2, "<informal_protocol 'protocol2'>")
1479            self.assertIs(sys.modules['TestFramework.protocols'], m)
1480            self.assertEqual(module_globals, {
1481                "enum_value":   42,
1482                "const_value":  "<constant 'const_value'>",
1483                "cftype_value":  "<cftype 'cftype_value'>",
1484                "function1":     "<function 'function1'>",
1485                "function2":     "<function 'function2'>",
1486                "function3":     "<function 'function2'>",
1487                "OCPoint":       "<struct 'OCPoint'>",
1488                "OCPoint2":      get_config_var,
1489                "opaque_type":   "<pointer 'opaque_type'>",
1490            })
1491            self.assertEqual(orig_libraries, bridgesupport._libraries)
1492
1493            # 3. inlineTab
1494            _meta_updates  = []
1495            metadata_registry = {}
1496            module_globals = {}
1497            orig_libraries = list(bridgesupport._libraries)
1498
1499            xml = b'''\
1500            <signatures>
1501              <function name='function1'>
1502                 <retval type='f' />
1503                 <arg type='@' />
1504                 <arg type='d' />
1505              </function>
1506              <function name='function2'>
1507                 <retval type='d' />
1508                 <arg type='d' />
1509              </function>
1510              <function name='inline_function_name'>
1511                <retval type='i' />
1512              </function>
1513            </signatures>
1514            '''
1515            objc.parseBridgeSupport(xml, module_globals, 'TestFramework2', inlineTab=InlineTab())
1516
1517            self.assertEqual(_meta_updates, [True, False])
1518            self.assertEqual(metadata_registry, {})
1519            self.assertNotIn('protocols', module_globals)
1520            self.assertNotIn('TestFramework2.protocols', sys.modules)
1521            self.assertEqual(module_globals, {
1522                "function1":     "<function 'function1'>",
1523                "function2":     "<function 'function2'>",
1524                "inline_function_name":     "<inline_function 'inline_function_name'>",
1525            })
1526            self.assertEqual(orig_libraries, bridgesupport._libraries)
1527
1528            # 4. dylib usage
1529            _meta_updates  = []
1530            metadata_registry = {}
1531            module_globals = {}
1532            orig_libraries = list(bridgesupport._libraries)
1533
1534            xml = b'''\
1535            <signatures>
1536              <function name='function1'>
1537                 <retval type='f' />
1538                 <arg type='@' />
1539                 <arg type='d' />
1540              </function>
1541              <function name='function2'>
1542                 <retval type='d' />
1543                 <arg type='d' />
1544              </function>
1545              <function name='inline_function_name'>
1546                <retval type='i' />
1547              </function>
1548            </signatures>
1549            '''
1550            objc.parseBridgeSupport(xml, module_globals, 'TestFramework2', dylib_path='/usr/lib/libxml2.dylib')
1551
1552            self.assertEqual(_meta_updates, [True, False])
1553            self.assertEqual(metadata_registry, {})
1554            self.assertNotIn('protocols', module_globals)
1555            self.assertNotIn('TestFramework2.protocols', sys.modules)
1556            self.assertEqual(module_globals, {
1557                "function1":     "<function 'function1'>",
1558                "function2":     "<function 'function2'>",
1559            })
1560            self.assertEqual(len(orig_libraries)+1, len(bridgesupport._libraries))
1561            self.assertIsInstance(bridgesupport._libraries[-1], ctypes.CDLL)
1562            self.assertEqual(bridgesupport._libraries[-1]._name, '/usr/lib/libxml2.dylib')
1563
1564
1565
1566class TestInitFrameworkWrapper (TestCase):
1567    def test_calls_parse_helper(self):
1568        # Test functionality of initFrameworkWrapper and _parseBridgeSupport
1569        # by mocking 'parseBridgeSupport' (and the location of the caller)
1570        with Patcher() as p:
1571            calls = []
1572            raise_exception = None
1573            update_globals = None
1574
1575            def parseBridgeSupport(xmldata, globals, frameworkName, dylib_path=None, inlineTab=None):
1576                calls.append((xmldata, globals, frameworkName, dylib_path, inlineTab))
1577                if update_globals is not None:
1578                    globals.update(update_globals)
1579
1580                if raise_exception is not None:
1581                    raise raise_exception()
1582
1583            class MockModule (object):
1584                pass
1585
1586
1587            p.patch('objc.parseBridgeSupport', parseBridgeSupport)
1588
1589
1590            # 1. Verify that failures to load bridgesupport emit a warning
1591            calls = []
1592            raise_exception = objc.internal_error
1593            update_globals = None
1594
1595            with filterWarnings("error", RuntimeWarning):
1596                self.assertRaises(RuntimeWarning, bridgesupport._parseBridgeSupport, '', {}, 'TestFramework')
1597
1598            calls = []
1599
1600            with filterWarnings("ignore", RuntimeWarning):
1601                g = {}
1602                update_globals = {'foo': 42, 'bar': 33,  'protocols': MockModule() }
1603                bridgesupport._parseBridgeSupport('', g, 'TestFramework')
1604                self.assertEqual(g, update_globals)
1605                self.assertEqual(calls, [('', g, 'TestFramework', None, None)])
1606
1607                self.assertNotEqual(len(g['protocols'].__dict__), 0)
1608                for v in g['protocols'].__dict__.values():
1609                    self.assertIsInstance(v, objc.formal_protocol)
1610
1611
1612            # 2. Run without problems, without 'protocols' in dictionary
1613            raise_exception = None
1614            update_globals = {'foo': 42, 'bar': 33,  }
1615
1616            calls = []
1617            g = {}
1618            bridgesupport._parseBridgeSupport('', g, 'TestFramework', 'a', 'b')
1619            self.assertEqual(g, update_globals)
1620            self.assertEqual(calls, [('', g, 'TestFramework', 'a', 'b')])
1621            self.assertNotIn('protocols', g)
1622
1623            calls = []
1624            g = {}
1625            bridgesupport._parseBridgeSupport('', g, 'TestFramework', inlineTab='a')
1626            self.assertEqual(g, update_globals)
1627            self.assertEqual(calls, [('', g, 'TestFramework', None, 'a')])
1628            self.assertNotIn('protocols', g)
1629
1630            # 3. Run witout problems, with 'protocols' in dictionary
1631            calls = []
1632            raise_exception = None
1633            update_globals = {'foo': 42, 'bar': 33,  'protocols': MockModule() }
1634
1635            g = {}
1636            bridgesupport._parseBridgeSupport('', g, 'TestFramework', 'a', 'b')
1637            self.assertEqual(g, update_globals)
1638            self.assertEqual(calls, [('', g, 'TestFramework', 'a', 'b')])
1639            self.assertNotEqual(len(g['protocols'].__dict__), 0)
1640            for v in g['protocols'].__dict__.values():
1641                self.assertIsInstance(v, objc.formal_protocol)
1642
1643            calls = []
1644            g = {}
1645            bridgesupport._parseBridgeSupport('', g, 'TestFramework', inlineTab='a')
1646            self.assertEqual(g, update_globals)
1647            self.assertEqual(calls, [('', g, 'TestFramework', None, 'a')])
1648            self.assertNotEqual(len(g['protocols'].__dict__), 0)
1649            for v in g['protocols'].__dict__.values():
1650                self.assertIsInstance(v, objc.formal_protocol)
1651
1652
1653    def test_calls_initwrappper(self):
1654        with Patcher() as p:
1655
1656            SENTINEL=object()
1657
1658            class InlineTab (object): pass
1659
1660            bundle_resources = {}
1661            class Bundle (object):
1662                def __init__(self, calls=None):
1663                    if calls is None:
1664                        calls = []
1665                    self.calls = calls
1666
1667                def pathForResource_ofType_inDirectory_(self, name, type, directory):
1668                    self.calls.append((name, type, directory))
1669                    return bundle_resources.get((name, type), None)
1670
1671                def __eq__(self, other):
1672                    if type(self) is not type(other):
1673                        return False
1674
1675                    return self.calls == other.calls
1676
1677                def __repr__(self):
1678                    return '<Bundle calls=%r>'%(self.calls,)
1679
1680
1681            load_calls = []
1682            bundle_exception = None
1683            def loadBundle(module_name, module_globals, bundle_path=SENTINEL, bundle_identifier=SENTINEL, scan_classes=True):
1684                if bundle_exception is not None:
1685                    bundle_exception(module_name, bundle_path, bundle_identifier)
1686                self.assertIsInstance(module_name, str)
1687                self.assertIsInstance(module_globals, dict)
1688                if bundle_path is not SENTINEL:
1689                    self.assertIsInstance(bundle_path, str)
1690                if bundle_identifier is not SENTINEL:
1691                    self.assertIsInstance(bundle_identifier, str)
1692                bool(scan_classes)
1693                bundle = Bundle()
1694                load_calls.append((bundle, module_name, module_globals, bundle_path, bundle_identifier, scan_classes))
1695                return bundle
1696
1697            resources = {}
1698            def resource_exists(package, name):
1699                return (package, name) in resources
1700
1701            def resource_string(package, name):
1702                return resources[(package, name)]
1703
1704            parse_calls = []
1705            def parseBridgeSupport(xml,  globals, framework, dylib_path=None, inlineTab=None):
1706                parse_calls.append((xml, globals, framework, dylib_path, inlineTab))
1707
1708            TEST_BRIDGESUPPORT_DIRECTORIES=[]
1709
1710            p.patch("objc.loadBundle", loadBundle)
1711            p.patch("pkg_resources.resource_exists", resource_exists)
1712            p.patch("pkg_resources.resource_string", resource_string)
1713            p.patch("objc._bridgesupport._parseBridgeSupport", parseBridgeSupport)
1714            p.patch("objc._bridgesupport.BRIDGESUPPORT_DIRECTORIES", TEST_BRIDGESUPPORT_DIRECTORIES)
1715
1716            helper_dir = os.path.join(os.path.dirname(__file__), 'data_bridgesupport')
1717
1718
1719            # 1. No resource files, no bundle files, no library files
1720            resources = {}
1721            bundle_resources = {}
1722            TEST_BRIDGESUPPORT_DIRECTORIES[:] = [ os.path.join(helper_dir, 'empty') ]
1723
1724            def basic_verify(g):
1725                self.assertIs(g['objc'], objc)
1726                self.assertIs(g['super'], objc.super)
1727
1728            load_calls = []
1729            parse_calls = []
1730            g = {}
1731            objc.initFrameworkWrapper("TestFramework", "/Library/Framework/Test.framework", "com.apple.Test", g)
1732            basic_verify(g)
1733            self.assertEqual(len(g), 2)
1734            self.assertEqual(load_calls, [
1735                (Bundle([('TestFramework', 'bridgesupport', 'BridgeSupport')]), 'TestFramework', g, SENTINEL, 'com.apple.Test', True)
1736            ])
1737            self.assertEqual(parse_calls, [])
1738
1739            load_calls = []
1740            parse_calls = []
1741            g = {}
1742            objc.initFrameworkWrapper("TestFramework", "/Library/Framework/Test.framework", None, g)
1743            basic_verify(g)
1744            self.assertEqual(len(g), 2)
1745            self.assertEqual(load_calls, [
1746                (Bundle([('TestFramework', 'bridgesupport', 'BridgeSupport')]), 'TestFramework', g, '/Library/Framework/Test.framework', SENTINEL, True)
1747            ])
1748            self.assertEqual(parse_calls, [])
1749
1750            load_calls = []
1751            parse_calls = []
1752            g = {}
1753            objc.initFrameworkWrapper("TestFramework", "/Library/Framework/Test.framework", None, g, scan_classes=False)
1754            basic_verify(g)
1755            self.assertEqual(len(g), 2)
1756            self.assertEqual(load_calls, [
1757                (Bundle([('TestFramework', 'bridgesupport', 'BridgeSupport')]), 'TestFramework', g, '/Library/Framework/Test.framework', SENTINEL, False)
1758            ])
1759            self.assertEqual(parse_calls, [])
1760
1761            load_calls = []
1762            parse_calls = []
1763            g = {}
1764            objc.initFrameworkWrapper("TestFramework", None, "com.apple.Test", g)
1765            basic_verify(g)
1766            self.assertEqual(len(g), 2)
1767            self.assertEqual(load_calls, [
1768                (Bundle([('TestFramework', 'bridgesupport', 'BridgeSupport')]), 'TestFramework', g, SENTINEL, 'com.apple.Test', True)
1769            ])
1770            self.assertEqual(parse_calls, [])
1771
1772            load_calls = []
1773            parse_calls = []
1774            g = {}
1775            objc.initFrameworkWrapper("TestFramework", None, "com.apple.Test", g, scan_classes=False)
1776            basic_verify(g)
1777            self.assertEqual(len(g), 2)
1778            self.assertEqual(load_calls, [
1779                (Bundle([('TestFramework', 'bridgesupport', 'BridgeSupport')]), 'TestFramework', g, SENTINEL, 'com.apple.Test', False)
1780            ])
1781            self.assertEqual(parse_calls, [])
1782
1783
1784            load_calls = []
1785            parse_calls = []
1786            g = {}
1787            objc.initFrameworkWrapper("TestFramework", "/Library/Framework/Test.framework", "com.apple.Test", g, inlineTab=InlineTab())
1788            basic_verify(g)
1789            self.assertEqual(len(g), 2)
1790            self.assertEqual(load_calls, [
1791                (Bundle([('TestFramework', 'bridgesupport', 'BridgeSupport')]), 'TestFramework', g, SENTINEL, 'com.apple.Test', True)
1792            ])
1793            self.assertEqual(parse_calls, [])
1794
1795            load_calls = []
1796            parse_calls = []
1797            g = {}
1798            objc.initFrameworkWrapper("TestFramework", "/Library/Framework/Test.framework", "com.apple.Test", g,
1799                    inlineTab=InlineTab(), scan_classes=False)
1800            basic_verify(g)
1801            self.assertEqual(len(g), 2)
1802            self.assertEqual(load_calls, [
1803                (Bundle([('TestFramework', 'bridgesupport', 'BridgeSupport')]), 'TestFramework', g, SENTINEL, 'com.apple.Test', False)
1804            ])
1805            self.assertEqual(parse_calls, [])
1806
1807
1808            # 2. Have resource files, bundle files and library files (only first is used)
1809            resources = {
1810                    ('Test', 'PyObjC.bridgesupport'): b"<signatures><constant name='test resource' type='@' /></signatures>",
1811            }
1812            bundle_resources = {
1813                    'Test': os.path.join(helper_dir, 'bundle_data', 'Test.bridgesupport'),
1814            }
1815            TEST_BRIDGESUPPORT_DIRECTORIES[:] = [ os.path.join(helper_dir, 'with_data') ]
1816
1817            load_calls = []
1818            parse_calls = []
1819            g = {}
1820            inlineTab = InlineTab()
1821            objc.initFrameworkWrapper("Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1822                    inlineTab=inlineTab, scan_classes=False)
1823            basic_verify(g)
1824            self.assertEqual(len(g), 2)
1825            self.assertEqual(load_calls, [
1826                (Bundle([]), 'Test', g, SENTINEL, 'com.apple.Test', False)
1827            ])
1828            self.assertEqual(parse_calls, [
1829                (b"<signatures><constant name='test resource' type='@' /></signatures>", g, 'Test', None, inlineTab)
1830            ])
1831
1832            # 3. No resource files, have bundle files and library files (only bundle one is used)
1833            resources = {
1834            }
1835            bundle_resources = {
1836                    ('Test', 'bridgesupport'): os.path.join(helper_dir, 'bundle_data', 'Test.bridgesupport'),
1837            }
1838            TEST_BRIDGESUPPORT_DIRECTORIES[:] = [ os.path.join(helper_dir, 'with_data') ]
1839
1840            load_calls = []
1841            parse_calls = []
1842            g = {}
1843            inlineTab = InlineTab()
1844            objc.initFrameworkWrapper("Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1845                    inlineTab=inlineTab, scan_classes=False)
1846            basic_verify(g)
1847            self.assertEqual(len(g), 2)
1848            self.assertEqual(load_calls, [
1849                (Bundle([('Test', 'bridgesupport', 'BridgeSupport'), ('Test', 'dylib', 'BridgeSupport')]),
1850                    'Test', g, SENTINEL, 'com.apple.Test', False)
1851            ])
1852            self.assertEqual(parse_calls, [
1853                (b"<signatures><constant name='bundle.test' type='@'/></signatures>\n", g, 'Test', None, None)
1854            ])
1855
1856            # 4. No resource files, have bundle files (with override) and library files (only bundle one is used)
1857            resources = {
1858                    ('Test', 'PyObjCOverrides.bridgesupport'): b"<signatures><constant name='test override' type='@' /></signatures>",
1859            }
1860            bundle_resources = {
1861                    ('Test', 'bridgesupport'): os.path.join(helper_dir, 'bundle_data', 'Test.bridgesupport'),
1862            }
1863            TEST_BRIDGESUPPORT_DIRECTORIES[:] = [ os.path.join(helper_dir, 'with_data') ]
1864
1865            load_calls = []
1866            parse_calls = []
1867            g = {}
1868            inlineTab = InlineTab()
1869            objc.initFrameworkWrapper("Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1870                    inlineTab=inlineTab, scan_classes=False)
1871            basic_verify(g)
1872            self.assertEqual(len(g), 2)
1873            self.assertEqual(load_calls, [
1874                (Bundle([('Test', 'bridgesupport', 'BridgeSupport'), ('Test', 'dylib', 'BridgeSupport')]), 'Test', g, SENTINEL, 'com.apple.Test', False)
1875            ])
1876            self.assertEqual(parse_calls, [
1877                (b"<signatures><constant name='bundle.test' type='@'/></signatures>\n", g, 'Test', None, None),
1878                (b"<signatures><constant name='test override' type='@' /></signatures>", g, 'Test', None, inlineTab),
1879            ])
1880
1881            resources = {
1882                    ('Test', 'PyObjCOverrides.bridgesupport'): b"<signatures><constant name='test override' type='@' /></signatures>",
1883            }
1884            bundle_resources = {
1885                    ('Test', 'bridgesupport'): os.path.join(helper_dir, 'bundle_data', 'Test.bridgesupport'),
1886                    ('Test', 'dylib'): os.path.join(helper_dir, 'bundle_data', 'Test.dylib'),
1887            }
1888            TEST_BRIDGESUPPORT_DIRECTORIES[:] = [ os.path.join(helper_dir, 'with_data') ]
1889
1890            load_calls = []
1891            parse_calls = []
1892            g = {}
1893            inlineTab = InlineTab()
1894            objc.initFrameworkWrapper("Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1895                    inlineTab=inlineTab, scan_classes=False)
1896            basic_verify(g)
1897            self.assertEqual(len(g), 2)
1898            self.assertEqual(load_calls, [
1899                (Bundle([('Test', 'bridgesupport', 'BridgeSupport'), ('Test', 'dylib', 'BridgeSupport')]), 'Test', g, SENTINEL, 'com.apple.Test', False)
1900            ])
1901            self.assertEqual(parse_calls, [
1902                (b"<signatures><constant name='bundle.test' type='@'/></signatures>\n", g, 'Test', os.path.join(helper_dir, 'bundle_data', 'Test.dylib'), None),
1903                (b"<signatures><constant name='test override' type='@' /></signatures>", g, 'Test', None, inlineTab),
1904            ])
1905
1906            # 5. No resource file, no bundle file, have library file
1907            resources = {}
1908            bundle_resources = {}
1909            TEST_BRIDGESUPPORT_DIRECTORIES[:] = [ os.path.join(helper_dir, 'with_data') ]
1910
1911            load_calls = []
1912            parse_calls = []
1913            g = {}
1914            inlineTab = InlineTab()
1915            objc.initFrameworkWrapper("Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1916                    inlineTab=inlineTab, scan_classes=False)
1917            basic_verify(g)
1918            self.assertEqual(len(g), 2)
1919            self.assertEqual(load_calls, [
1920                (Bundle([('Test', 'bridgesupport', 'BridgeSupport')]), 'Test', g, SENTINEL, 'com.apple.Test', False)
1921            ])
1922            self.assertEqual(parse_calls, [
1923                (b'<signatures version=\'1\'><string_constant name="info" value="system test.bridgesupport" /></signatures>\n', g, 'Test', None, None)
1924            ])
1925
1926            resources = {}
1927            bundle_resources = {}
1928            TEST_BRIDGESUPPORT_DIRECTORIES[:] = [ os.path.join(helper_dir, 'with_data_dylib') ]
1929
1930            load_calls = []
1931            parse_calls = []
1932            g = {}
1933            inlineTab = InlineTab()
1934            objc.initFrameworkWrapper("Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1935                    inlineTab=inlineTab, scan_classes=False)
1936            basic_verify(g)
1937            self.assertEqual(len(g), 2)
1938            self.assertEqual(load_calls, [
1939                (Bundle([('Test', 'bridgesupport', 'BridgeSupport')]), 'Test', g, SENTINEL, 'com.apple.Test', False)
1940            ])
1941            self.assertEqual(parse_calls, [
1942                (b'<signatures version=\'1\'><string_constant name="info" value="system test.bridgesupport 2" /></signatures>\n', g, 'Test', os.path.join(helper_dir, 'with_data_dylib', 'Test.dylib'), None)
1943            ])
1944
1945            # 6. No resource file, no bundle file, have library file (with override)
1946            resources = {
1947                    ('Test', 'PyObjCOverrides.bridgesupport'): b'<signatures><contant name="override" type="@"></signatures>',
1948            }
1949            bundle_resources = {}
1950            TEST_BRIDGESUPPORT_DIRECTORIES[:] = [ os.path.join(helper_dir, 'with_data') ]
1951
1952            load_calls = []
1953            parse_calls = []
1954            g = {}
1955            inlineTab = InlineTab()
1956            objc.initFrameworkWrapper("Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1957                    inlineTab=inlineTab, scan_classes=False)
1958            basic_verify(g)
1959            self.assertEqual(len(g), 2)
1960            self.assertEqual(load_calls, [
1961                (Bundle([('Test', 'bridgesupport', 'BridgeSupport')]), 'Test', g, SENTINEL, 'com.apple.Test', False)
1962            ])
1963            self.assertEqual(parse_calls, [
1964                (b'<signatures version=\'1\'><string_constant name="info" value="system test.bridgesupport" /></signatures>\n', g, 'Test', None, None),
1965                (b'<signatures><contant name="override" type="@"></signatures>', g, 'Test', None, inlineTab)
1966            ])
1967
1968            # 7. Cannot load bundle (should not look for bridgesupport)
1969            load_calls = []
1970            parse_calls = []
1971            g = {}
1972            inlineTab = InlineTab()
1973            def bundle_exception(name, path, identifier):
1974                raise ImportError(name)
1975
1976            self.assertRaises(ImportError, objc.initFrameworkWrapper,
1977                    "Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1978                    inlineTab=inlineTab, scan_classes=False)
1979
1980            self.assertEquals(load_calls, [])
1981            self.assertEquals(parse_calls, [])
1982            self.assertEquals(g, {})
1983
1984            # 8. framework_identifier is not None, cannot find through identifier
1985            resources = {}
1986            bundle_resources = {}
1987            TEST_BRIDGESUPPORT_DIRECTORIES[:] = []
1988
1989            load_calls = []
1990            parse_calls = []
1991            g = {}
1992            inlineTab = InlineTab()
1993            def bundle_exception(name, path, identifier):
1994                if identifier is not SENTINEL:
1995                    raise ImportError(name)
1996
1997            objc.initFrameworkWrapper(
1998                    "Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
1999                    inlineTab=inlineTab, scan_classes=False)
2000
2001            self.assertEquals(load_calls, [
2002                (Bundle(calls=[('Test', 'bridgesupport', 'BridgeSupport')]), 'Test', g, '/Library/Framework/Test.framework', SENTINEL, False),
2003            ])
2004            self.assertEquals(parse_calls, [])
2005
2006            load_calls = []
2007            parse_calls = []
2008            g = {}
2009            inlineTab = InlineTab()
2010            def bundle_exception(name, path, identifier):
2011                if identifier is not SENTINEL:
2012                    raise ImportError(name)
2013
2014            objc.initFrameworkWrapper(
2015                    "Test", "/Library/Framework/Test.framework", "com.apple.Test", g,
2016                    inlineTab=inlineTab)
2017
2018            self.assertEquals(load_calls, [
2019                (Bundle(calls=[('Test', 'bridgesupport', 'BridgeSupport')]), 'Test', g, '/Library/Framework/Test.framework', SENTINEL, True),
2020            ])
2021            self.assertEquals(parse_calls, [])
2022
2023
2024            # XXX: The following path's aren't properly tested at the moment:
2025            # 8. Use the 'frameworkResourceName' parameter
2026
2027
2028    def test_safe_resource_exists(self):
2029        with Patcher() as p:
2030            return_value = False
2031            exception  = None
2032
2033            def resource_exists(resource, path):
2034                if exception: raise exception
2035                return return_value
2036            p.patch("pkg_resources.resource_exists", resource_exists)
2037
2038            return_value = False
2039            exception  = None
2040            self.assertEquals(bridgesupport.safe_resource_exists("a", "b"), False)
2041
2042            return_value = True
2043            exception  = None
2044            self.assertEquals(bridgesupport.safe_resource_exists("a", "b"), True)
2045
2046            return_value = True
2047            exception  = ImportError
2048            self.assertEquals(bridgesupport.safe_resource_exists("a", "b"), False)
2049
2050
2051    def test_real_loader(self):
2052        script = os.path.join(os.path.dirname(__file__), 'helper_bridgesupport.py')
2053        path_elem = os.path.dirname(objc.__file__)
2054
2055        if sys.byteorder == 'big':
2056            if sys.maxsize < 2**32:
2057                arch = '-ppc'
2058            else:
2059                arch = '-ppc64'
2060
2061        else:
2062            if sys.maxsize < 2**32:
2063                arch = '-i386'
2064            else:
2065                arch = '-x86_64'
2066
2067        return # XXX
2068        p = subprocess.Popen([
2069            '/usr/bin/arch', arch,
2070            sys.executable, script, path_elem], stdout=subprocess.PIPE)
2071        stdout, _ = p.communicate()
2072        if p.returncode != 0:
2073            self.fail("Selftest failed: %r" % stdout)
2074
2075class TestMisc (TestCase):
2076    def test_struct_alias(self):
2077        tp1 = objc.createStructType('TestStruct1', b'{TestStruct1="f1"d"f2"d}', None)
2078
2079        with filterWarnings("error", DeprecationWarning):
2080            self.assertRaises(DeprecationWarning, objc.registerStructAlias, b'{TestStruct2=dd}', tp1)
2081
2082        with filterWarnings("ignore", DeprecationWarning):
2083            tp2 = objc.registerStructAlias(b'{TestStruct2=dd}', tp1)
2084            self.assertIs(tp1, tp2)
2085
2086        self.assertHasAttr(objc.ivar, 'TestStruct1')
2087        self.assertNotHasAttr(objc.ivar, 'TestStruct2')
2088
2089        tp3 = objc.createStructAlias('TestStruct3', b'{TestStruct3=dd}', tp1)
2090        self.assertIs(tp1, tp3)
2091        self.assertHasAttr(objc.ivar, 'TestStruct3')
2092
2093
2094if __name__ == "__main__":
2095    main()
2096