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