1"""
2These tests check if method prototypes match method signatures.
3
4TODO: a separate test file that tests calls with optional arguments in
5method defintions:
6        class MyClass (NSObject):
7            def myMethod_(self, arg=1):
8                pass
9        o = MyClass.alloc().init()
10        o.myMethod_(2)
11        o.myMethod_() # should also work, arg == 1
12"""
13
14from PyObjCTools.TestSupport import *
15from PyObjCTest.fnd import NSObject
16
17class TestInheritedProtoype (TestCase):
18    #
19    # These tests check for methods that are inherited from a superclass and
20    # therefore have an explict method signature. The number of arguments in
21    # the actual method implementation must match that signature.
22    #
23    def setUp(self):
24        import warnings
25        warnings.filterwarnings('error', category=DeprecationWarning)
26
27    def tearDown(self):
28        import warnings
29        del warnings.filters[0]
30
31
32    def testCorrectArgCount(self):
33        # OK: same number of arguments
34
35        class OC_InPro_CorrectArgCount1 (NSObject):
36            def init(self):
37                pass
38
39        class OC_InPro_CorrectArgCount2 (NSObject):
40            def replacementObjectForArchiver_(self, archiver):
41                pass
42
43        class OC_InPro_CorrectArgCount3 (NSObject):
44            def replacementObjectForArchiver_(self, archiver=None):
45                pass
46
47    def testTooFewArguments(self):
48        # BAD: too few arguments, should raise error
49
50        try:
51            class OC_InPro_TooFew1 (NSObject):
52                def init():
53                    pass
54
55            self.fail()
56        except objc.BadPrototypeError:
57            pass
58
59        try:
60            class OC_InPro_TooFew2 (NSObject):
61                def replacementObjectForArchiver_(self):
62                    pass
63
64            self.fail()
65        except objc.BadPrototypeError:
66            pass
67
68        try:
69            class OC_InPro_TooFew3 (NSObject):
70                def replacementObjectForArchiver_():
71                    pass
72
73            self.fail()
74        except objc.BadPrototypeError:
75            pass
76
77    def testTooManyArguments(self):
78        # BAD: too many arguments, should raise error
79
80        try:
81            class OC_InPro_TooMany1 (NSObject):
82                def init(self, arg):
83                    pass
84
85            self.fail()
86        except objc.BadPrototypeError:
87            pass
88
89        try:
90            class OC_InPro_TooMany2 (NSObject):
91                def init(self, arg, arg2):
92                    pass
93
94            self.fail()
95        except objc.BadPrototypeError:
96            pass
97
98        try:
99            class OC_InPro_TooMany3 (NSObject):
100                def replacementObjectForArchiver_(self, archiver, extra):
101                    pass
102
103            self.fail()
104        except objc.BadPrototypeError:
105            pass
106
107        try:
108            class OC_InPro_TooMany4 (NSObject):
109                def replacementObjectForArchiver_(self, archiver, opt=3):
110                    pass
111
112            self.fail()
113        except objc.BadPrototypeError:
114            pass
115
116    def testBadOverriddenSignature(self):
117        # BAD: signature doesn't match super class signature
118        try:
119            class OC_InPro_BadSig1 (NSObject):
120                def init(self):
121                    pass
122                init = objc.selector(init, signature='v@:')
123
124            self.fail()
125
126        except objc.BadPrototypeError:
127            pass
128
129        try:
130            class OC_InPro_BadSig2 (NSObject):
131                def init(self, arg1, arg2):
132                    pass
133                init = objc.selector(init, signature='v@:@@')
134
135            self.fail()
136
137        except objc.BadPrototypeError:
138            pass
139
140
141        # This one is fine, just to ensure PyObjC isn't too
142        # strict.
143        class OC_InPro_BadSig3 (NSObject):
144            def init(self):
145                pass
146            init = objc.selector(init, signature='@@:')
147
148
149    def testAllArgsOptional(self):
150        # Dodgy, all arguments are optional using '*args, **kwds'
151        #
152        # This should be accepted because simple decorators will use
153        # a method signature like this and we don't want errors or warnings
154        # for that.
155        #
156        # NOTE: see also the 'decorator' library, that allows you to
157        # use decorators without ending up with an ugly signature.
158        class OC_InPro_AllOpt1 (NSObject):
159            def init(*args, **kwds):
160                pass
161
162        class OC_InPro_AllOpt2 (NSObject):
163            def replacementObjectForArchiver_(*args, **kwds):
164                pass
165
166
167        # Also allow versions with an explicit self argument, those
168        # are commonly used as well.
169        class OC_InPro_AllOpt3 (NSObject):
170            def init(self, *args, **kwds):
171                pass
172
173        class OC_InPro_AllOpt4 (NSObject):
174            def replacementObjectForArchiver_(self, *args, **kwds):
175                pass
176
177    def testOptionalArgs(self):
178        # BAD: optional arguments, which don't exist in Objective-C
179        try:
180            class OC_InPro_OptArgs1 (NSObject):
181                def replacementObjectForArchiver_(self, *args):
182                    pass
183
184            self.fail()
185        except objc.BadPrototypeError:
186            pass
187
188        try:
189            class OC_InPro_OptArgs2 (NSObject):
190                def replacementObjectForArchiver_(self, **kwds):
191                    pass
192
193            self.fail()
194        except objc.BadPrototypeError:
195            pass
196
197class TestExplicitPrototype (TestCase):
198    #
199    # These tests check for methods with an explict method signature in the
200    # python code (not inheritted). The python code should match the provided
201    # signature.
202    def setUp(self):
203        import warnings
204        warnings.filterwarnings('error', category=DeprecationWarning)
205
206    def tearDown(self):
207        import warnings
208        del warnings.filters[0]
209
210    def testCorrectArgCount(self):
211        # OK: same number of arguments
212
213        class OC_ExplProto_CorrectArgCount1 (NSObject):
214
215            def noargsmethod(self):
216                pass
217            noargsmethod = objc.selector(noargsmethod, signature='v@:')
218
219        class OC_ExplProto_CorrectArgCount2 (NSObject):
220            def oneargmethod_(self, archiver):
221                pass
222            oneargmethod_ = objc.selector(oneargmethod_, signature='v@:i')
223
224        class OC_ExplProto_CorrectArgCount3 (NSObject):
225            def oneargmethod2_(self, archiver=None):
226                pass
227            oneargmethod2_ = objc.selector(oneargmethod2_, signature='v@:i')
228
229    def testSignatureDoesNotMatchColons(self):
230        # OK: the signature specifies more arguments than the implicit or
231        # explicit selector seems to need.
232
233        class OC_ExplProto_ColonVsCount1 (NSObject):
234            def twoargmethod(self, arg1, arg2):
235                pass
236            twoargmethod = objc.selector(twoargmethod)
237
238        class OC_ExplProto_ColonVsCount2 (NSObject):
239            def twoargmethod(self, arg1, arg2):
240                pass
241            twoargmethod = objc.selector(twoargmethod, signature='v@:@@')
242
243        class OC_ExplProto_ColonVsCount3 (NSObject):
244            def twoargmethod(self, arg1, arg2):
245                pass
246            twoargmethod = objc.selector(twoargmethod, selector='twoargs')
247
248        #class OC_ExplProto_ColonVsCount4 (NSObject):
249        #    def noargmethod_(self):
250        #        pass
251        #    noargmethod_ = objc.selector(noargmethod_)
252
253        #class OC_ExplProto_ColonVsCount5 (NSObject):
254        #    def noargmethod_(self):
255        #        pass
256        #    noargmethod_ = objc.selector(noargmethod_, signature='v@:')
257
258        #class OC_ExplProto_ColonVsCount6 (NSObject):
259        #    def noargmethod_(self):
260        #        pass
261        #    noargmethod_ = objc.selector(noargmethod_, selector='doit:')
262
263
264    def testTooFewArguments(self):
265        # BAD: too few arguments, should raise error
266
267        try:
268            class OC_ExplProto_TooFew1 (NSObject):
269
270                def oneargmethod3_(self):
271                    pass
272                oneargmethod3_ = objc.selector(oneargmethod3_, signature='i@:f')
273
274            self.fail()
275        except objc.BadPrototypeError:
276            pass
277
278    def testTooManyArguments(self):
279        # BAD: too many arguments, should raise error
280
281        try:
282            class OC_ExplProto_TooMany1 (NSObject):
283                def oneargmethod4_(self, a, b):
284                    pass
285                oneargmethod4_ = objc.selector(oneargmethod4_, signature='i@:f')
286
287            self.fail()
288        except objc.BadPrototypeError:
289            pass
290
291    def testAllArgsOptional(self):
292        # Dodgy, all arguments are optional using '*args, **kwds'
293        #
294        # This should be accepted because simple decorators will use
295        # a method signature like this and we don't want errors or warnings
296        # for that.
297        #
298        # NOTE: see also the 'decorator' library, that allows you to
299        # use decorators without ending up with an ugly signature.
300        class OC_ExplProto_AllOpt1 (NSObject):
301            def oneargmethod_(*args, **kwds):
302                pass
303            oneargmethod_ = objc.selector(oneargmethod_, signature='i@:i')
304
305    def testOptionalArgs(self):
306        # BAD: optional arguments, which don't exist in Objective-C
307        try:
308            class OC_ExplProto_OptArgs1 (NSObject):
309                def oneargmethod_(self, *args):
310                    pass
311                oneargmethod_ = objc.selector(oneargmethod_, signature='i@:i')
312
313            self.fail()
314        except objc.BadPrototypeError:
315            pass
316
317        try:
318            class OC_ExplProto_OptArgs2 (NSObject):
319                def oneargmethod_(self, **kwds):
320                    pass
321                oneargmethod_ = objc.selector(oneargmethod_, signature='i@:i')
322
323            self.fail()
324        except objc.BadPrototypeError:
325            pass
326
327    def testOutputArgumentsPresent(self):
328        # OK: Output arguments, all arguments are present
329        class OC_ExplProto_OutputPresent1 (NSObject):
330
331            def oneoutput_(self, output):
332                pass
333            oneoutput_ = objc.selector(oneoutput_, signature='i@:^@')
334
335        class OC_ExplProto_OutputPresent2 (NSObject):
336
337            def oneinput_output_(self, input, output):
338                pass
339            oneinput_output_ = objc.selector(oneinput_output_,
340                    signature='i@:f^@')
341
342    def testOutputArgumentsAbsent(self):
343        # BAD: Output arguments, output not in prototype
344        #
345        # NOTE: this was a warning in PyObjC 2.0 and the only
346        # valid way to work in PyObjC 1.x.
347        try:
348            class OC_ExplProto_OutputAbsent1 (NSObject):
349
350                def oneoutput_(self):
351                    pass
352                oneoutput_ = objc.selector(oneoutput_, signature='i@:^@')
353
354            self.fail()
355
356        except objc.BadPrototypeError:
357            pass
358
359        try:
360            class OC_ExplProto_OutputAbsent2 (NSObject):
361
362                def oneinput_output_(self, input):
363                    pass
364                oneinput_output_ = objc.selector(oneinput_output_,
365                        signature='i@:i^@')
366
367            self.fail()
368
369        except objc.BadPrototypeError:
370            pass
371
372class TestImplicitSignature (TestCase):
373    #
374    # These tests check for methods that aren't inheritted and don't have
375    # an explicit prototype either
376    #
377
378    def setUp(self):
379        import warnings
380        warnings.filterwarnings('error', category=DeprecationWarning)
381
382    def tearDown(self):
383        import warnings
384        del warnings.filters[0]
385
386    def testColonMatch(self):
387        # OK: the number of underscores matches the number of arguments
388        class OC_ImplProto_ColonMatch1 (NSObject):
389            def simplemethod(self):
390                pass
391        self.assertEquals(OC_ImplProto_ColonMatch1.simplemethod.selector, 'simplemethod')
392        self.assertEquals(OC_ImplProto_ColonMatch1.simplemethod.signature, 'v@:')
393
394        class OC_ImplProto_ColonMatch2 (NSObject):
395            def simplemethod_arg2_(self, a, b):
396                return 1
397        self.assertEquals(OC_ImplProto_ColonMatch2.simplemethod_arg2_.selector, 'simplemethod:arg2:')
398        self.assertEquals(OC_ImplProto_ColonMatch2.simplemethod_arg2_.signature, '@@:@@')
399
400    def testTooFewColons(self):
401        # OK: the number of implied colons is smaller than the actual number of
402        # arguments.
403        #
404        # This is fine because you want to use the regular python naming
405        # conventions for methods that won't be called from Objective-C,
406        # that keeps Python code as nice as possible.
407        class OC_ImplProto_TooFew1 (NSObject):
408            def myMethod(self, arg1, arg2=4):
409                pass
410        self.assertEquals(OC_ImplProto_TooFew1.myMethod.selector, 'myMethod')
411        self.assertEquals(OC_ImplProto_TooFew1.myMethod.signature, 'v@:@@')
412
413    def testTooManyColons(self):
414        # OK: the number of implied colons is larger than the actual number
415        # of arguments
416        #
417        # (see 'testTooFewColons', same argument but other coding style)
418        class OC_ImplProto_TooMany2 (NSObject):
419            def run_to_completion(self):
420                pass
421        self.assertEquals(OC_ImplProto_TooMany2.run_to_completion.selector, 'run_to_completion')
422        self.assertEquals(OC_ImplProto_TooMany2.run_to_completion.signature, 'v@:')
423
424    def testImpliedColonTooFew(self):
425        # BAD: a method that is obviously intented to be an objective-C method, but
426        # has too few arguments.
427        try:
428            class OC_ImplProto_TooFew2 (NSObject):
429                def setFoo_(self):
430                    pass
431            self.fail()
432
433        except objc.BadPrototypeError:
434            pass
435
436        # OK: leading underscore won't be converted to a colon
437        class OC_ImplProto_TooFew3 (NSObject):
438            def _setFoo_(self, value):
439                pass
440
441    def testImpliedColonTooMany(self):
442        # BAD: a method that is obviously intended to be an objective-C method,
443        # but has too many arguments.
444
445        try:
446            class OC_ImplProto_TooMany1 (NSObject):
447                def setFoo_(self, value, other):
448                    pass
449            self.fail()
450        except objc.BadPrototypeError:
451            pass
452
453        try:
454            class OC_ImplProto_TooMany2 (NSObject):
455                def setFoo_(self, value, other=3):
456                    pass
457            self.fail()
458        except objc.BadPrototypeError:
459            pass
460
461        try:
462            class OC_ImplProto_TooMany3 (NSObject):
463                def setFoo_(self, value, *rest):
464                    pass
465            self.fail()
466        except objc.BadPrototypeError:
467            pass
468
469        try:
470            class OC_ImplProto_TooMany4 (NSObject):
471                def setFoo_(self, value, **rest):
472                    pass
473            self.fail()
474        except objc.BadPrototypeError:
475            pass
476
477
478    def testMethodVariations(self):
479        # OK: all methods with an implied signature are fine
480        #
481        # That is, as long as the implied selector doesn't contain
482        # colons. If the implied selector does contain colons the
483        # method must have the right number of parameters, that
484        # should help us to avoid obvious errors.
485        class OC_ImplProto_Variations (NSObject):
486            def method1(self): pass
487            def method2(self): return 1
488            def method1_(self, arg): pass
489            def methodWithX_andY_(self, x, y): pass
490            def method_with_embedded_underscores(self, a): pass
491            def __magic__(self): pass
492            def _leadingColon(self): pass
493            def _leadingColon_(self, arg): return 1
494            def methodWithArg_(self, arg): pass
495
496        # Check method signatures
497        self.assertEquals(OC_ImplProto_Variations.method1.selector, "method1")
498        self.assertEquals(OC_ImplProto_Variations.method2.selector, "method2")
499        self.assertEquals(OC_ImplProto_Variations.method1_.selector, "method1:")
500        self.assertEquals(OC_ImplProto_Variations.methodWithX_andY_.selector, "methodWithX:andY:")
501        self.assertEquals(OC_ImplProto_Variations.method_with_embedded_underscores.selector, "method_with_embedded_underscores")
502        #self.assertEquals(OC_ImplProto_Variations.__magic__.selector, "__magic__")
503        self.assertEquals(OC_ImplProto_Variations._leadingColon.selector, "_leadingColon")
504        self.assertEquals(OC_ImplProto_Variations._leadingColon_.selector, "_leadingColon:")
505        self.assertEquals(OC_ImplProto_Variations.methodWithArg_.selector, "methodWithArg:")
506
507        # And the implied type signature
508        self.assertEquals(OC_ImplProto_Variations.method1.signature, "v@:")
509        self.assertEquals(OC_ImplProto_Variations.method2.signature, "@@:")
510        self.assertEquals(OC_ImplProto_Variations.method1_.signature, "v@:@")
511        self.assertEquals(OC_ImplProto_Variations.methodWithX_andY_.signature, "v@:@@")
512        self.assertEquals(OC_ImplProto_Variations.method_with_embedded_underscores.signature, "v@:@")
513        #self.assertEquals(OC_ImplProto_Variations.__magic__.signature, "v@:")
514        self.assertEquals(OC_ImplProto_Variations._leadingColon.signature, "v@:")
515        self.assertEquals(OC_ImplProto_Variations._leadingColon_.signature, "@@:@")
516        self.assertEquals(OC_ImplProto_Variations.methodWithArg_.signature, "v@:@")
517
518if __name__ == "__main__":
519    main()
520