1# FIXME: This test suite seems to polute it's environment, other tests fail
2# when this test suite is active!
3from __future__ import unicode_literals
4from PyObjCTools.TestSupport import *
5import sys
6
7from PyObjCTools.TestSupport import onlyPython2
8
9import objc
10
11NSObject = objc.lookUpClass('NSObject')
12
13class MEClass(NSObject):
14    pass
15
16preEverythingInstance = MEClass.new()
17
18class Methods(NSObject):
19    def description(self):
20        return "<methods>"
21
22    def newMethod(self):
23        return "<new-method>"
24
25class MethodsSub(NSObject):
26    def description(self):
27        return "<sub-methods>"
28
29    def newMethod(self):
30        return "<sub-new-method>"
31
32    def newSubMethod(self):
33        return "<new-method-sub>"
34
35class PurePython:
36    def description(self):
37        return "<pure>"
38
39    def newMethod(self):
40        return "<pure-new>"
41
42    def purePythonMethod(self):
43        return "<pure-py>"
44
45class TestFromObjCSuperToObjCClass(TestCase):
46    def testClassAddMethod(self):
47        import objc._category as mod
48
49        orig_classAddMethods = mod.classAddMethods
50        try:
51            l = []
52            def classAddMethods(cls, values):
53                self.assertIsInstance(cls, objc.objc_class)
54                for item in values:
55                    self.assertIsInstance(item, objc.selector)
56                l.append((cls, values))
57
58            mod.classAddMethods = classAddMethods
59
60            def my_python_description(self):
61                return "foo the bar"
62
63            objc.classAddMethod(NSObject, b"python_description", my_python_description)
64            self.assertEqual(len(l), 1)
65            self.assertIs(l[0][0], NSObject)
66            self.assertEqual(len(l[0][1]), 1)
67            m = l[0][1][0]
68            self.assertIsInstance(m, objc.selector)
69            self.assertIs(m.callable, my_python_description)
70            self.assertEqual(m.selector, b"python_description")
71            self.assertEqual(m.signature, b"@@:")
72
73            @objc.typedSelector(b"q@:")
74            def myAction(self):
75                return 1
76
77            l[:] = []
78            objc.classAddMethod(NSObject, b"value",  myAction)
79            self.assertIs(l[0][0], NSObject)
80            self.assertEqual(len(l[0][1]), 1)
81            m = l[0][1][0]
82            self.assertIsInstance(m, objc.selector)
83            self.assertIs(m.callable, myAction.callable)
84            self.assertEqual(m.selector, b"value")
85            self.assertEqual(m.signature, b"q@:")
86
87
88            # Cannot add native selectors:
89            self.assertRaises(AttributeError, objc.classAddMethod, NSObject, b"descriptionAlias", NSObject.description)
90
91        finally:
92            mod.classAddMethods = orig_classAddMethods
93
94
95    def testBasicBehavior(self):
96        anInstance = Methods.new()
97        self.assertEqual(anInstance.description(), "<methods>")
98        self.assertEqual(anInstance.newMethod(), "<new-method>")
99
100    def testDescriptionOverride(self):
101        objc.classAddMethods(MEClass, [Methods.pyobjc_instanceMethods.description])
102
103        self.assertTrue(MEClass.instancesRespondToSelector_("description"))
104
105        newInstance = MEClass.new()
106
107        self.assertEqual(newInstance.description(), "<methods>")
108        self.assertEqual(preEverythingInstance.description(), "<methods>")
109
110    def testNewMethod(self):
111        objc.classAddMethods(MEClass, [Methods.pyobjc_instanceMethods.newMethod])
112
113        self.assertTrue(MEClass.instancesRespondToSelector_("newMethod"))
114
115        newInstance = MEClass.new()
116
117        self.assertEqual(newInstance.newMethod(), "<new-method>")
118        self.assertEqual(preEverythingInstance.newMethod(), "<new-method>")
119
120    def testSubDescriptionOverride(self):
121        objc.classAddMethods(MEClass, [MethodsSub.pyobjc_instanceMethods.description])
122
123        self.assertTrue(MEClass.instancesRespondToSelector_("description"))
124
125        newInstance = MEClass.new()
126
127        self.assertEqual(newInstance.description(), "<sub-methods>")
128        self.assertEqual(preEverythingInstance.description(), "<sub-methods>")
129
130    def testSubNewMethod(self):
131        objc.classAddMethods(MEClass, [MethodsSub.newMethod, MethodsSub.newSubMethod])
132
133        self.assertTrue(MEClass.instancesRespondToSelector_("newMethod"))
134        self.assertTrue(MEClass.instancesRespondToSelector_("newSubMethod"))
135
136        newInstance = MEClass.new()
137
138        self.assertEqual(newInstance.newMethod(), "<sub-new-method>")
139        self.assertEqual(preEverythingInstance.newMethod(), "<sub-new-method>")
140        self.assertEqual(newInstance.newSubMethod(), "<new-method-sub>")
141        self.assertEqual(preEverythingInstance.newSubMethod(), "<new-method-sub>")
142
143    def testNewClassMethod(self):
144
145        def aNewClassMethod(cls):
146            return "Foo cls"
147        aNewClassMethod = classmethod(aNewClassMethod)
148
149        self.assertTrue(not MEClass.pyobjc_classMethods.respondsToSelector_("aNewClassMethod"))
150        objc.classAddMethods(MEClass, [aNewClassMethod])
151        self.assertTrue(MEClass.pyobjc_classMethods.respondsToSelector_("aNewClassMethod"))
152
153        self.assertTrue(MEClass.aNewClassMethod.isClassMethod)
154        self.assertEqual(MEClass.aNewClassMethod(), 'Foo cls')
155
156    def testAddedMethodType(self):
157        def anotherNewClassMethod(cls):
158            "CLS DOC STRING"
159            return "BAR CLS"
160        anotherNewClassMethod = classmethod(anotherNewClassMethod)
161
162        def anotherNewMethod(self):
163            "INST DOC STRING"
164            return "BAR SELF"
165
166        self.assertTrue(not MEClass.pyobjc_classMethods.respondsToSelector_("anotherNewClassMethod"))
167        self.assertTrue(not MEClass.pyobjc_classMethods.instancesRespondToSelector_("anotherNewMethod"))
168
169        objc.classAddMethods(MEClass, [anotherNewClassMethod, anotherNewMethod])
170        self.assertTrue(MEClass.pyobjc_classMethods.respondsToSelector_("anotherNewClassMethod"))
171        self.assertTrue(MEClass.pyobjc_classMethods.instancesRespondToSelector_("anotherNewMethod"))
172
173        self.assertEqual(MEClass.anotherNewClassMethod.__doc__, "CLS DOC STRING")
174        self.assertEqual(MEClass.anotherNewMethod.__doc__, "INST DOC STRING")
175
176
177
178
179class TestFromPythonClassToObjCClass(TestCase):
180
181    @onlyPython2
182    def testPythonSourcedMethods(self):
183        # 20031227, Ronald: Assigning the methods works alright, but actually
184        # using them won't because the new methods are actually still methods
185        # of a different class and will therefore complain about the type
186        # of 'self'.
187        objc.classAddMethods(MEClass, [PurePython.description,
188                                                  PurePython.newMethod,
189                                                  PurePython.purePythonMethod])
190
191
192        self.assertTrue(MEClass.instancesRespondToSelector_("description"))
193        self.assertTrue(MEClass.instancesRespondToSelector_("newMethod"))
194        self.assertTrue(MEClass.instancesRespondToSelector_("purePythonMethod"))
195
196        newInstance = MEClass.new()
197
198        # This is bogus, see above:
199        #self.assertEqual(newInstance.description(), "<pure>")
200        #self.assertEqual(newInstance.newMethod(), "<pure-new>")
201        #self.assertEqual(newInstance.purePythonMethod(), "<pure-py>")
202
203        #self.assertEqual(preEverythingInstance.description(), "<pure>")
204        #self.assertEqual(preEverythingInstance.newMethod(), "<pure-new>")
205        #self.assertEqual(preEverythingInstance.purePythonMethod(), "<pure-py>")
206
207        self.assertRaises(TypeError, newInstance.description)
208        self.assertRaises(TypeError, newInstance.newMethod)
209        self.assertRaises(TypeError, newInstance.purePythonMethod)
210        self.assertRaises(TypeError, preEverythingInstance.description)
211        self.assertRaises(TypeError, preEverythingInstance.newMethod)
212        self.assertRaises(TypeError, preEverythingInstance.purePythonMethod)
213
214    def testPythonSourcedFunctions(self):
215        # Same as testPythonSourcedMethods, but using function objects instead
216        # of method objects.
217
218
219        if sys.version_info[0] == 2:
220            objc.classAddMethods(MEClass, [
221                PurePython.description.im_func,
222                PurePython.newMethod.im_func,
223                PurePython.purePythonMethod.im_func,
224            ])
225        else:
226            objc.classAddMethods(MEClass, [
227                PurePython.description,
228                PurePython.newMethod,
229                PurePython.purePythonMethod,
230            ])
231
232        self.assertTrue(MEClass.instancesRespondToSelector_("description"))
233        self.assertTrue(MEClass.instancesRespondToSelector_("newMethod"))
234        self.assertTrue(MEClass.instancesRespondToSelector_("purePythonMethod"))
235
236        newInstance = MEClass.new()
237
238        self.assertEqual(newInstance.description(), "<pure>")
239        self.assertEqual(newInstance.newMethod(), "<pure-new>")
240        self.assertEqual(newInstance.purePythonMethod(), "<pure-py>")
241
242        self.assertEqual(preEverythingInstance.description(), "<pure>")
243        self.assertEqual(preEverythingInstance.newMethod(), "<pure-new>")
244        self.assertEqual(preEverythingInstance.purePythonMethod(), "<pure-py>")
245
246
247
248class TestClassAsignments (TestCase):
249    def testAssignAMethod(self):
250        MEClass.doSomethingElse = lambda self: 2*2
251        MEClass.doDuplicate_ = lambda self, x: 2*x
252
253        self.assertTrue(MEClass.instancesRespondToSelector_("doSomethingElse"))
254        self.assertTrue(MEClass.instancesRespondToSelector_("doDuplicate:"))
255
256        o = MEClass.alloc().init()
257
258        self.assertEqual(4, o.doSomethingElse())
259        self.assertEqual(8, o.doDuplicate_(4))
260
261    def testAssignAClassMethod(self):
262        MEClass.classSomethingElse = classmethod(lambda self: 2*2)
263        MEClass.classDuplicate_ = classmethod(lambda self, x: 2*x)
264
265        self.assertTrue(MEClass.pyobjc_classMethods.respondsToSelector_(b"classSomethingElse"))
266        self.assertTrue(MEClass.pyobjc_classMethods.respondsToSelector_(b"classDuplicate:"))
267
268        self.assertEqual(4, MEClass.classSomethingElse())
269        self.assertEqual(8, MEClass.classDuplicate_(4))
270
271    def testAssignFuzzyMethod(self):
272        self.assertRaises((ValueError, TypeError), setattr, MEClass, 'fuzzyMethod', objc.selector(None, selector=b'fuzzy', signature=b'@@:'))
273
274    def testRemovingMethods(self):
275        theClass = NSObject
276
277        self.assertRaises(AttributeError, delattr, theClass, 'alloc')
278        self.assertRaises(AttributeError, delattr, theClass, 'init')
279
280class TestCategory (TestCase):
281    # Tests of objc.Category
282
283    def testPyClassCategory(self):
284        global Methods
285
286        o = Methods.alloc().init()
287        self.assertRaises(AttributeError, getattr, o, 'categoryMethod')
288
289        class Methods (objc.Category(Methods)):
290            def categoryMethod(self):
291                return True
292
293            def categoryMethod2(self):
294                return False
295
296            def anotherClassMethod(self):
297                return "hello"
298            anotherClassMethod = classmethod(anotherClassMethod)
299
300        self.assertTrue(o.categoryMethod())
301        self.assertTrue(not o.categoryMethod2())
302        self.assertEqual(Methods.anotherClassMethod(), "hello")
303
304    def testNoInstanceVariables(self):
305        global Methods
306
307        try:
308            class Methods(objc.Category(Methods)):
309                outlet = objc.IBOutlet()
310
311        except TypeError:
312            pass
313
314        else:
315            self.fail("Can add instance variable in a category")
316
317    def testObjCClassCategory(self):
318        NSObject = objc.lookUpClass('NSObject')
319
320        o = NSObject.alloc().init()
321        self.assertRaises(AttributeError, getattr, o, 'myCategoryMethod')
322
323        class NSObject (objc.Category(NSObject)):
324            def myCategoryMethod(self):
325                return True
326
327            def myCategoryMethod2(self):
328                return False
329
330        self.assertTrue(o.myCategoryMethod())
331        self.assertTrue(not o.myCategoryMethod2())
332
333    def testCategoryMultipleInheritance(self):
334
335        NSObject = objc.lookUpClass('NSObject')
336
337
338        try:
339
340            class NSObject ( objc.Category(NSObject), object ):
341                pass
342
343            self.fail("Can use objc.Category with MI")
344        except TypeError:
345            pass
346
347    def testCategoryName(self):
348        try:
349            class NSFoo (objc.Category(NSObject)):
350                pass
351
352            self.fail("Category name != class name")
353
354        except TypeError:
355            pass
356
357    def testCategoryOnPurePython(self):
358        try:
359            global list
360
361            class list (objc.Category(list)):
362                pass
363
364            self.fail("Category on list???")
365
366        except TypeError:
367            pass
368
369    def testCategoryRedefiningPythonMethod(self):
370        class BaseClassRedef(NSObject):
371            def foo(self):
372                return 1
373
374        class BaseClassRedef(objc.Category(BaseClassRedef)):
375            def foo(self):
376                return 2
377
378        obj = BaseClassRedef.alloc().init()
379
380        self.assertEqual(obj.foo(), 2)
381
382        def foo(self):
383            return 3
384        BaseClassRedef.foo = foo
385
386        self.assertEqual(obj.foo(), 3)
387
388    def testCategeryWithDocString (self):
389        class NSObjectCat (NSObject):
390            pass
391
392        class NSObjectCat (objc.Category(NSObjectCat)):
393            """
394            This is a docstring
395            """
396
397            def withDocStringMethod(self):
398                return 42
399
400        o = NSObjectCat.alloc().init()
401        self.assertEqual(o.withDocStringMethod(), 42)
402
403    def testCategoryWithClassMethod(self):
404        class NSObjectCat2 (NSObject):
405            pass
406
407        class NSObjectCat2 (objc.Category(NSObjectCat2)):
408            @classmethod
409            def aClassMethod(cls):
410                return 1
411
412        self.assertEqual(NSObjectCat2.aClassMethod(), 1)
413
414    def testCategoryWithVariables(self):
415        class NSObjectCat3 (NSObject):
416            pass
417
418        class NSObjectCat3 (objc.Category(NSObjectCat3)):
419            classValue = "aap"
420
421            def getClassValue(self):
422                return self.classValue
423
424
425        self.assertHasAttr(NSObjectCat3, "classValue")
426        o = NSObjectCat3.alloc().init()
427        self.assertEqual(o.getClassValue(), "aap")
428
429
430
431
432if __name__ == '__main__':
433    main()
434