""" These tests check if method prototypes match method signatures. TODO: a separate test file that tests calls with optional arguments in method defintions: class MyClass (NSObject): def myMethod_(self, arg=1): pass o = MyClass.alloc().init() o.myMethod_(2) o.myMethod_() # should also work, arg == 1 """ from PyObjCTools.TestSupport import * from PyObjCTest.fnd import NSObject class TestInheritedProtoype (TestCase): # # These tests check for methods that are inherited from a superclass and # therefore have an explict method signature. The number of arguments in # the actual method implementation must match that signature. # def setUp(self): import warnings warnings.filterwarnings('error', category=DeprecationWarning) def tearDown(self): import warnings del warnings.filters[0] def testCorrectArgCount(self): # OK: same number of arguments class OC_InPro_CorrectArgCount1 (NSObject): def init(self): pass class OC_InPro_CorrectArgCount2 (NSObject): def replacementObjectForArchiver_(self, archiver): pass class OC_InPro_CorrectArgCount3 (NSObject): def replacementObjectForArchiver_(self, archiver=None): pass def testTooFewArguments(self): # BAD: too few arguments, should raise error try: class OC_InPro_TooFew1 (NSObject): def init(): pass self.fail() except objc.BadPrototypeError: pass try: class OC_InPro_TooFew2 (NSObject): def replacementObjectForArchiver_(self): pass self.fail() except objc.BadPrototypeError: pass try: class OC_InPro_TooFew3 (NSObject): def replacementObjectForArchiver_(): pass self.fail() except objc.BadPrototypeError: pass def testTooManyArguments(self): # BAD: too many arguments, should raise error try: class OC_InPro_TooMany1 (NSObject): def init(self, arg): pass self.fail() except objc.BadPrototypeError: pass try: class OC_InPro_TooMany2 (NSObject): def init(self, arg, arg2): pass self.fail() except objc.BadPrototypeError: pass try: class OC_InPro_TooMany3 (NSObject): def replacementObjectForArchiver_(self, archiver, extra): pass self.fail() except objc.BadPrototypeError: pass try: class OC_InPro_TooMany4 (NSObject): def replacementObjectForArchiver_(self, archiver, opt=3): pass self.fail() except objc.BadPrototypeError: pass def testBadOverriddenSignature(self): # BAD: signature doesn't match super class signature try: class OC_InPro_BadSig1 (NSObject): def init(self): pass init = objc.selector(init, signature=b'v@:') self.fail() except objc.BadPrototypeError: pass try: class OC_InPro_BadSig2 (NSObject): def init(self, arg1, arg2): pass init = objc.selector(init, signature=b'v@:@@') self.fail() except objc.BadPrototypeError: pass # This one is fine, just to ensure PyObjC isn't too # strict. class OC_InPro_BadSig3 (NSObject): def init(self): pass init = objc.selector(init, signature=b'@@:') def testAllArgsOptional(self): # Dodgy, all arguments are optional using '*args, **kwds' # # This should be accepted because simple decorators will use # a method signature like this and we don't want errors or warnings # for that. # # NOTE: see also the 'decorator' library, that allows you to # use decorators without ending up with an ugly signature. class OC_InPro_AllOpt1 (NSObject): def init(*args, **kwds): pass class OC_InPro_AllOpt2 (NSObject): def replacementObjectForArchiver_(*args, **kwds): pass # Also allow versions with an explicit self argument, those # are commonly used as well. class OC_InPro_AllOpt3 (NSObject): def init(self, *args, **kwds): pass class OC_InPro_AllOpt4 (NSObject): def replacementObjectForArchiver_(self, *args, **kwds): pass def testOptionalArgs(self): # BAD: optional arguments, which don't exist in Objective-C try: class OC_InPro_OptArgs1 (NSObject): def replacementObjectForArchiver_(self, *args): pass self.fail() except objc.BadPrototypeError: pass try: class OC_InPro_OptArgs2 (NSObject): def replacementObjectForArchiver_(self, **kwds): pass self.fail() except objc.BadPrototypeError: pass class TestExplicitPrototype (TestCase): # # These tests check for methods with an explict method signature in the # python code (not inheritted). The python code should match the provided # signature. def setUp(self): import warnings warnings.filterwarnings('error', category=DeprecationWarning) def tearDown(self): import warnings del warnings.filters[0] def testCorrectArgCount(self): # OK: same number of arguments class OC_ExplProto_CorrectArgCount1 (NSObject): def noargsmethod(self): pass noargsmethod = objc.selector(noargsmethod, signature=b'v@:') class OC_ExplProto_CorrectArgCount2 (NSObject): def oneargmethod_(self, archiver): pass oneargmethod_ = objc.selector(oneargmethod_, signature=b'v@:i') class OC_ExplProto_CorrectArgCount3 (NSObject): def oneargmethod2_(self, archiver=None): pass oneargmethod2_ = objc.selector(oneargmethod2_, signature=b'v@:i') def testSignatureDoesNotMatchColons(self): # OK: the signature specifies more arguments than the implicit or # explicit selector seems to need. class OC_ExplProto_ColonVsCount1 (NSObject): def twoargmethod(self, arg1, arg2): pass twoargmethod = objc.selector(twoargmethod) class OC_ExplProto_ColonVsCount2 (NSObject): def twoargmethod(self, arg1, arg2): pass twoargmethod = objc.selector(twoargmethod, signature=b'v@:@@') class OC_ExplProto_ColonVsCount3 (NSObject): def twoargmethod(self, arg1, arg2): pass twoargmethod = objc.selector(twoargmethod, selector=b'twoargs') #class OC_ExplProto_ColonVsCount4 (NSObject): # def noargmethod_(self): # pass # noargmethod_ = objc.selector(noargmethod_) #class OC_ExplProto_ColonVsCount5 (NSObject): # def noargmethod_(self): # pass # noargmethod_ = objc.selector(noargmethod_, signature=b'v@:') #class OC_ExplProto_ColonVsCount6 (NSObject): # def noargmethod_(self): # pass # noargmethod_ = objc.selector(noargmethod_, selector='doit:') def testTooFewArguments(self): # BAD: too few arguments, should raise error try: class OC_ExplProto_TooFew1 (NSObject): def oneargmethod3_(self): pass oneargmethod3_ = objc.selector(oneargmethod3_, signature=b'i@:f') self.fail() except objc.BadPrototypeError: pass def testTooManyArguments(self): # BAD: too many arguments, should raise error try: class OC_ExplProto_TooMany1 (NSObject): def oneargmethod4_(self, a, b): pass oneargmethod4_ = objc.selector(oneargmethod4_, signature=b'i@:f') self.fail() except objc.BadPrototypeError: pass def testAllArgsOptional(self): # Dodgy, all arguments are optional using '*args, **kwds' # # This should be accepted because simple decorators will use # a method signature like this and we don't want errors or warnings # for that. # # NOTE: see also the 'decorator' library, that allows you to # use decorators without ending up with an ugly signature. class OC_ExplProto_AllOpt1 (NSObject): def oneargmethod_(*args, **kwds): pass oneargmethod_ = objc.selector(oneargmethod_, signature=b'i@:i') def testOptionalArgs(self): # BAD: optional arguments, which don't exist in Objective-C try: class OC_ExplProto_OptArgs1 (NSObject): def oneargmethod_(self, *args): pass oneargmethod_ = objc.selector(oneargmethod_, signature=b'i@:i') self.fail() except objc.BadPrototypeError: pass try: class OC_ExplProto_OptArgs2 (NSObject): def oneargmethod_(self, **kwds): pass oneargmethod_ = objc.selector(oneargmethod_, signature=b'i@:i') self.fail() except objc.BadPrototypeError: pass def testOutputArgumentsPresent(self): # OK: Output arguments, all arguments are present class OC_ExplProto_OutputPresent1 (NSObject): def oneoutput_(self, output): pass oneoutput_ = objc.selector(oneoutput_, signature=b'i@:^@') class OC_ExplProto_OutputPresent2 (NSObject): def oneinput_output_(self, input, output): pass oneinput_output_ = objc.selector(oneinput_output_, signature=b'i@:f^@') def testOutputArgumentsAbsent(self): # BAD: Output arguments, output not in prototype # # NOTE: this was a warning in PyObjC 2.0 and the only # valid way to work in PyObjC 1.x. try: class OC_ExplProto_OutputAbsent1 (NSObject): def oneoutput_(self): pass oneoutput_ = objc.selector(oneoutput_, signature=b'i@:^@') self.fail() except objc.BadPrototypeError: pass try: class OC_ExplProto_OutputAbsent2 (NSObject): def oneinput_output_(self, input): pass oneinput_output_ = objc.selector(oneinput_output_, signature=b'i@:i^@') self.fail() except objc.BadPrototypeError: pass class TestImplicitSignature (TestCase): # # These tests check for methods that aren't inheritted and don't have # an explicit prototype either # def setUp(self): import warnings warnings.filterwarnings('error', category=DeprecationWarning) def tearDown(self): import warnings del warnings.filters[0] def testColonMatch(self): # OK: the number of underscores matches the number of arguments class OC_ImplProto_ColonMatch1 (NSObject): def simplemethod(self): pass self.assertEquals(OC_ImplProto_ColonMatch1.simplemethod.selector, b'simplemethod') self.assertEquals(OC_ImplProto_ColonMatch1.simplemethod.signature, b'v@:') class OC_ImplProto_ColonMatch2 (NSObject): def simplemethod_arg2_(self, a, b): return 1 self.assertEquals(OC_ImplProto_ColonMatch2.simplemethod_arg2_.selector, b'simplemethod:arg2:') self.assertEquals(OC_ImplProto_ColonMatch2.simplemethod_arg2_.signature, b'@@:@@') def testTooFewColons(self): # OK: the number of implied colons is smaller than the actual number of # arguments. # # This is fine because you want to use the regular python naming # conventions for methods that won't be called from Objective-C, # that keeps Python code as nice as possible. class OC_ImplProto_TooFew1 (NSObject): def myMethod(self, arg1, arg2=4): pass self.assertEquals(OC_ImplProto_TooFew1.myMethod.selector, b'myMethod') self.assertEquals(OC_ImplProto_TooFew1.myMethod.signature, b'v@:@@') def testTooManyColons(self): # OK: the number of implied colons is larger than the actual number # of arguments # # (see 'testTooFewColons', same argument but other coding style) class OC_ImplProto_TooMany2 (NSObject): def run_to_completion(self): pass self.assertEquals(OC_ImplProto_TooMany2.run_to_completion.selector, b'run_to_completion') self.assertEquals(OC_ImplProto_TooMany2.run_to_completion.signature, b'v@:') def testImpliedColonTooFew(self): # BAD: a method that is obviously intented to be an objective-C method, but # has too few arguments. try: class OC_ImplProto_TooFew2 (NSObject): def setFoo_(self): pass self.fail() except objc.BadPrototypeError: pass # OK: leading underscore won't be converted to a colon class OC_ImplProto_TooFew3 (NSObject): def _setFoo_(self, value): pass def testImpliedColonTooMany(self): # BAD: a method that is obviously intended to be an objective-C method, # but has too many arguments. try: class OC_ImplProto_TooMany1 (NSObject): def setFoo_(self, value, other): pass self.fail() except objc.BadPrototypeError: pass try: class OC_ImplProto_TooMany2 (NSObject): def setFoo_(self, value, other=3): pass self.fail() except objc.BadPrototypeError: pass try: class OC_ImplProto_TooMany3 (NSObject): def setFoo_(self, value, *rest): pass self.fail() except objc.BadPrototypeError: pass try: class OC_ImplProto_TooMany4 (NSObject): def setFoo_(self, value, **rest): pass self.fail() except objc.BadPrototypeError: pass def testMethodVariations(self): # OK: all methods with an implied signature are fine # # That is, as long as the implied selector doesn't contain # colons. If the implied selector does contain colons the # method must have the right number of parameters, that # should help us to avoid obvious errors. class OC_ImplProto_Variations (NSObject): def method1(self): pass def method2(self): return 1 def method1_(self, arg): pass def methodWithX_andY_(self, x, y): pass def method_with_embedded_underscores(self, a): pass def __magic__(self): pass def _leadingColon(self): pass def _leadingColon_(self, arg): return 1 def methodWithArg_(self, arg): pass # Check method signatures self.assertEquals(OC_ImplProto_Variations.method1.selector, b"method1") self.assertEquals(OC_ImplProto_Variations.method2.selector, b"method2") self.assertEquals(OC_ImplProto_Variations.method1_.selector, b"method1:") self.assertEquals(OC_ImplProto_Variations.methodWithX_andY_.selector, b"methodWithX:andY:") self.assertEquals(OC_ImplProto_Variations.method_with_embedded_underscores.selector, b"method_with_embedded_underscores") #self.assertEquals(OC_ImplProto_Variations.__magic__.selector, b"__magic__") self.assertEquals(OC_ImplProto_Variations._leadingColon.selector, b"_leadingColon") self.assertEquals(OC_ImplProto_Variations._leadingColon_.selector, b"_leadingColon:") self.assertEquals(OC_ImplProto_Variations.methodWithArg_.selector, b"methodWithArg:") # And the implied type signature self.assertEquals(OC_ImplProto_Variations.method1.signature, b"v@:") self.assertEquals(OC_ImplProto_Variations.method2.signature, b"@@:") self.assertEquals(OC_ImplProto_Variations.method1_.signature, b"v@:@") self.assertEquals(OC_ImplProto_Variations.methodWithX_andY_.signature, b"v@:@@") self.assertEquals(OC_ImplProto_Variations.method_with_embedded_underscores.signature, b"v@:@") #self.assertEquals(OC_ImplProto_Variations.__magic__.signature, b"v@:") self.assertEquals(OC_ImplProto_Variations._leadingColon.signature, b"v@:") self.assertEquals(OC_ImplProto_Variations._leadingColon_.signature, b"@@:@") self.assertEquals(OC_ImplProto_Variations.methodWithArg_.signature, b"v@:@") if __name__ == "__main__": main()