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