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=b'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=b'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=b'@@:') 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=b'v@:') 218 219 class OC_ExplProto_CorrectArgCount2 (NSObject): 220 def oneargmethod_(self, archiver): 221 pass 222 oneargmethod_ = objc.selector(oneargmethod_, signature=b'v@:i') 223 224 class OC_ExplProto_CorrectArgCount3 (NSObject): 225 def oneargmethod2_(self, archiver=None): 226 pass 227 oneargmethod2_ = objc.selector(oneargmethod2_, signature=b'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=b'v@:@@') 242 243 class OC_ExplProto_ColonVsCount3 (NSObject): 244 def twoargmethod(self, arg1, arg2): 245 pass 246 twoargmethod = objc.selector(twoargmethod, selector=b'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=b'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=b'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=b'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=b'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=b'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=b'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=b'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=b'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=b'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=b'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.assertEqual(OC_ImplProto_ColonMatch1.simplemethod.selector, b'simplemethod') 392 self.assertEqual(OC_ImplProto_ColonMatch1.simplemethod.signature, b'v@:') 393 394 class OC_ImplProto_ColonMatch2 (NSObject): 395 def simplemethod_arg2_(self, a, b): 396 return 1 397 self.assertEqual(OC_ImplProto_ColonMatch2.simplemethod_arg2_.selector, b'simplemethod:arg2:') 398 self.assertEqual(OC_ImplProto_ColonMatch2.simplemethod_arg2_.signature, b'@@:@@') 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.assertEqual(OC_ImplProto_TooFew1.myMethod.selector, b'myMethod') 411 self.assertEqual(OC_ImplProto_TooFew1.myMethod.signature, b'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.assertEqual(OC_ImplProto_TooMany2.run_to_completion.selector, b'run_to_completion') 422 self.assertEqual(OC_ImplProto_TooMany2.run_to_completion.signature, b'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.assertEqual(OC_ImplProto_Variations.method1.selector, b"method1") 498 self.assertEqual(OC_ImplProto_Variations.method2.selector, b"method2") 499 self.assertEqual(OC_ImplProto_Variations.method1_.selector, b"method1:") 500 self.assertEqual(OC_ImplProto_Variations.methodWithX_andY_.selector, b"methodWithX:andY:") 501 self.assertEqual(OC_ImplProto_Variations.method_with_embedded_underscores.selector, b"method_with_embedded_underscores") 502 #self.assertEqual(OC_ImplProto_Variations.__magic__.selector, b"__magic__") 503 self.assertEqual(OC_ImplProto_Variations._leadingColon.selector, b"_leadingColon") 504 self.assertEqual(OC_ImplProto_Variations._leadingColon_.selector, b"_leadingColon:") 505 self.assertEqual(OC_ImplProto_Variations.methodWithArg_.selector, b"methodWithArg:") 506 507 # And the implied type signature 508 self.assertEqual(OC_ImplProto_Variations.method1.signature, b"v@:") 509 self.assertEqual(OC_ImplProto_Variations.method2.signature, b"@@:") 510 self.assertEqual(OC_ImplProto_Variations.method1_.signature, b"v@:@") 511 self.assertEqual(OC_ImplProto_Variations.methodWithX_andY_.signature, b"v@:@@") 512 self.assertEqual(OC_ImplProto_Variations.method_with_embedded_underscores.signature, b"v@:@") 513 #self.assertEqual(OC_ImplProto_Variations.__magic__.signature, b"v@:") 514 self.assertEqual(OC_ImplProto_Variations._leadingColon.signature, b"v@:") 515 self.assertEqual(OC_ImplProto_Variations._leadingColon_.signature, b"@@:@") 516 self.assertEqual(OC_ImplProto_Variations.methodWithArg_.signature, b"v@:@") 517 518if __name__ == "__main__": 519 main() 520