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