1from __future__ import unicode_literals 2from PyObjCTools.TestSupport import * 3import objc 4import copy 5from PyObjCTest.fnd import * 6 7objc.registerMetaDataForSelector( 8 b"NSObject", b"validateValue:forKey:error:", 9 dict( 10 arguments={ 11 2: dict(type_modifier=objc._C_INOUT), 12 4: dict(type_modifier=objc._C_OUT), 13 }, 14 )) 15 16 17class OCCopy (NSObject): 18 def copy(self): 19 return self.copyWithZone_(None) 20 21 def copyWithZone_(self, zone): 22 v = OCCopy.allocWithZone_(zone).init() 23 return v 24 25class OCObserve (NSObject): 26 def init(self): 27 self = super(OCObserve, self).init() 28 self.values = [] 29 self.registrations = [] 30 return self 31 32 @property 33 def seen(self): 34 return { v[1]: v[2]['new'] for v in self.values } 35 36 37 def register(self, object, keypath): 38 object.addObserver_forKeyPath_options_context_( 39 self, keypath, 0x3, None) 40 self.registrations.append((object, keypath)) 41 42 def unregister(self, object, keypath): 43 object.removeObserver_forKeyPath_(self, keypath) 44 45 def observeValueForKeyPath_ofObject_change_context_( 46 self, keypath, object, change, context): 47 48 # We don't get to keep the 'change' dictionary, make 49 # a copy (it gets reused in future calls) 50 new_change = {} 51 for k in change: 52 v = change[k] 53 if isinstance(v, (list, tuple, set)): 54 v = copy.copy(v) 55 new_change[k] = v 56 self.values.append((object, keypath, new_change)) 57 58 def __enter__(self): 59 return self 60 61 def __exit__(self, type, value, traceback): 62 for o, k in self.registrations: 63 self.unregister(o, k) 64 self.registrations = [] 65 66 67class TestObjectProperty (TestCase): 68 def testCreation(self): 69 class OCTestObjectProperty1 (NSObject): 70 p1 = objc.object_property() 71 p2 = objc.object_property(copy=True) 72 p3 = objc.object_property(read_only=True) 73 p4 = objc.object_property(ivar='myp4') 74 p5 = objc.object_property(typestr=objc._C_INT) 75 p6 = objc.object_property(typestr=objc._C_DBL) 76 77 78 o = OCTestObjectProperty1.alloc().init() 79 80 self.assertTrue(o.respondsToSelector(b'p1')) 81 self.assertTrue(o.respondsToSelector(b'setP1:')) 82 83 v = OCCopy.alloc().init() 84 o.p1 = v 85 self.assertIs(o.p1, v) 86 self.assertIs(o._p1, v) 87 88 self.assertTrue(o.respondsToSelector(b'p2')) 89 self.assertTrue(o.respondsToSelector(b'setP2:')) 90 91 o.p2 = v 92 self.assertIsInstance(o.p2, OCCopy) 93 self.assertIsNot(o.p2, v) 94 self.assertIsNot(o._p2, v) 95 96 self.assertTrue(o.respondsToSelector(b'p3')) 97 self.assertFalse(o.respondsToSelector(b'setP3:')) 98 99 o._p3 = v 100 self.assertIs(o.p3, v) 101 102 103 self.assertTrue(o.respondsToSelector(b'p4')) 104 self.assertTrue(o.respondsToSelector(b'setP4:')) 105 106 o.p4 = v 107 self.assertIs(o.p4, v) 108 self.assertIs(o.myp4, v) 109 110 self.assertTrue(o.respondsToSelector(b'p5')) 111 self.assertTrue(o.respondsToSelector(b'setP5:')) 112 self.assertTrue(o.respondsToSelector(b'p6')) 113 self.assertTrue(o.respondsToSelector(b'setP6:')) 114 s = o.methodSignatureForSelector_(b'p5') 115 self.assertEqual(s.methodReturnType(), objc._C_INT) 116 s = o.methodSignatureForSelector_(b'p6') 117 self.assertEqual(s.methodReturnType(), objc._C_DBL) 118 119 def testDepends(self): 120 class OCTestObjectProperty2 (NSObject): 121 p1 = objc.object_property() 122 p2 = objc.object_property() 123 p3 = objc.object_property(read_only=True, depends_on=['p1', 'p2']) 124 125 @p3.getter 126 def p3(self): 127 return (self.p1 or '', self.p2 or '') 128 129 class OCTestObjectProperty2b (OCTestObjectProperty2): 130 p4 = objc.object_property() 131 132 @OCTestObjectProperty2.p3.getter 133 def p3(self): 134 return (self.p4 or '', self.p2 or '', self.p1 or '') 135 p3.depends_on('p4') 136 137 p5 = objc.object_property(read_only=True) 138 139 @p5.getter 140 def p5(self): 141 return "-%s-"%(self.p4,) 142 p5.depends_on('p4') 143 144 observer1 = OCObserve.alloc().init() 145 observer2 = OCObserve.alloc().init() 146 object1 = OCTestObjectProperty2.alloc().init() 147 object2 = OCTestObjectProperty2b.alloc().init() 148 149 v = type(object1).keyPathsForValuesAffectingP3() 150 self.assertIsInstance(v, objc.lookUpClass('NSSet')) 151 self.assertEqual(v, {'p1', 'p2'}) 152 153 v = type(object2).keyPathsForValuesAffectingP3() 154 self.assertIsInstance(v, objc.lookUpClass('NSSet')) 155 self.assertEqual(v, {'p1', 'p2', 'p4'}) 156 157 self.assertTrue(object1.respondsToSelector('p1')) 158 self.assertTrue(object1.respondsToSelector('setP1:')) 159 self.assertTrue(object1.respondsToSelector('p2')) 160 self.assertTrue(object1.respondsToSelector('setP2:')) 161 self.assertTrue(object1.respondsToSelector('p3')) 162 self.assertFalse(object1.respondsToSelector('setP3:')) 163 164 self.assertTrue(object2.respondsToSelector('p1')) 165 self.assertTrue(object2.respondsToSelector('setP1:')) 166 self.assertTrue(object2.respondsToSelector('p2')) 167 self.assertTrue(object2.respondsToSelector('setP2:')) 168 self.assertTrue(object2.respondsToSelector('p3')) 169 self.assertFalse(object2.respondsToSelector('setP3:')) 170 self.assertTrue(object2.respondsToSelector('p4')) 171 self.assertTrue(object2.respondsToSelector('setP4:')) 172 173 observer1.register(object1, 'p1') 174 observer1.register(object1, 'p2') 175 observer1.register(object1, 'p3') 176 177 observer2.register(object2, 'p1') 178 observer2.register(object2, 'p2') 179 observer2.register(object2, 'p3') 180 observer2.register(object2, 'p4') 181 observer2.register(object2, 'p5') 182 183 try: 184 self.assertEqual(observer1.values, []) 185 self.assertEqual(observer2.values, []) 186 187 object1.p1 = "a" 188 object1.p2 = "b" 189 self.assertEqual(object1.p3, ("a", "b")) 190 self.assertEqual(object1.pyobjc_instanceMethods.p3(), ("a", "b")) 191 192 193 object2.p1 = "a" 194 object2.p2 = "b" 195 object2.p4 = "c" 196 self.assertEqual(object2.p3, ("c", "b", "a")) 197 self.assertEqual(object2.pyobjc_instanceMethods.p3(), ("c", "b", "a")) 198 self.assertEqual(object2.pyobjc_instanceMethods.p4(), "c") 199 200 #seen = { v[1]: v[2]['new'] for v in observer1.values } 201 self.assertEqual(observer1.seen, 202 {'p1': 'a', 'p2': 'b', 'p3': ('a', 'b') }) 203 204 #seen = { v[1]: v[2]['new'] for v in observer2.values } 205 self.assertEqual(observer2.seen, 206 {'p1': 'a', 'p2': 'b', 'p3': ('c', 'b', 'a'), 'p4': 'c', 'p5': '-c-' }) 207 208 finally: 209 observer1.unregister(object1, 'p1') 210 observer1.unregister(object1, 'p2') 211 observer1.unregister(object1, 'p3') 212 213 observer2.unregister(object2, 'p1') 214 observer2.unregister(object2, 'p2') 215 observer2.unregister(object2, 'p3') 216 observer2.unregister(object2, 'p4') 217 observer2.unregister(object2, 'p5') 218 219 def testDepends2(self): 220 class OCTestObjectProperty2B (NSObject): 221 p1 = objc.object_property() 222 @p1.getter 223 def p1(self): 224 return self._p1 225 @p1.setter 226 def p1(self, v): 227 self._p1 = v 228 229 p2 = objc.object_property() 230 @p2.getter 231 def p2(self): 232 return self._p2 233 @p2.setter 234 def p2(self, v): 235 self._p2 = v 236 237 p3 = objc.object_property(read_only=True, depends_on=['p1', 'p2']) 238 239 @p3.getter 240 def p3(self): 241 return (self.p1 or '', self.p2 or '') 242 243 class OCTestObjectProperty2Bb (OCTestObjectProperty2B): 244 p4 = objc.object_property() 245 246 @OCTestObjectProperty2B.p1.getter 247 def p1(self): 248 return self._p1 249 250 @OCTestObjectProperty2B.p3.getter 251 def p3(self): 252 return (self.p4 or '', self.p2 or '', self.p1 or '') 253 p3.depends_on('p4') 254 255 observer1 = OCObserve.alloc().init() 256 observer2 = OCObserve.alloc().init() 257 object1 = OCTestObjectProperty2B.alloc().init() 258 object2 = OCTestObjectProperty2Bb.alloc().init() 259 260 v = type(object1).keyPathsForValuesAffectingP3() 261 self.assertIsInstance(v, objc.lookUpClass('NSSet')) 262 self.assertEqual(v, {'p1', 'p2'}) 263 264 v = type(object2).keyPathsForValuesAffectingP3() 265 self.assertIsInstance(v, objc.lookUpClass('NSSet')) 266 self.assertEqual(v, {'p1', 'p2', 'p4'}) 267 268 self.assertTrue(object1.respondsToSelector('p1')) 269 self.assertTrue(object1.respondsToSelector('setP1:')) 270 self.assertTrue(object1.respondsToSelector('p2')) 271 self.assertTrue(object1.respondsToSelector('setP2:')) 272 self.assertTrue(object1.respondsToSelector('p3')) 273 self.assertFalse(object1.respondsToSelector('setP3:')) 274 275 self.assertTrue(object2.respondsToSelector('p1')) 276 self.assertTrue(object2.respondsToSelector('setP1:')) 277 self.assertTrue(object2.respondsToSelector('p2')) 278 self.assertTrue(object2.respondsToSelector('setP2:')) 279 self.assertTrue(object2.respondsToSelector('p3')) 280 self.assertFalse(object2.respondsToSelector('setP3:')) 281 self.assertTrue(object2.respondsToSelector('p4')) 282 self.assertTrue(object2.respondsToSelector('setP4:')) 283 284 observer1.register(object1, 'p1') 285 observer1.register(object1, 'p2') 286 observer1.register(object1, 'p3') 287 288 observer2.register(object2, 'p1') 289 observer2.register(object2, 'p2') 290 observer2.register(object2, 'p3') 291 observer2.register(object2, 'p4') 292 293 try: 294 self.assertEqual(observer1.values, []) 295 self.assertEqual(observer2.values, []) 296 297 object1.p1 = "a" 298 object1.p2 = "b" 299 self.assertEqual(object1.p3, ("a", "b")) 300 self.assertEqual(object1.pyobjc_instanceMethods.p3(), ("a", "b")) 301 302 303 object2.p1 = "a" 304 object2.p2 = "b" 305 object2.p4 = "c" 306 self.assertEqual(object2.p3, ("c", "b", "a")) 307 self.assertEqual(object2.pyobjc_instanceMethods.p3(), ("c", "b", "a")) 308 self.assertEqual(object2.pyobjc_instanceMethods.p4(), "c") 309 310 #seen = { v[1]: v[2]['new'] for v in observer1.values } 311 self.assertEqual(observer1.seen, 312 {'p1': 'a', 'p2': 'b', 'p3': ('a', 'b') }) 313 314 #seen = { v[1]: v[2]['new'] for v in observer2.values } 315 self.assertEqual(observer2.seen, 316 {'p1': 'a', 'p2': 'b', 'p3': ('c', 'b', 'a'), 'p4': 'c' }) 317 318 finally: 319 observer1.unregister(object1, 'p1') 320 observer1.unregister(object1, 'p2') 321 observer1.unregister(object1, 'p3') 322 323 observer2.unregister(object2, 'p1') 324 observer2.unregister(object2, 'p2') 325 observer2.unregister(object2, 'p3') 326 observer2.unregister(object2, 'p4') 327 328 def testMethods(self): 329 l = [] 330 331 class OCTestObjectProperty4 (NSObject): 332 333 p1 = objc.object_property() 334 335 @p1.getter 336 def p1(self): 337 l.append(('get',)) 338 return self._p1 + '!' 339 340 @p1.setter 341 def p1(self, v): 342 l.append(('set', v)) 343 self._p1 = v + '?' 344 345 346 @p1.validate 347 def p1(self, value, error): 348 if value == 1: 349 return (True, value, None) 350 else: 351 return (False, 2, "snake") 352 353 class OCTestObjectProperty4b (OCTestObjectProperty4): 354 @OCTestObjectProperty4.p1.validate 355 def p1(self, value, error): 356 if value == 2: 357 return (True, value, None) 358 else: 359 return (False, 2, "monty") 360 361 362 o = OCTestObjectProperty4.alloc().init() 363 o.p1 = 'f' 364 self.assertEqual(o.p1, 'f?!') 365 self.assertEqual(o._p1, 'f?') 366 self.assertEqual(l, [('set', 'f'), ('get',)]) 367 368 ok, value, error = o.validateValue_forKey_error_( 369 1, 'p1', None) 370 self.assertTrue(ok) 371 self.assertEqual(value, 1) 372 self.assertEqual(error, None) 373 374 ok, value, error = o.validateValue_forKey_error_( 375 9, 'p1', None) 376 self.assertFalse(ok) 377 self.assertEqual(value, 2) 378 self.assertEqual(error, "snake") 379 380 l = [] 381 o = OCTestObjectProperty4b.alloc().init() 382 o.p1 = 'f' 383 self.assertEqual(o.p1, 'f?!') 384 self.assertEqual(o._p1, 'f?') 385 self.assertEqual(l, [('set', 'f'), ('get',)]) 386 387 ok, value, error = o.validateValue_forKey_error_( 388 2, 'p1', None) 389 self.assertTrue(ok) 390 self.assertEqual(value, 2) 391 self.assertEqual(error, None) 392 393 ok, value, error = o.validateValue_forKey_error_( 394 9, 'p1', None) 395 self.assertFalse(ok) 396 self.assertEqual(value, 2) 397 self.assertEqual(error, "monty") 398 399 def testNative(self): 400 l = [] 401 class OCTestObjectProperty7 (NSObject): 402 p1 = objc.object_property() 403 404 @p1.getter 405 def p1(self): 406 l.append('get') 407 return self._p1 408 409 @p1.setter 410 def p1(self, value): 411 l.append('set') 412 self._p1 = value 413 414 415 o = OCTestObjectProperty7.alloc().init() 416 o.setValue_forKey_(42, 'p1') 417 self.assertEqual(o._p1, 42) 418 419 o._p1 = "monkey" 420 v = o.valueForKey_('p1') 421 self.assertEqual(v, "monkey") 422 423 self.assertEqual(l, ["set", "get"]) 424 425 426 def testDynamic(self): 427 class OCTestObjectProperty8 (NSObject): 428 p1 = objc.object_property(dynamic=True) 429 p2 = objc.object_property(dynamic=True, typestr=objc._C_NSBOOL) 430 431 self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"p1")) 432 self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"setP1:")) 433 self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"isP2")) 434 self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"setP2:")) 435 436 v = [42] 437 def getter(self): 438 return v[0] 439 def setter(self, value): 440 v[0] = value 441 442 OCTestObjectProperty8.p1 = getter 443 OCTestObjectProperty8.setP1_ = setter 444 445 v2 = [False] 446 def getter2(self): 447 return v2[0] 448 def setter2(self, value): 449 v2[0] = bool(value) 450 OCTestObjectProperty8.isP2 = getter2 451 OCTestObjectProperty8.setP2_ = setter2 452 453 454 self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"p1")) 455 self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"setP1:")) 456 self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"isP2")) 457 self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"setP2:")) 458 459 o = OCTestObjectProperty8.alloc().init() 460 self.assertIsInstance(OCTestObjectProperty8.p1, objc.object_property) 461 self.assertIsInstance(OCTestObjectProperty8.p2, objc.object_property) 462 463 464 self.assertEqual(o.p1, 42) 465 self.assertEqual(o.p2, False) 466 o.p1 = 99 467 o.p2 = True 468 self.assertEqual(o.p1, 99) 469 self.assertEqual(v[0], 99) 470 self.assertEqual(o.p2, True) 471 self.assertEqual(v2[0], True) 472 473 474 def testReadOnly(self): 475 476 class OCTestObjectProperty3 (NSObject): 477 p1 = objc.object_property(read_only=True) 478 479 o = OCTestObjectProperty3.alloc().init() 480 481 self.assertRaises(ValueError, setattr, o, 'p1', 42) 482 483 def testSubclass(self): 484 485 class OCTestObjectProperty5 (NSObject): 486 p1 = objc.object_property(read_only=True) 487 p2 = objc.object_property() 488 p3 = objc.object_property(read_only=True, typestr=objc._C_NSBOOL) 489 490 class OCTestObjectProperty6 (OCTestObjectProperty5): 491 @OCTestObjectProperty5.p1.setter 492 def p1(self, value): 493 self._p1 = value 494 495 @OCTestObjectProperty5.p2.setter 496 def p2(self, value): 497 self._p2 = value * 2 498 499 @OCTestObjectProperty5.p3.getter 500 def p3(self): 501 return not super(OCTestObjectProperty6, self).p3 502 503 base = OCTestObjectProperty5.alloc().init() 504 self.assertRaises(ValueError, setattr, base, 'p1', 1) 505 self.assertRaises(ValueError, setattr, base, 'p3', 1) 506 base.p2 = 'b' 507 self.assertEqual(base.p2, 'b') 508 509 sub = OCTestObjectProperty6.alloc().init() 510 sub.p1 = 1 511 sub.p2 = 'a' 512 sub._p3 = False 513 self.assertEqual(sub.p1, 1) 514 self.assertEqual(sub.p2, 'aa') 515 self.assertEqual(sub.p3, True) 516 517 self.assertTrue(base.respondsToSelector_(b'p2')) 518 self.assertFalse(base.respondsToSelector_(b'setP1:')) 519 self.assertTrue(base.respondsToSelector_(b'isP3')) 520 self.assertFalse(base.respondsToSelector_(b'p3')) 521 522 self.assertTrue(sub.respondsToSelector_(b'p2')) 523 self.assertTrue(sub.respondsToSelector_(b'setP1:')) 524 self.assertTrue(sub.respondsToSelector_(b'isP3')) 525 self.assertFalse(sub.respondsToSelector_(b'p3')) 526 527 try: 528 del sub.p3 529 except TypeError: 530 pass 531 else: 532 self.fail("Deleting an object_property shouldn't be possible") 533 534 def testDefaultSetterWithoutIvar(self): 535 536 try: 537 class OCTestObjectProperty7 (NSObject): 538 p1 = objc.object_property(ivar=objc.NULL) 539 except ValueError: 540 pass 541 542 else: 543 self.fail("ValueError not raised") 544 545 try: 546 class OCTestObjectProperty8 (NSObject): 547 p1 = objc.object_property(ivar=objc.NULL, read_only=True) 548 except ValueError: 549 pass 550 551 else: 552 self.fail("ValueError not raised") 553 554 555 try: 556 class OCTestObjectProperty9 (NSObject): 557 p1 = objc.object_property(read_only=True) 558 559 @p1.setter 560 def p1(self, v): 561 pass 562 563 except ValueError: 564 pass 565 566 else: 567 self.fail("ValueError not raised") 568 569 try: 570 class OCTestObjectProperty9 (NSObject): 571 p1 = objc.object_property(read_only=True) 572 573 @p1.validate 574 def p1(self, v): 575 pass 576 577 except ValueError: 578 pass 579 580 else: 581 self.fail("ValueError not raised") 582 583class TestBoolProperty (TestCase): 584 def testDefault(self): 585 class OCTestBoolProperty1 (NSObject): 586 p1 = objc.bool_property() 587 588 o = OCTestBoolProperty1.alloc().init() 589 self.assertEqual(o.p1, False) 590 591 o.p1 = [1, 2] 592 self.assertEqual(o.p1, True) 593 594if __name__ == "__main__": 595 main() 596