1"""
2Tests for the Key-Value Coding for hybrid python objects.
3
4NOTE: Testcases here should be synchronized with the Key-Value Coding tests
5in PyObjCTools.test.test_keyvalue and objc.test.test_keyvalue.
6
7TODO:
8    - This test uses C code, that code should be added to this package!
9    - Tests that access properties in the parent Objective-C class!
10    - More key-error tests, the tests don't cover all relevant code yet.
11"""
12import objc
13from PyObjCTools.TestSupport import *
14import sys
15from PyObjCTest.testhelper import PyObjC_TestClass3 as STUB
16from Foundation import *
17
18class KeyValueClass1 (NSObject):
19    def init(self):
20        self = super(KeyValueClass1, self).init()
21        self.key3 = 3
22        self._key4 = u"4"
23        self.__private = u"private"
24        return self
25
26    def addMultiple(self):
27        self.multiple = KeyValueClass1.alloc().init()
28        self.multiple.level2 = KeyValueClass1.alloc().init()
29        self.multiple.level2.level3 = KeyValueClass1.alloc().init()
30        self.multiple.level2.level3.keyA = u"hello"
31        self.multiple.level2.level3.keyB = u"world"
32
33    def getKey1(self):
34        return 1
35
36    def key2(self):
37        return 2
38
39    def setKey4_(self, value):
40        self._key4 = value * 4
41
42    def setKey5_(self, value):
43        self.key5 = value * 5
44
45    def keyRaisingValueError(self):
46        raise ValueError, "42"
47
48    def keyRaisingNSUnknownKeyException(self):
49        return self.valueForKey_("thisKeyDoesNotExist")
50
51    def keyReturningSameSlector(self):
52        return self.keyReturningSameSelector
53
54    def keyReturningOtherSelector(self):
55        return self.getKey1
56
57class KeyValueClass1Explicit (NSObject):
58    def init(self):
59        self = super(KeyValueClass1Explicit, self).init()
60        self._values = {}
61        self._values[u'key3'] = 3
62        self._values[u'key4'] = u"4"
63        self._values['_private'] = u"private"
64        return self
65
66    def addMultiple(self):
67        self._values["multiple"] = KeyValueClass1Explicit.alloc().init()
68        self._values["multiple"]._values["level2"] = KeyValueClass1Explicit.alloc().init()
69        self._values["multiple"]._values["level2"]._values["level3"] = KeyValueClass1Explicit.alloc().init()
70        self._values["multiple"]._values["level2"]._values["level3"]._values["keyA"] = u"hello"
71        self._values["multiple"]._values["level2"]._values["level3"]._values["keyB"] = u"world"
72
73    def valueForKey_(self, key):
74        if key == "key1":
75            return 1
76
77        elif key == "key2":
78            return 2
79
80        return self._values[key]
81
82    def storedValueForKey_(self, key):
83        return self.valueForKey_(key)
84
85    def setValue_forKey_(self, value, key):
86        if key == "key4":
87            value = value * 4
88        elif key == "key5":
89            value = value * 5
90
91        self._values[key] = value
92
93    def takeStoredValue_forKey_(self, value, key):
94        self.setValue_forKey_(value, key)
95
96    def takeValue_forKey_(self, value, key):
97        self.setValue_forKey_(value, key)
98
99class KeyValueClass4 (NSObject):
100    __slots__ = ('foo', )
101
102    def init(self):
103        self = super(KeyValueClass4, self).init()
104        self.foo = u"foobar"
105        return self
106
107    # Definition for property 'bar'. Use odd names for the methods
108    # because the KeyValue support recognizes the usual names.
109    def read_bar(self):
110        return self.foo + self.foo
111
112    def write_bar (self, value):
113        self.foo = value + value
114
115    bar = property(read_bar, write_bar)
116
117    roprop = property(lambda self: u"read-only")
118
119class KVOClass(NSObject):
120    def automaticallyNotifiesObserversForKey_(self, aKey):
121        return objc.NO
122
123    def test(self): return u"test"
124
125
126class KeyValueObserver (NSObject):
127    def init(self):
128        self.observed = []
129        return self
130
131    def observeValueForKeyPath_ofObject_change_context_(
132            self, keyPath, object, change, context):
133        self.observed.append( (keyPath, object, change) )
134
135
136class PyKeyValueCoding (TestCase):
137    def testNoPrivateVars(self):
138        # Private instance variables ('anObject.__value') are not accessible using
139        # key-value coding.
140        o = KeyValueClass1.alloc().init()
141        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o, u"private")
142
143    def testValueForKey(self):
144        o = KeyValueClass1.alloc().init()
145        o.addMultiple()
146
147        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"key1"), 1)
148
149        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"key2"), 2)
150        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"key3"), 3)
151        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"key4"), "4")
152        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"multiple"), o.multiple)
153
154        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o, u"nokey")
155
156        self.assertRaises(ValueError, STUB.keyValue_forObject_key_, 0, o,
157                u"keyRaisingValueError")
158        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o,
159                u"keyRaisingNSUnknownKeyException")
160        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o,
161                u"keyReturningSameSelector")
162
163        obj = STUB.keyValue_forObject_key_( 0, o, u"keyReturningOtherSelector")
164        self.failUnless( isinstance(obj, objc.selector) )
165        self.assertEquals(obj.selector, "getKey1")
166        self.failUnless( obj.self is o )
167
168
169    def testValueForKey2(self):
170        o = KeyValueClass4.alloc().init()
171
172        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"foo"), u"foobar")
173        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"bar"), u"foobarfoobar")
174        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"roprop"), u"read-only")
175
176
177    def testStoredValueForKey(self):
178        o = KeyValueClass1.alloc().init()
179        o.addMultiple()
180
181        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"key1"), 1)
182        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"key2"), 2)
183        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"key3"), 3)
184        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"key4"), "4")
185        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"multiple"), o.multiple)
186
187        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 2, o, u"nokey")
188
189    def testStoredValueForKey2(self):
190        o = KeyValueClass4.alloc().init()
191
192        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"foo"), u"foobar")
193        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"bar"), u"foobarfoobar")
194        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"roprop"), u"read-only")
195
196    def testValueForKeyPath(self):
197        o = KeyValueClass1.alloc().init()
198        o.addMultiple()
199
200        self.assertEquals(STUB.keyValue_forObject_key_(1, o, u"multiple"), o.multiple)
201        self.assertEquals(STUB.keyValue_forObject_key_(1, o, u"multiple.level2"), o.multiple.level2)
202        self.assertEquals(STUB.keyValue_forObject_key_(1, o, u"multiple.level2.level3.keyA"),
203            o.multiple.level2.level3.keyA)
204        self.assertEquals(STUB.keyValue_forObject_key_(1, o, u"multiple.level2.level3.keyB"),
205            o.multiple.level2.level3.keyB)
206
207        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 1, o, u"multiple.level2.nokey")
208
209    def testValuesForKeys(self):
210        o = KeyValueClass1.alloc().init()
211
212        self.assertEquals(STUB.keyValue_forObject_key_(3, o, [u"key1", u"key2", u"key3", u"key4"]), { u"key1":1, u"key2":2, u"key3": 3, u"key4": u"4"} )
213
214        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 3, o, [u"key1", u"key3", u"nosuchkey"])
215
216    def testTakeValueForKey(self):
217        o = KeyValueClass1.alloc().init()
218
219        self.assertEquals(o.key3, 3)
220        STUB.setKeyValue_forObject_key_value_(0, o, u'key3', u'drie')
221        self.assertEquals(o.key3, u"drie")
222
223        self.assertEquals(o._key4, u"4")
224        STUB.setKeyValue_forObject_key_value_(0, o, u'key4', u'vier')
225        self.assert_(not hasattr(o, u"key4"))
226        self.assertEquals(o._key4, u"viervierviervier")
227
228        o.key5 = 1
229        STUB.setKeyValue_forObject_key_value_(0, o, u'key5', u'V')
230        self.assertEquals(o.key5, u"VVVVV")
231
232        self.assert_(not hasattr(o, u'key9'))
233        STUB.setKeyValue_forObject_key_value_(0, o, u'key9', u'IX')
234        self.assert_(hasattr(o, u'key9'))
235        self.assertEquals(o.key9, u'IX')
236
237    def testTakeValueForKey2(self):
238        o = KeyValueClass4.alloc().init()
239
240        self.assertEquals(o.foo, u"foobar")
241        STUB.setKeyValue_forObject_key_value_(0, o, u'foo', u'FOO')
242        self.assertEquals(o.foo, u"FOO")
243
244        self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 0, o, u'key9', u'IX')
245
246
247    def testTakeStoredValueForKey(self):
248        o = KeyValueClass1.alloc().init()
249
250        self.assertEquals(o.key3, 3)
251        STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'drie')
252        self.assertEquals(o.key3, u"drie")
253
254        self.assertEquals(o._key4, u"4")
255        STUB.setKeyValue_forObject_key_value_(2, o, u'key4', u'vier')
256        self.assertEquals(o._key4, u"viervierviervier")
257
258        o.key5 = 1
259        STUB.setKeyValue_forObject_key_value_(2, o, u'key5', u'V')
260        self.assertEquals(o.key5, u"VVVVV")
261
262        self.assert_(not hasattr(o, u'key9'))
263        STUB.setKeyValue_forObject_key_value_(2, o, u'key9', u'IX')
264        self.assert_(hasattr(o, u'key9'))
265        self.assertEquals(o.key9, u'IX')
266
267    def testStoredTakeValueForKey2(self):
268        o = KeyValueClass4.alloc().init()
269
270        self.assertEquals(o.foo, u"foobar")
271        STUB.setKeyValue_forObject_key_value_(2, o, u'foo', u'FOO')
272        self.assertEquals(o.foo, u"FOO")
273
274        self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 2, o, u'key9', u'IX')
275        self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 2, o, u'roprop', u'IX')
276
277    def testTakeValuesFromDictionary(self):
278        o = KeyValueClass1.alloc().init()
279
280        self.assertEquals(o.key3, 3)
281        self.assertEquals(o._key4, u"4")
282        o.key5 = 1
283        self.assert_(not hasattr(o, u'key9'))
284
285        STUB.setKeyValue_forObject_key_value_(3, o, None,
286            {
287                u'key3': u'drie',
288                u'key4': u'vier',
289                u'key5': u'V',
290                u'key9': u'IX',
291            })
292
293        self.assertEquals(o.key3, u"drie")
294        self.assertEquals(o._key4, u"viervierviervier")
295        self.assertEquals(o.key5, u"VVVVV")
296        self.assert_(hasattr(o, u'key9'))
297        self.assertEquals(o.key9, u'IX')
298
299    def testTakeValuesFromDictionary2(self):
300        o = KeyValueClass4.alloc().init()
301
302        self.assertEquals(o.foo, u"foobar")
303        STUB.setKeyValue_forObject_key_value_(3, o, None, { u'foo': u'FOO' })
304        self.assertEquals(o.foo, u"FOO")
305
306        self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 3, o, None, { u'key9':  u'IX' })
307        self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 3, o, None, { u'roprop':  u'IX' })
308
309    def testTakeValueForKeyPath(self):
310        o = KeyValueClass1.alloc().init()
311        o.addMultiple()
312
313        self.assertEquals(o.multiple.level2.level3.keyA, u"hello")
314        self.assertEquals(o.multiple.level2.level3.keyB, u"world")
315
316        STUB.setKeyValue_forObject_key_value_(1, o, u"multiple.level2.level3.keyA", u"KeyAValue")
317        self.assertEquals(o.multiple.level2.level3.keyA, u"KeyAValue")
318
319        STUB.setKeyValue_forObject_key_value_(1, o, u"multiple.level2.level3.keyB", 9.999)
320        self.assertEquals(o.multiple.level2.level3.keyB, 9.999)
321
322    if hasattr(NSObject, u"willChangeValueForKey_"):
323        # NSKeyValueObserving is only available on Panther and beyond
324        def testKVO1(self):
325            o = KVOClass.alloc().init()
326            o.addObserver_forKeyPath_options_context_(self, u"test", 0, None)
327            o.removeObserver_forKeyPath_(self, u"test")
328
329        def testKVO2(self):
330            """
331            Check if observations work for python-based keys on ObjC classes
332            """
333            observer = KeyValueObserver.alloc().init()
334            self.assertEquals(observer.observed, [])
335
336            o = KeyValueClass1.alloc().init()
337
338            o.addObserver_forKeyPath_options_context_(observer, u"key3", 0, 0)
339            try:
340                STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'drie')
341                self.assertEquals(o.key3, u"drie")
342
343                self.assertEquals(len(observer.observed), 1)
344
345                keyPath, object, change = observer.observed[0]
346                self.assertEquals(keyPath, u"key3")
347                self.assert_(object is o)
348                self.assertEquals(change, {NSKeyValueChangeKindKey: 1 })
349
350            finally:
351                o.removeObserver_forKeyPath_(observer, u'key3')
352
353        def testKVO3(self):
354            """
355            Check if observations work for python-based keys on ObjC classes
356            """
357            observer = KeyValueObserver.alloc().init()
358            self.assertEquals(observer.observed, [])
359
360            o = KeyValueClass1.alloc().init()
361            STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'three')
362
363            o.addObserver_forKeyPath_options_context_(observer, u"key3",
364                    NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld,
365                    0)
366            try:
367                STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'drie')
368                self.assertEquals(o.key3, u"drie")
369
370                self.assertEquals(len(observer.observed), 1)
371
372                keyPath, object, change = observer.observed[0]
373                self.assertEquals(keyPath, u"key3")
374                self.assert_(object is o)
375                self.assertEquals(change,
376                    {
377                        NSKeyValueChangeKindKey:1,
378                        NSKeyValueChangeNewKey:u'drie',
379                        NSKeyValueChangeOldKey:u'three'
380                    })
381
382            finally:
383                o.removeObserver_forKeyPath_(observer, u'key3')
384
385class PyKeyValueCodingExplicit (TestCase):
386
387    def testValueForKey(self):
388        o = KeyValueClass1Explicit.alloc().init()
389        o.addMultiple()
390
391        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"key1"), 1)
392
393        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"key2"), 2)
394        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"key3"), 3)
395        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"key4"), "4")
396        self.assertEquals(STUB.keyValue_forObject_key_(0, o, u"multiple"), o._values['multiple'])
397
398        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o, u"nokey")
399
400    def testStoredValueForKey(self):
401        o = KeyValueClass1Explicit.alloc().init()
402        o.addMultiple()
403
404        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"key1"), 1)
405        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"key2"), 2)
406        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"key3"), 3)
407        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"key4"), "4")
408        self.assertEquals(STUB.keyValue_forObject_key_(2, o, u"multiple"), o._values['multiple'])
409
410        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 2, o, u"nokey")
411
412    def testValueForKeyPath(self):
413        o = KeyValueClass1Explicit.alloc().init()
414        o.addMultiple()
415
416        self.assertEquals(STUB.keyValue_forObject_key_(1, o, u"multiple"), o._values['multiple'])
417        self.assertEquals(STUB.keyValue_forObject_key_(1, o, u"multiple.level2"), o._values['multiple']._values['level2'])
418        self.assertEquals(STUB.keyValue_forObject_key_(1, o, u"multiple.level2.level3.keyA"),
419            o._values['multiple']._values['level2']._values['level3']._values['keyA'])
420        self.assertEquals(STUB.keyValue_forObject_key_(1, o, u"multiple.level2.level3.keyB"),
421            o._values['multiple']._values['level2']._values['level3']._values['keyB'])
422
423        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 1, o, u"multiple.level2.nokey")
424
425    def testValuesForKeys(self):
426        o = KeyValueClass1Explicit.alloc().init()
427
428        self.assertEquals(STUB.keyValue_forObject_key_(3, o, [u"key1", u"key2", u"key3", u"key4"]), { u"key1":1, u"key2":2, u"key3": 3, u"key4": u"4"} )
429
430        self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 3, o, [u"key1", u"key3", u"nosuchkey"])
431
432    def testTakeValueForKey(self):
433        o = KeyValueClass1Explicit.alloc().init()
434
435        self.assertEquals(o._values['key3'], 3)
436        STUB.setKeyValue_forObject_key_value_(0, o, u'key3', u'drie')
437        self.assertEquals(o._values['key3'], u"drie")
438
439        self.assertEquals(o._values['key4'], u"4")
440        STUB.setKeyValue_forObject_key_value_(0, o, u'key4', u'vier')
441        self.assert_(not hasattr(o, u"key4"))
442        self.assertEquals(o._values['key4'], u"viervierviervier")
443
444        o._values['key5'] = 1
445        STUB.setKeyValue_forObject_key_value_(0, o, u'key5', u'V')
446        self.assertEquals(o._values['key5'], u"VVVVV")
447
448        self.assert_(not hasattr(o, u'key9'))
449        self.assert_('key9' not in o._values)
450        STUB.setKeyValue_forObject_key_value_(0, o, u'key9', u'IX')
451        self.assert_(not hasattr(o, u'key9'))
452        self.assert_('key9' in o._values)
453        self.assertEquals(o._values['key9'], u'IX')
454
455    def testTakeStoredValueForKey(self):
456        o = KeyValueClass1Explicit.alloc().init()
457
458        self.assertEquals(o._values['key3'], 3)
459        STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'drie')
460        self.assertEquals(o._values['key3'], u"drie")
461
462        self.assertEquals(o._values['key4'], u"4")
463        STUB.setKeyValue_forObject_key_value_(2, o, u'key4', u'vier')
464        self.assertEquals(o._values['key4'], u"viervierviervier")
465
466        o.key5 = 1
467        STUB.setKeyValue_forObject_key_value_(2, o, u'key5', u'V')
468        self.assertEquals(o._values['key5'], u"VVVVV")
469
470        self.assert_('key9' not in o._values)
471        STUB.setKeyValue_forObject_key_value_(2, o, u'key9', u'IX')
472        self.assert_('key9' in o._values)
473        self.assertEquals(o._values['key9'], u'IX')
474
475    def testTakeValuesFromDictionary(self):
476        o = KeyValueClass1Explicit.alloc().init()
477
478        self.assertEquals(o._values['key3'], 3)
479        self.assertEquals(o._values['key4'], u"4")
480        o._values['key5'] = 1
481        self.assert_('key9' not in o._values)
482
483        STUB.setKeyValue_forObject_key_value_(3, o, None,
484            {
485                u'key3': u'drie',
486                u'key4': u'vier',
487                u'key5': u'V',
488                u'key9': u'IX',
489            })
490
491        self.assertEquals(o._values['key3'], u"drie")
492        self.assertEquals(o._values['key4'], u"viervierviervier")
493        self.assertEquals(o._values['key5'], u"VVVVV")
494        self.assertEquals(o._values['key9'], u'IX')
495
496    def testTakeValueForKeyPath(self):
497        o = KeyValueClass1Explicit.alloc().init()
498        o.addMultiple()
499
500        self.assertEquals(o._values['multiple']._values['level2']._values['level3']._values['keyA'], u"hello")
501        self.assertEquals(o._values['multiple']._values['level2']._values['level3']._values['keyB'], u"world")
502
503        STUB.setKeyValue_forObject_key_value_(1, o, u"multiple.level2.level3.keyA", u"KeyAValue")
504        self.assertEquals(o._values['multiple']._values['level2']._values['level3']._values['keyA'], u"KeyAValue")
505
506        STUB.setKeyValue_forObject_key_value_(1, o, u"multiple.level2.level3.keyB", 9.999)
507        self.assertEquals(o._values['multiple']._values['level2']._values['level3']._values['keyB'], 9.999)
508
509
510class TestBaseExceptions (TestCase):
511    """
512    Check that NSObject implementation of Key-Value coding raises the
513    exception that we expect it to raise.
514    """
515    def testValueForKey(self):
516        o = NSObject.alloc().init()
517
518        self.assertRaises(KeyError, o.valueForKey_, u"unknownKey")
519
520    def testStoredValueForKey(self):
521        o = NSObject.alloc().init()
522
523        self.assertRaises(KeyError, o.storedValueForKey_, u"unknownKey")
524
525    def testTakeStoredValue(self):
526        o = NSObject.alloc().init()
527
528        self.assertRaises(KeyError,
529            o.takeStoredValue_forKey_, u"value", u"unknownKey")
530
531
532
533if __name__ == "__main__":
534    main()
535