1""" 2Tests for the Key-Value Coding support in OC_PythonObject 3 4NOTE: Testcases here should be synchronized with the Key-Value Coding tests 5in PyObjCTools.test.test_keyvalue and Foundation.test.test_keyvalue. 6""" 7import objc 8from PyObjCTools.TestSupport import * 9from PyObjCTest.fnd import * 10 11 12# Native code is needed to access the python class from Objective-C, otherwise 13# the Key-Value support cannot be tested. 14from PyObjCTest.testbndl import PyObjC_TestClass3 as STUB 15from PyObjCTest.testbndl import PyObjCTest_KeyValueObserver 16from PyObjCTest.testbndl import * 17from PyObjCTest.keyvaluehelper import * 18 19class KeyValueClass2 (object): 20 def __init__(self): 21 self.key3 = 3 22 self._key4 = u"4" 23 self._pythonConvention = u'BAD' 24 self._pythonConventionValue = u'GOOD' 25 self.__private = u'private' 26 27 def addMultiple(self): 28 self.multiple = KeyValueClass2() 29 self.multiple.level2 = KeyValueClass2() 30 self.multiple.level2.level3 = KeyValueClass2() 31 self.multiple.level2.level3.keyA = u"hello" 32 self.multiple.level2.level3.keyB = u"world" 33 34 def pythonConvention(self): 35 return self._pythonConventionValue 36 37 def setPythonConvention_(self, value): 38 self._pythonConventionValue = value 39 40 def getKey1(self): 41 return 1 42 43 def get_key2(self): 44 return 2 45 46 def setKey4(self, value): 47 self._key4 = value * 4 48 49 def set_key5(self, value): 50 self.key5 = value * 5 51 52 53class KeyValueClass3 (object): 54 __slots__ = ('foo', ) 55 56 def __init__(self): 57 self.foo = u"foobar" 58 59 # Definition for property 'bar'. Use odd names for the methods 60 # because the KeyValue support recognizes the usual names. 61 def read_bar(self): 62 return self.foo + self.foo 63 64 def write_bar (self, value): 65 self.foo = value + value 66 67 bar = property(read_bar, write_bar) 68 69 roprop = property(lambda self: u"read-only") 70 71class PyKeyValueCoding (TestCase): 72 def testNoPrivateVars(self): 73 # Private instance variables ('anObject.__value') are not accessible using 74 # key-value coding. 75 o = KeyValueClass2() 76 self.assertRaises(KeyError, 77 STUB.keyValue_forObject_key_, DO_VALUEFORKEY, o, u"private") 78 79 def testValueForKey(self): 80 o = KeyValueClass2() 81 o.addMultiple() 82 83 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"key1"), 1) 84 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"key2"), 2) 85 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"key3"), 3) 86 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"key4"), u"4") 87 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"multiple"), o.multiple) 88 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"pythonConvention"), u'GOOD') 89 90 self.assertRaises(KeyError, STUB.keyValue_forObject_key_, DO_VALUEFORKEY, o, u"nokey") 91 92 def testValueForKey2(self): 93 o = KeyValueClass3() 94 95 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"foo"), u"foobar") 96 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"bar"), u"foobarfoobar") 97 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"roprop"), u"read-only") 98 99 def testStoredValueForKey(self): 100 o = KeyValueClass2() 101 o.addMultiple() 102 103 self.assertEquals(STUB.keyValue_forObject_key_(DO_STOREDVALUEFORKEY, o, u"key1"), 1) 104 self.assertEquals(STUB.keyValue_forObject_key_(DO_STOREDVALUEFORKEY, o, u"key2"), 2) 105 self.assertEquals(STUB.keyValue_forObject_key_(DO_STOREDVALUEFORKEY, o, u"key3"), 3) 106 self.assertEquals(STUB.keyValue_forObject_key_(DO_STOREDVALUEFORKEY, o, u"key4"), u"4") 107 self.assertEquals(STUB.keyValue_forObject_key_(DO_STOREDVALUEFORKEY, o, u"multiple"), o.multiple) 108 109 self.assertRaises(KeyError, STUB.keyValue_forObject_key_, DO_STOREDVALUEFORKEY, o, u"nokey") 110 111 def testStoredValueForKey2(self): 112 o = KeyValueClass3() 113 114 self.assertEquals(STUB.keyValue_forObject_key_(DO_STOREDVALUEFORKEY, o, u"foo"), u"foobar") 115 self.assertEquals(STUB.keyValue_forObject_key_(DO_STOREDVALUEFORKEY, o, u"bar"), u"foobarfoobar") 116 self.assertEquals(STUB.keyValue_forObject_key_(DO_STOREDVALUEFORKEY, o, u"roprop"), u"read-only") 117 118 def testValueForKeyPath(self): 119 o = KeyValueClass2() 120 o.addMultiple() 121 122 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, o, u"multiple"), o.multiple) 123 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, o, u"multiple.level2"), o.multiple.level2) 124 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, o, u"multiple.level2.level3.keyA"), o.multiple.level2.level3.keyA) 125 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, o, u"multiple.level2.level3.keyB"), o.multiple.level2.level3.keyB) 126 127 self.assertRaises(KeyError, STUB.keyValue_forObject_key_, DO_VALUEFORKEYPATH, o, u"multiple.level2.nokey") 128 129 def testValuesForKeys(self): 130 o = KeyValueClass2() 131 132 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUESFORKEYS, o, [u"key1", u"key2", u"key3", u"key4"]), { u"key1":1, u"key2": 2, u"key3": 3, u"key4": u"4"} ) 133 134 self.assertRaises(KeyError, STUB.keyValue_forObject_key_, DO_VALUESFORKEYS, o, [ u"key1", u"key2", u"nokey", u"key3" ]) 135 136 def testTakeValueForKey(self): 137 o = KeyValueClass2() 138 139 self.assertEquals(o.key3, 3) 140 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUE_FORKEY, o, u'key3', u'drie') 141 self.assertEquals(o.key3, u"drie") 142 143 self.assertEquals(o._key4, u"4") 144 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUE_FORKEY, o, u'key4', u'vier') 145 self.assertEquals(o._key4, u"viervierviervier") 146 147 o.key5 = 1 148 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUE_FORKEY, o, u'key5', u'V') 149 self.assertEquals(o.key5, u"VVVVV") 150 151 self.assertNotHasAttr(o, u'key9') 152 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUE_FORKEY, o, u'key9', u'IX') 153 self.assertHasAttr(o, u'key9') 154 self.assertEquals(o.key9, u'IX') 155 156 def testTakeValueForKey2(self): 157 o = KeyValueClass3() 158 159 self.assertEquals(o.foo, u"foobar") 160 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUE_FORKEY, o, u'foo', u'FOO') 161 self.assertEquals(o.foo, u"FOO") 162 163 self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, DO_TAKEVALUE_FORKEY, o, u'key9', u'IX') 164 165 def testTakeStoredValueForKey(self): 166 o = KeyValueClass2() 167 168 self.assertEquals(o.key3, 3) 169 STUB.setKeyValue_forObject_key_value_(DO_TAKESTOREDVALUE_FORKEY, o, u'key3', u'drie') 170 self.assertEquals(o.key3, u"drie") 171 172 self.assertEquals(o._key4, u"4") 173 STUB.setKeyValue_forObject_key_value_(DO_TAKESTOREDVALUE_FORKEY, o, u'key4', u'vier') 174 self.assertEquals(o._key4, u"viervierviervier") 175 176 o.key5 = 1 177 STUB.setKeyValue_forObject_key_value_(DO_TAKESTOREDVALUE_FORKEY, o, u'key5', u'V') 178 self.assertEquals(o.key5, u"VVVVV") 179 180 self.assertNotHasAttr(o, u'key9') 181 STUB.setKeyValue_forObject_key_value_(DO_TAKESTOREDVALUE_FORKEY, o, u'key9', u'IX') 182 self.assertHasAttr(o, u'key9') 183 self.assertEquals(o.key9, u'IX') 184 185 def testStoredTakeValueForKey2(self): 186 o = KeyValueClass3() 187 188 self.assertEquals(o.foo, u"foobar") 189 STUB.setKeyValue_forObject_key_value_(DO_TAKESTOREDVALUE_FORKEY, o, u'foo', u'FOO') 190 self.assertEquals(o.foo, u"FOO") 191 192 self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, DO_TAKESTOREDVALUE_FORKEY, o, u'key9', u'IX') 193 self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, DO_TAKESTOREDVALUE_FORKEY, o, u'roprop', u'IX') 194 195 def testTakeValuesFromDictionary(self): 196 o = KeyValueClass2() 197 198 self.assertEquals(o.key3, 3) 199 self.assertEquals(o._key4, u"4") 200 o.key5 = 1 201 self.assertNotHasAttr(o, u'key9') 202 203 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUESFROMDICT, o, None, 204 { 205 u'key3': u'drie', 206 u'key4': u'vier', 207 u'key5': u'V', 208 u'key9': u'IX', 209 }) 210 211 self.assertEquals(o.key3, u"drie") 212 self.assertEquals(o._key4, u"viervierviervier") 213 self.assertEquals(o.key5, u"VVVVV") 214 self.assertHasAttr(o, u'key9') 215 self.assertEquals(o.key9, u'IX') 216 217 def testTakeValuesFromDictionary2(self): 218 o = KeyValueClass3() 219 220 self.assertEquals(o.foo, u"foobar") 221 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUESFROMDICT, o, None, { u'foo': u'FOO' }) 222 self.assertEquals(o.foo, u"FOO") 223 224 self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, DO_TAKEVALUESFROMDICT, o, None, { u'key9': u'IX' }) 225 self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, DO_TAKEVALUESFROMDICT, o, None, { u'roprop': u'IX' }) 226 227 def testTakeValueForKeyPath(self): 228 o = KeyValueClass2() 229 o.addMultiple() 230 231 self.assertEquals(o.multiple.level2.level3.keyA, u"hello") 232 self.assertEquals(o.multiple.level2.level3.keyB, u"world") 233 234 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUE_FORKEYPATH, o, u"multiple.level2.level3.keyA", u"KeyAValue") 235 self.assertEquals(o.multiple.level2.level3.keyA, u"KeyAValue") 236 237 STUB.setKeyValue_forObject_key_value_(DO_TAKEVALUE_FORKEYPATH, o, u"multiple.level2.level3.keyB", 9.999) 238 self.assertEquals(o.multiple.level2.level3.keyB, 9.999) 239 240 241class TestAccMethod (TestCase): 242 def testStrCap(self): 243 class Foo: 244 def callme(self): 245 return u"FOO" 246 247 # check the result for valueForKey: u"callme" on a Foo instance 248 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, Foo(), u"callme"), u"FOO") 249 250 def testStr(self): 251 # Strings are automaticly converted to NSStrings, and those don't have 252 # a capitalize key. 253 self.assertRaises(KeyError, STUB.keyValue_forObject_key_, DO_VALUEFORKEY, 254 u"hello", u"capitalize") 255 self.assertRaises(KeyError, STUB.keyValue_forObject_key_, DO_VALUEFORKEY, 256 u"hello", u"capitalize") 257 258 259class AbstractKVCodingTest: 260 def testBaseValueForKey(self): 261 self.assertEquals(DirectString, 262 STUB.keyValue_forObject_key_(DO_VALUEFORKEY, self.base, u"directString")) 263 self.assertEquals(IndirectString, 264 STUB.keyValue_forObject_key_(DO_VALUEFORKEY, self.base, u"indirectString")) 265 self.assertEquals(DirectNumber, 266 STUB.keyValue_forObject_key_(DO_VALUEFORKEY, self.base, u"directNumber")) 267 self.assertEquals(IndirectNumber, 268 STUB.keyValue_forObject_key_(DO_VALUEFORKEY, self.base, u"indirectNumber")) 269 270 def testPathValueForKey(self): 271 self.assertEquals(DirectString, 272 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"directHead.directString")) 273 self.assertEquals(DirectString, 274 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"indirectHead.directString")) 275 self.assertEquals(IndirectString, 276 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"directHead.indirectString")) 277 self.assertEquals(IndirectString, 278 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"indirectHead.indirectString")) 279 self.assertEquals(DirectNumber, 280 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"directHead.directNumber")) 281 self.assertEquals(DirectNumber, 282 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"indirectHead.directNumber")) 283 self.assertEquals(IndirectNumber, 284 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"directHead.indirectNumber")) 285 self.assertEquals(IndirectNumber, 286 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"indirectHead.indirectNumber")) 287 288class TestObjCKVCoding(AbstractKVCodingTest, TestCase): 289 def setUp(self): 290 self.base = PyObjCTest_KVBaseClass.new() 291 self.path = PyObjCTest_KVPathClass.new() 292 293class TestPythonKVCoding(AbstractKVCodingTest, TestCase): 294 def setUp(self): 295 self.base = KVPyBase() 296 self.path = KVPyPath() 297 298class TestPythonSubObjCContainerCoding(AbstractKVCodingTest, TestCase): 299 def setUp(self): 300 self.base = KVPySubObjCBase.new() 301 self.path = KVPySubObjCPath.new() 302 303class TestPythonSubOverObjC(AbstractKVCodingTest, TestCase): 304 def setUp(self): 305 self.base = KVPySubOverObjCBase.new() 306 self.path = KVPySubOverObjCPath.new() 307 308 def testOverValueKey(self): 309 self.assertEquals(DirectString, 310 STUB.keyValue_forObject_key_(DO_VALUEFORKEY, self.base, u"overDirectString")) 311 self.assertEquals(IndirectString, 312 STUB.keyValue_forObject_key_(DO_VALUEFORKEY, self.base, u"overIndirectString")) 313 314 def testOverValueKeyPath(self): 315 self.assertEquals(DirectString, 316 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"overDirectHead.directString")) 317 self.assertEquals(DirectString, 318 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"overIndirectHead.directString")) 319 self.assertEquals(IndirectString, 320 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"overDirectHead.indirectString")) 321 self.assertEquals(IndirectString, 322 STUB.keyValue_forObject_key_(DO_VALUEFORKEYPATH, self.path, u"overIndirectHead.indirectString")) 323 324 325 326import sys, os 327if sys.platform == "darwin" and os.uname()[2] >= '7.0.0': 328 329 # MacOS X 10.3 and later use 'setValue:forKey: u' instead of 330 # 'takeValue:forKey: u', test these as wel. 331 332 class PyKeyValueCoding_10_3 (TestCase): 333 def testPythonConvention(self): 334 o = KeyValueClass2() 335 336 self.assertEquals(o._pythonConvention, u'BAD') 337 self.assertEquals(o.pythonConvention(), u'GOOD') 338 self.assertEquals(o._pythonConventionValue, u'GOOD') 339 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"pythonConvention"), u'GOOD') 340 STUB.setKeyValue_forObject_key_value_(DO_SETVALUE_FORKEY, o, u'pythonConvention', u'CHANGED') 341 self.assertEquals(STUB.keyValue_forObject_key_(DO_VALUEFORKEY, o, u"pythonConvention"), u'CHANGED') 342 self.assertEquals(o._pythonConvention, u'BAD') 343 self.assertEquals(o.pythonConvention(), u'CHANGED') 344 self.assertEquals(o._pythonConventionValue, u'CHANGED') 345 346 347 def testSetValueForKey(self): 348 o = KeyValueClass2() 349 350 self.assertEquals(o.key3, 3) 351 STUB.setKeyValue_forObject_key_value_(DO_SETVALUE_FORKEY, o, u'key3', u'drie') 352 self.assertEquals(o.key3, u"drie") 353 354 self.assertEquals(o._key4, u"4") 355 STUB.setKeyValue_forObject_key_value_(DO_SETVALUE_FORKEY, o, u'key4', u'vier') 356 self.assertEquals(o._key4, u"viervierviervier") 357 358 o.key5 = 1 359 STUB.setKeyValue_forObject_key_value_(DO_SETVALUE_FORKEY, o, u'key5', u'V') 360 self.assertEquals(o.key5, u"VVVVV") 361 362 self.assertNotHasAttr(o, u'key9') 363 STUB.setKeyValue_forObject_key_value_(DO_SETVALUE_FORKEY, o, u'key9', u'IX') 364 self.assertHasAttr(o, u'key9') 365 self.assertEquals(o.key9, u'IX') 366 367 def testTakeValueForKey2(self): 368 o = KeyValueClass3() 369 370 self.assertEquals(o.foo, u"foobar") 371 STUB.setKeyValue_forObject_key_value_(DO_SETVALUE_FORKEY, o, u'foo', u'FOO') 372 self.assertEquals(o.foo, u"FOO") 373 374 self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, DO_SETVALUE_FORKEY, o, u'key9', u'IX') 375 376 def testSetValuesForKeysFromDictionary(self): 377 o = KeyValueClass2() 378 379 self.assertEquals(o.key3, 3) 380 self.assertEquals(o._key4, u"4") 381 o.key5 = 1 382 self.assertNotHasAttr(o, u'key9') 383 384 STUB.setKeyValue_forObject_key_value_(DO_SETVALUESFORKEYSFROMDICT, o, None, 385 { 386 u'key3': u'drie', 387 u'key4': u'vier', 388 u'key5': u'V', 389 u'key9': u'IX', 390 }) 391 392 self.assertEquals(o.key3, u"drie") 393 self.assertEquals(o._key4, u"viervierviervier") 394 self.assertEquals(o.key5, u"VVVVV") 395 self.assertHasAttr(o, u'key9') 396 self.assertEquals(o.key9, u'IX') 397 398 def testSetValuesForKeysFromDictionary2(self): 399 o = KeyValueClass3() 400 401 self.assertEquals(o.foo, u"foobar") 402 STUB.setKeyValue_forObject_key_value_(DO_SETVALUESFORKEYSFROMDICT, o, None, { u'foo': u'FOO' }) 403 self.assertEquals(o.foo, u"FOO") 404 405 self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, DO_SETVALUESFORKEYSFROMDICT, o, None, { u'key9': u'IX' }) 406 self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, DO_SETVALUESFORKEYSFROMDICT, o, None, { u'roprop': u'IX' }) 407 408 def testSetValueForKeyPath(self): 409 o = KeyValueClass2() 410 o.addMultiple() 411 412 self.assertEquals(o.multiple.level2.level3.keyA, u"hello") 413 self.assertEquals(o.multiple.level2.level3.keyB, u"world") 414 415 STUB.setKeyValue_forObject_key_value_(DO_SETVALUE_FORKEYPATH, o, u"multiple.level2.level3.keyA", u"KeyAValue") 416 self.assertEquals(o.multiple.level2.level3.keyA, u"KeyAValue") 417 418 STUB.setKeyValue_forObject_key_value_(DO_SETVALUE_FORKEYPATH, o, u"multiple.level2.level3.keyB", 9.999) 419 self.assertEquals(o.multiple.level2.level3.keyB, 9.999) 420 421 422class PyObjC_TestKeyValueSource (NSObject): 423 def getFoobar(self): 424 return u"Hello world" 425 426if PyObjCTest_KeyValueObserver is not None: 427 class TestKeyValueObservingFromNative (TestCase): 428 # This test makes uses of Key-Value Coding/Observing from Objective-C. 429 # Versions of PyObjC upto 2003-12-29 crashed on this test due to the way 430 # key-value observing is implemented in Cocoa. 431 432 def testOne(self): 433 o = PyObjCTest_KeyValueObserver.alloc().initWithInstanceOfClass_withKey_(PyObjC_TestKeyValueSource, u"foobar") 434 self.assertEquals(o.getValue(), u"Hello world") 435 del o 436 437 global DEALLOCS 438 DEALLOCS = 0 439 440 class PyObjCTestObserved1 (NSObject): 441 __slots__ = ( '_kvo_bar', '_kvo_foo') 442 443 FOOBASE = u"base" 444 445 def init(self): 446 self = super(PyObjCTestObserved1, self).init() 447 if self is not None: 448 self._kvo_bar = None 449 self._kvo_foo = None 450 return self 451 452 def initiateDestructionSequence(self): 453 # Exercise a bug between KVO and the old 454 # initialization scheme. 455 pass 456 457 def setBar_(self, value): 458 self.initiateDestructionSequence() 459 self._kvo_bar = value 460 setBar_ = objc.accessor(setBar_) 461 462 def bar(self): 463 self.initiateDestructionSequence() 464 return self._kvo_bar 465 bar = objc.accessor(bar) 466 467 def setFoo_(self, value): 468 self.initiateDestructionSequence() 469 self._kvo_foo = self.FOOBASE + value 470 setFoo_ = objc.accessor(setFoo_) 471 472 def foo(self): 473 self.initiateDestructionSequence() 474 return self._kvo_foo 475 foo = objc.accessor(foo) 476 477 def __del__(self): 478 global DEALLOCS 479 DEALLOCS += 1 480 481 class PyObjCTestObserved2(NSObject): 482 bar = objc.ivar('bar') 483 484 def init(self): 485 self = super(PyObjCTestObserved2, self).init() 486 self.foo = None 487 return self 488 489 def __del__(self): 490 global DEALLOCS 491 DEALLOCS += 1 492 493 class TestKeyValueObservingFromPython (TestCase): 494 # Check for using KVO in python. 495 496 def testAutomaticObserving(self): 497 outer_pool = NSAutoreleasePool.alloc().init() 498 observer = PyObjCTestObserver.alloc().init() 499 o = PyObjCTestObserved2.alloc().init() 500 pool = NSAutoreleasePool.alloc().init() 501 502 self.assertEquals(o.foo, None) 503 self.assertEquals(o.bar, None) 504 505 o.foo = u'foo' 506 self.assertEquals(o.foo, u'foo') 507 508 o.bar = u'bar' 509 self.assertEquals(o.bar, u'bar') 510 511 o.addObserver_forKeyPath_options_context_(observer, u'bar', 512 (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld), 513 0) 514 o.addObserver_forKeyPath_options_context_(observer, u'foo', 515 (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld), 516 0) 517 try: 518 o.bar = u"world" 519 self.assertEquals(o.bar, u"world") 520 521 o.foo = u"xxx" 522 self.assertEquals(o.foo, u"xxx") 523 finally: 524 o.removeObserver_forKeyPath_(observer, u"bar") 525 o.removeObserver_forKeyPath_(observer, u"foo") 526 527 self.assertEquals(len(observer.observed), 2) 528 529 self.assertEquals(observer.observed[0], 530 (u'bar', o, { u'kind': 1, u'new': u'world', u'old': u'bar' }, 0)) 531 self.assertEquals(observer.observed[1], 532 (u'foo', o, { u'kind': 1, u'new': u'xxx', u'old': u'foo' }, 0)) 533 534 del observer 535 del pool 536 537 before = DEALLOCS 538 del o 539 del outer_pool 540 self.assertEquals(DEALLOCS, before+1, u"Leaking an observed object") 541 542 def testObserving(self): 543 outer_pool = NSAutoreleasePool.alloc().init() 544 observer = PyObjCTestObserver.alloc().init() 545 546 o = PyObjCTestObserved1.alloc().init() 547 548 self.assertEquals(o.bar(), None) 549 o.setBar_(u"hello") 550 self.assertEquals(o.bar(), u"hello") 551 552 # See below 553 PyObjCTestObserved1.FOOBASE = u"base3" 554 try: 555 o.setFoo_(u"yyy") 556 self.assertEquals(o.foo(), u"base3yyy") 557 finally: 558 PyObjCTestObserved1.FOOBASE = u"base" 559 560 561 # XXX: To be debugged, when flags == 0 everything is fine, 562 # otherwise we leak a reference 563 o.addObserver_forKeyPath_options_context_(observer, u'bar', 564 (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld), 565 0) 566 o.addObserver_forKeyPath_options_context_(observer, u'foo', 567 (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld), 568 0) 569 570 try: 571 o.setBar_(u"world") 572 self.assertEquals(o.bar(), u"world") 573 574 o.setFoo_(u"xxx") 575 self.assertEquals(o.foo(), u"basexxx") 576 577 # Change a "class" attribute, and make sure the object sees 578 # that change (e.g. the fact that Cocoa changes the ISA pointer 579 # should be mostly invisible) 580 PyObjCTestObserved1.FOOBASE = u"base2" 581 582 o.setFoo_(u"yyy") 583 self.assertEquals(o.foo(), u"base2yyy") 584 585 finally: 586 o.removeObserver_forKeyPath_(observer, u"bar") 587 o.removeObserver_forKeyPath_(observer, u"foo") 588 PyObjCTestObserved1.FOOBASE = u"base" 589 590 self.assertEquals(len(observer.observed), 3) 591 592 self.assertEquals(observer.observed[0], 593 (u'bar', o, { u'kind': 1, u'new': u'world', u'old': u'hello' }, 0)) 594 self.assertEquals(observer.observed[1], 595 (u'foo', o, { u'kind': 1, u'new': u'basexxx', u'old': u'base3yyy' }, 0)) 596 self.assertEquals(observer.observed[2], 597 (u'foo', o, { u'kind': 1, u'new': u'base2yyy', u'old': u'basexxx' }, 0)) 598 self.assertEquals(o.bar(), u"world") 599 600 del observer 601 602 before = DEALLOCS 603 del o 604 del outer_pool 605 self.assertEquals(DEALLOCS, before+1, u"Leaking an observed object") 606 607 def testObserving2(self): 608 observer = PyObjCTestObserver.alloc().init() 609 610 o = PyObjCTestObserved1.alloc().init() 611 612 613 o.addObserver_forKeyPath_options_context_(observer, u'bar', 614 (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld), 615 0) 616 617 a = NSArray.arrayWithArray_([o]) 618 del o 619 o = a[0] 620 621 try: 622 PyObjCTestObserved1.FOOBASE = u"base2" 623 624 o.setFoo_(u"yyy") 625 self.assertEquals(o.foo(), u"base2yyy") 626 627 finally: 628 o.removeObserver_forKeyPath_(observer, u"bar") 629 PyObjCTestObserved1.FOOBASE = u"base" 630 631 self.assertEquals(len(observer.observed), 0) 632 633 @min_os_level('10.5') 634 def testReceiveObserved(self): 635 # Create an object in Objective-C, add an observer and then 636 # pass the object to Python. Unless we take special care the 637 # Python wrapper will have the wrong type (that of the 638 # internal helper class). 639 # 640 # NOTE: This test is known to fail on OSX 10.5 due to the way 641 # KVO is implemented there. 642 643 observer = PyObjCTestObserver.alloc().init() 644 o = STUB.createObservedOfClass_observer_keyPath_( 645 NSObject, observer, u"observationInfo") 646 647 try: 648 self.assertIsInstance(o, NSObject) 649 finally: 650 o.removeObserver_forKeyPath_(observer, u"observationInfo") 651 652 653 654if __name__ == "__main__": 655 main() 656