1from PyObjCTools.TestSupport import *
2import objc
3from PyObjCTest.testbndl import PyObjC_TestClass3
4import sys
5import types
6
7# Most useful systems will at least have 'NSObject'.
8NSObject = objc.lookUpClass('NSObject')
9NSArray = objc.lookUpClass('NSArray')
10NSAutoreleasePool = objc.lookUpClass('NSAutoreleasePool')
11
12class TestSubclassing(TestCase):
13    def testMethodRaise(self):
14        # Defining a method whose name is a keyword followed by two underscores
15        # should define the method name without underscores in the runtime,
16        # and this method should be accesible both with and without the
17        # underscores.
18
19        class RaiseClass (NSObject):
20            def raise__(self):
21                pass
22
23        self.assertNotHasAttr(NSObject, 'raise__')
24        self.assertNotHasAttr(NSObject, 'raise')
25
26        self.assertHasAttr(RaiseClass, 'raise__')
27        self.assertHasAttr(RaiseClass, 'raise')
28        self.assertEqual(RaiseClass.raise__.selector, b'raise')
29        self.assertEqual(getattr(RaiseClass, 'raise').selector, b'raise')
30
31    def testMIObjC(self):
32        try:
33            class MIClass1(NSObject, NSArray):
34                pass
35            self.fail("Can multiple inherit from two objc classes")
36        except TypeError:
37            pass
38
39    def testSubclassOfSubclass(self):
40        class Level1Class (NSObject):
41            def hello(self):
42                return "level1"
43
44        class Level2Class (Level1Class):
45            def hello(self):
46                return "level2"
47
48            def superHello(self):
49                return objc.super(Level2Class, self).hello()
50
51            def description(self):
52                return objc.super(Level2Class, self).description()
53
54        obj = Level1Class.alloc().init()
55        v = obj.hello()
56        self.assertEqual(v, "level1")
57
58        obj = Level2Class.alloc().init()
59        v = obj.hello()
60        self.assertEqual(v, "level2")
61
62        v = obj.superHello()
63        self.assertEqual(v, "level1")
64
65        v = obj.description()
66        # this may be a bit hardwired for comfort
67        self.assertEqual(v.find("<Level2Class"), 0)
68
69    def testMethodSignature(self):
70        class Signature (NSObject):
71            def test_x_(self, arg, x):
72                pass
73            test_x_ = objc.selector(test_x_, signature=b'v@:@i')
74
75        v = Signature.new()
76
77        self.assertIsInstance(v, Signature)
78
79        self.assertEqual(v.methodSignatureForSelector_('foo:'), None)
80
81        x = v.methodSignatureForSelector_('test:x:')
82        self.assertIsNotNone(x)
83
84        self.assertEqual(x.methodReturnType(), b'v')
85        self.assertEqual(x.numberOfArguments(), 4)
86        self.assertEqual(x.getArgumentTypeAtIndex_(0), b'@')
87        self.assertEqual(x.getArgumentTypeAtIndex_(1), b':')
88        self.assertEqual(x.getArgumentTypeAtIndex_(2), b'@')
89        self.assertEqual(x.getArgumentTypeAtIndex_(3), b'i')
90
91
92class TestSelectors(TestCase):
93    def testSelectorRepr(self):
94        class SelectorRepr(NSObject):
95            def foo(self):
96                pass
97
98        self.assertTrue(repr(SelectorRepr.foo).startswith('<unbound selector foo of SelectorRepr at'))
99
100
101class TestCopying (TestCase):
102
103    def testCopy(self):
104        class MyCopyClass (NSObject):
105            def copyWithZone_(self, zone):
106                # NSObject doesn't implement the copying protocol
107                #o = super(MyCopyClass, self).copyWithZone_(zone)
108                o = self.__class__.alloc().init()
109                o.foobar = 2
110                return o
111            copyWithZone_ = objc.selector(
112                copyWithZone_,
113                signature=NSObject.copyWithZone_.signature,
114                isClassMethod=0)
115
116
117        # Make sure the runtime correctly marked our copyWithZone_
118        # implementation.
119        o = MyCopyClass.alloc().init()
120
121        self.assertFalse((o.copyWithZone_.__metadata__()['classmethod']))
122        self.assertTrue(o.copyWithZone_.__metadata__()['retval']['already_retained'])
123        #self.assertTrue(o.copyWithZone_.callable == MyCopyClass.__dict__['copyWithZone_'].callable)
124
125        o = MyCopyClass.alloc().init()
126        o.foobar = 1
127
128        self.assertEqual(o.foobar, 1)
129
130        # Make a copy from ObjC (see testbundle.m)
131        c = PyObjC_TestClass3.copyValue_(o)
132
133        self.assertIsInstance(c, MyCopyClass)
134        self.assertEqual(c.foobar, 2)
135
136
137    def testMultipleInheritance1(self):
138        # New-style class mixin
139        class MixinClass1 (object):
140            def mixinMethod(self):
141                return "foo"
142
143        class MITestClass1 (NSObject, MixinClass1):
144            def init(self):
145                return NSObject.pyobjc_instanceMethods.init(self)
146
147        self.assertHasAttr(MITestClass1, 'mixinMethod')
148
149        o = MITestClass1.alloc().init()
150        self.assertEqual(o.mixinMethod(), "foo")
151
152    def testMultipleInheritance2(self):
153        # old-style class mixin
154        class MixinClass2:
155            def mixinMethod(self):
156                return "foo"
157
158        class MITestClass2 (NSObject, MixinClass2):
159            def init(self):
160                return NSObject.pyobjc_instanceMethods.init(self)
161
162        self.assertHasAttr(MITestClass2, 'mixinMethod')
163
164        o = MITestClass2.alloc().init()
165        self.assertEqual(o.mixinMethod(), "foo")
166
167class TestClassMethods (TestCase):
168
169    def testClassMethod(self):
170        """ check that classmethod()-s are converted to selectors """
171
172        class ClassMethodTest (NSObject):
173            def clsMeth(self):
174                return "hello"
175            clsMeth = classmethod(clsMeth)
176
177        self.assertIsInstance(ClassMethodTest.clsMeth, objc.selector)
178        self.assertTrue(ClassMethodTest.clsMeth.isClassMethod)
179
180    def testStaticMethod(self):
181        """ check that staticmethod()-s are not converted to selectors """
182
183        class StaticMethodTest (NSObject):
184            def stMeth(self):
185                return "hello"
186            stMeth = staticmethod(stMeth)
187
188        def func(): pass
189
190        self.assertIsInstance(StaticMethodTest.stMeth, type(func))
191
192
193class TestOverridingSpecials(TestCase):
194    def testOverrideSpecialMethods(self):
195        aList = [0]
196
197        class ClassWithAlloc(NSObject):
198            def alloc(cls):
199                aList[0] += 1
200                return objc.super(ClassWithAlloc, cls).alloc()
201
202
203        self.assertEqual(aList[0], 0)
204        o = ClassWithAlloc.alloc().init()
205        self.assertEqual(aList[0], 1)
206        self.assertIsInstance(o, NSObject)
207        del o
208
209        class ClassWithRetaining(NSObject):
210            def retain(self):
211                aList.append('retain')
212                v =  objc.super(ClassWithRetaining, self).retain()
213                return v
214
215            def release(self):
216                aList.append('release')
217                return objc.super(ClassWithRetaining, self).release()
218
219            def __del__(self):
220                aList.append('__del__')
221
222
223        del aList[:]
224        o = ClassWithRetaining.alloc().init()
225        v = o.retainCount()
226        o.retain()
227        self.assertEqual(aList, ['retain'])
228        self.assertEqual(o.retainCount(), v+1)
229        o.release()
230        self.assertEqual(aList, ['retain', 'release'])
231        self.assertEqual(o.retainCount(), v)
232        del o
233
234        self.assertEqual(aList, ['retain', 'release', 'release', '__del__'])
235
236        # Test again, now remove all python references and create one
237        # again.
238        del aList[:]
239        pool = NSAutoreleasePool.alloc().init()
240        o = ClassWithRetaining.alloc().init()
241        v = NSArray.arrayWithArray_([o])
242        del o
243        self.assertEqual(aList, ['retain'])
244        o = v[0]
245        self.assertEqual(aList, ['retain'])
246        del v
247        del o
248        del pool
249
250        self.assertEqual(aList, ['retain', 'release', 'release', '__del__'])
251
252        class ClassWithRetainCount(NSObject):
253            def retainCount(self):
254                aList.append('retainCount')
255                return objc.super(ClassWithRetainCount, self).retainCount()
256
257        del aList[:]
258        o = ClassWithRetainCount.alloc().init()
259        self.assertEqual(aList, [])
260        v = o.retainCount()
261        self.assertIsInstance(v, int)
262        self.assertEqual(aList, ['retainCount'])
263        del o
264
265    def testOverrideDealloc(self):
266        aList = []
267
268        class Dummy:
269            def __del__(self):
270                aList.append('__del__')
271
272        self.assertEqual(aList, [])
273        Dummy()
274        self.assertEqual(aList, ['__del__'])
275
276        class ClassWithDealloc(NSObject):
277            def init(self):
278                self = objc.super(ClassWithDealloc, self).init()
279                if self is not None:
280                    self.obj = Dummy()
281                return self
282
283            def dealloc(self):
284                aList.append('dealloc')
285                return objc.super(ClassWithDealloc, self).dealloc()
286
287        del aList[:]
288        o = ClassWithDealloc.alloc().init()
289        self.assertEqual(aList, [])
290        del o
291        self.assertEqual(len(aList), 2)
292        self.assertIn('dealloc', aList)
293        self.assertIn('__del__', aList)
294
295        class SubClassWithDealloc(ClassWithDealloc):
296            def dealloc(self):
297                aList.append('dealloc.dealloc')
298                return objc.super(SubClassWithDealloc, self).dealloc()
299
300        del aList[:]
301        o = SubClassWithDealloc.alloc().init()
302        self.assertEqual(aList, [])
303        del o
304        self.assertEqual(len(aList), 3)
305        self.assertIn('dealloc.dealloc', aList)
306        self.assertIn('dealloc', aList)
307        self.assertIn('__del__', aList)
308
309        class ClassWithDeallocAndDel(NSObject):
310            def init(self):
311                self = objc.super(ClassWithDeallocAndDel, self).init()
312                if self is not None:
313                    self.obj = Dummy()
314                return self
315
316            def dealloc(self):
317                aList.append('dealloc')
318                return objc.super(ClassWithDeallocAndDel, self).dealloc()
319
320            def __del__(self):
321                aList.append('mydel')
322
323        del aList[:]
324        o = ClassWithDeallocAndDel.alloc().init()
325        self.assertEqual(aList, [])
326        del o
327        self.assertEqual(len(aList), 3)
328        self.assertIn('mydel', aList)
329        self.assertIn('dealloc', aList)
330        self.assertIn('__del__', aList)
331
332    def testMethodNames(self):
333
334        class MethodNamesClass (NSObject):
335            def someName_andArg_(self, name, arg):
336                pass
337
338            def _someName_andArg_(self, name, arg):
339                pass
340
341
342            def raise__(self):
343                pass
344
345            def froobnicate__(self, a, b):
346                pass
347
348        # XXX: workaround for a 'feature' in class-builder.m, that code
349        # ignores methods whose name starts with two underscores. That code
350        # is not necessary, or the other ways of adding methods to a class
351        # should be changed.
352        def __foo_bar__(self, a, b, c):
353            pass
354
355        MethodNamesClass.__foo_bar__ = __foo_bar__
356
357        self.assertEqual(MethodNamesClass.someName_andArg_.selector,
358                b'someName:andArg:')
359        self.assertEqual(MethodNamesClass._someName_andArg_.selector,
360                b'_someName:andArg:')
361        self.assertEqual(MethodNamesClass.__foo_bar__.selector,
362                b'__foo_bar__')
363        self.assertEqual(MethodNamesClass.raise__.selector,
364                b'raise')
365        self.assertEqual(MethodNamesClass.froobnicate__.selector,
366                b'froobnicate::')
367
368    def testOverrideRespondsToSelector(self):
369        class OC_RespondsClass (NSObject):
370            def initWithList_(self, lst):
371                objc.super(OC_RespondsClass, self).init()
372                self.lst = lst
373                return self
374
375            def respondsToSelector_(self, selector):
376                self.lst.append(selector)
377                return objc.super(OC_RespondsClass, self).respondsToSelector_(selector)
378
379        lst = []
380        o = OC_RespondsClass.alloc().initWithList_(lst)
381
382        self.assertEqual(lst, [])
383
384        b = o.respondsToSelector_('init')
385        self.assertTrue(b)
386        self.assertEqual(lst, ['init'])
387
388        b = o.respondsToSelector_('alloc')
389        self.assertFalse(b)
390        self.assertEqual(lst, ['init', 'alloc'])
391
392    def testOverrideInstancesRespondToSelector(self):
393        lst = []
394        class OC_InstancesRespondClass (NSObject):
395
396            @classmethod
397            def instancesRespondToSelector_(cls, selector):
398                lst.append(selector)
399                return objc.super(OC_InstancesRespondClass, cls).instancesRespondToSelector_(selector)
400
401        self.assertEqual(lst, [])
402
403        b = OC_InstancesRespondClass.instancesRespondToSelector_('init')
404        self.assertTrue(b)
405        self.assertEqual(lst, ['init'])
406
407        b = OC_InstancesRespondClass.instancesRespondToSelector_('alloc')
408        self.assertFalse(b)
409        self.assertEqual(lst, ['init', 'alloc'])
410
411    def testImplementingSetValueForKey(self):
412        values = {}
413        class CrashTest (NSObject):
414            def setValue_forKey_(self, v, k):
415                values[k] = v
416
417        o = CrashTest.alloc().init()
418        o.setValue_forKey_(42,"key")
419
420        self.assertEqual(values, {"key":42})
421
422
423if __name__ == '__main__':
424    main()
425