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