"""
Tests for the Key-Value Coding for hybrid python objects.
NOTE: Testcases here should be synchronized with the Key-Value Coding tests
in PyObjCTools.test.test_keyvalue and objc.test.test_keyvalue.
TODO:
- This test uses C code, that code should be added to this package!
- Tests that access properties in the parent Objective-C class!
- More key-error tests, the tests don't cover all relevant code yet.
"""
import objc
from PyObjCTools.TestSupport import *
import sys
from PyObjCTest.testhelper import PyObjC_TestClass3
from Foundation import *
class KeyValueClass1 (NSObject):
def init(self):
self = super(KeyValueClass1, self).init()
self.key3 = 3
self._key4 = u"4"
self.__private = u"private"
return self
def addMultiple(self):
self.multiple = KeyValueClass1.alloc().init()
self.multiple.level2 = KeyValueClass1.alloc().init()
self.multiple.level2.level3 = KeyValueClass1.alloc().init()
self.multiple.level2.level3.keyA = u"hello"
self.multiple.level2.level3.keyB = u"world"
def getKey1(self):
return 1
def key2(self):
return 2
def setKey4_(self, value):
self._key4 = value * 4
def setKey5_(self, value):
self.key5 = value * 5
def keyRaisingValueError(self):
raise ValueError, "42"
def keyRaisingNSUnknownKeyException(self):
return self.valueForKey_("thisKeyDoesNotExist")
def keyReturningSameSlector(self):
return self.keyReturningSameSelector
def keyReturningOtherSelector(self):
return self.getKey1
class KeyValueClass1Explicit (NSObject):
def init(self):
self = super(KeyValueClass1Explicit, self).init()
self._values = {}
self._values[u'key3'] = 3
self._values[u'key4'] = u"4"
self._values['_private'] = u"private"
return self
def addMultiple(self):
self._values["multiple"] = KeyValueClass1Explicit.alloc().init()
self._values["multiple"]._values["level2"] = KeyValueClass1Explicit.alloc().init()
self._values["multiple"]._values["level2"]._values["level3"] = KeyValueClass1Explicit.alloc().init()
self._values["multiple"]._values["level2"]._values["level3"]._values["keyA"] = u"hello"
self._values["multiple"]._values["level2"]._values["level3"]._values["keyB"] = u"world"
def valueForKey_(self, key):
if key == "key1":
return 1
elif key == "key2":
return 2
return self._values[key]
def storedValueForKey_(self, key):
return self.valueForKey_(key)
def setValue_forKey_(self, value, key):
if key == "key4":
value = value * 4
elif key == "key5":
value = value * 5
self._values[key] = value
def takeStoredValue_forKey_(self, value, key):
self.setValue_forKey_(value, key)
def takeValue_forKey_(self, value, key):
self.setValue_forKey_(value, key)
class KeyValueClass4 (NSObject):
__slots__ = ('foo', )
def init(self):
self = super(KeyValueClass4, self).init()
self.foo = u"foobar"
return self
# Definition for property 'bar'. Use odd names for the methods
# because the KeyValue support recognizes the usual names.
def read_bar(self):
return self.foo + self.foo
def write_bar (self, value):
self.foo = value + value
bar = property(read_bar, write_bar)
roprop = property(lambda self: u"read-only")
class KVOClass(NSObject):
def automaticallyNotifiesObserversForKey_(self, aKey):
return objc.NO
def test(self): return u"test"
class KeyValueObserver (NSObject):
def init(self):
self.observed = []
return self
def observeValueForKeyPath_ofObject_change_context_(
self, keyPath, object, change, context):
self.observed.append( (keyPath, object, change) )
class PyKeyValueCoding (TestCase):
def testNoPrivateVars(self):
# Private instance variables ('anObject.__value') are not accessible using
# key-value coding.
o = KeyValueClass1.alloc().init()
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o, u"private")
def testValueForKey(self):
o = KeyValueClass1.alloc().init()
o.addMultiple()
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"key1"), 1)
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"key2"), 2)
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"key3"), 3)
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"key4"), "4")
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"multiple"), o.multiple)
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o, u"nokey")
self.assertRaises(ValueError, STUB.keyValue_forObject_key_, 0, o,
u"keyRaisingValueError")
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o,
u"keyRaisingNSUnknownKeyException")
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o,
u"keyReturningSameSelector")
obj = STUB.keyValue_forObject_key_( 0, o, u"keyReturningOtherSelector")
self.assertIsInstance(obj, objc.selector)
self.assertEqual(obj.selector, b"getKey1")
self.assertIsObject(obj.self, o )
def testValueForKey2(self):
o = KeyValueClass4.alloc().init()
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"foo"), u"foobar")
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"bar"), u"foobarfoobar")
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"roprop"), u"read-only")
def testStoredValueForKey(self):
o = KeyValueClass1.alloc().init()
o.addMultiple()
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"key1"), 1)
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"key2"), 2)
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"key3"), 3)
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"key4"), "4")
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"multiple"), o.multiple)
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 2, o, u"nokey")
def testStoredValueForKey2(self):
o = KeyValueClass4.alloc().init()
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"foo"), u"foobar")
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"bar"), u"foobarfoobar")
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"roprop"), u"read-only")
def testValueForKeyPath(self):
o = KeyValueClass1.alloc().init()
o.addMultiple()
self.assertEqual(STUB.keyValue_forObject_key_(1, o, u"multiple"), o.multiple)
self.assertEqual(STUB.keyValue_forObject_key_(1, o, u"multiple.level2"), o.multiple.level2)
self.assertEqual(STUB.keyValue_forObject_key_(1, o, u"multiple.level2.level3.keyA"),
o.multiple.level2.level3.keyA)
self.assertEqual(STUB.keyValue_forObject_key_(1, o, u"multiple.level2.level3.keyB"),
o.multiple.level2.level3.keyB)
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 1, o, u"multiple.level2.nokey")
@max_os_level('10.5')
def testValuesForKeys(self):
o = KeyValueClass1.alloc().init()
self.assertEqual(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"} )
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 3, o, [u"key1", u"key3", u"nosuchkey"])
@max_os_level('10.5')
def testTakeValueForKey(self):
o = KeyValueClass1.alloc().init()
self.assertEqual(o.key3, 3)
STUB.setKeyValue_forObject_key_value_(0, o, u'key3', u'drie')
self.assertEqual(o.key3, u"drie")
self.assertEqual(o._key4, u"4")
STUB.setKeyValue_forObject_key_value_(0, o, u'key4', u'vier')
self.assert_(not hasattr(o, u"key4"))
self.assertEqual(o._key4, u"viervierviervier")
o.key5 = 1
STUB.setKeyValue_forObject_key_value_(0, o, u'key5', u'V')
self.assertEqual(o.key5, u"VVVVV")
self.assert_(not hasattr(o, u'key9'))
STUB.setKeyValue_forObject_key_value_(0, o, u'key9', u'IX')
self.assert_(hasattr(o, u'key9'))
self.assertEqual(o.key9, u'IX')
@max_os_level('10.5')
def testTakeValueForKey2(self):
o = KeyValueClass4.alloc().init()
self.assertEqual(o.foo, u"foobar")
STUB.setKeyValue_forObject_key_value_(0, o, u'foo', u'FOO')
self.assertEqual(o.foo, u"FOO")
self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 0, o, u'key9', u'IX')
def testTakeStoredValueForKey(self):
o = KeyValueClass1.alloc().init()
self.assertEqual(o.key3, 3)
STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'drie')
self.assertEqual(o.key3, u"drie")
self.assertEqual(o._key4, u"4")
STUB.setKeyValue_forObject_key_value_(2, o, u'key4', u'vier')
self.assertEqual(o._key4, u"viervierviervier")
o.key5 = 1
STUB.setKeyValue_forObject_key_value_(2, o, u'key5', u'V')
self.assertEqual(o.key5, u"VVVVV")
self.assert_(not hasattr(o, u'key9'))
STUB.setKeyValue_forObject_key_value_(2, o, u'key9', u'IX')
self.assert_(hasattr(o, u'key9'))
self.assertEqual(o.key9, u'IX')
def testStoredTakeValueForKey2(self):
o = KeyValueClass4.alloc().init()
self.assertEqual(o.foo, u"foobar")
STUB.setKeyValue_forObject_key_value_(2, o, u'foo', u'FOO')
self.assertEqual(o.foo, u"FOO")
self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 2, o, u'key9', u'IX')
self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 2, o, u'roprop', u'IX')
@max_os_level('10.5')
def testTakeValuesFromDictionary(self):
o = KeyValueClass1.alloc().init()
self.assertEqual(o.key3, 3)
self.assertEqual(o._key4, u"4")
o.key5 = 1
self.assert_(not hasattr(o, u'key9'))
STUB.setKeyValue_forObject_key_value_(3, o, None,
{
u'key3': u'drie',
u'key4': u'vier',
u'key5': u'V',
u'key9': u'IX',
})
self.assertEqual(o.key3, u"drie")
self.assertEqual(o._key4, u"viervierviervier")
self.assertEqual(o.key5, u"VVVVV")
self.assert_(hasattr(o, u'key9'))
self.assertEqual(o.key9, u'IX')
@max_os_level('10.5')
def testTakeValuesFromDictionary2(self):
o = KeyValueClass4.alloc().init()
self.assertEqual(o.foo, u"foobar")
STUB.setKeyValue_forObject_key_value_(3, o, None, { u'foo': u'FOO' })
self.assertEqual(o.foo, u"FOO")
self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 3, o, None, { u'key9': u'IX' })
self.assertRaises(KeyError, STUB.setKeyValue_forObject_key_value_, 3, o, None, { u'roprop': u'IX' })
@max_os_level('10.5')
def testTakeValueForKeyPath(self):
o = KeyValueClass1.alloc().init()
o.addMultiple()
self.assertEqual(o.multiple.level2.level3.keyA, u"hello")
self.assertEqual(o.multiple.level2.level3.keyB, u"world")
STUB.setKeyValue_forObject_key_value_(1, o, u"multiple.level2.level3.keyA", u"KeyAValue")
self.assertEqual(o.multiple.level2.level3.keyA, u"KeyAValue")
STUB.setKeyValue_forObject_key_value_(1, o, u"multiple.level2.level3.keyB", 9.999)
self.assertEqual(o.multiple.level2.level3.keyB, 9.999)
if hasattr(NSObject, u"willChangeValueForKey_"):
# NSKeyValueObserving is only available on Panther and beyond
def testKVO1(self):
o = KVOClass.alloc().init()
o.addObserver_forKeyPath_options_context_(self, u"test", 0, None)
o.removeObserver_forKeyPath_(self, u"test")
def testKVO2(self):
"""
Check if observations work for python-based keys on ObjC classes
"""
observer = KeyValueObserver.alloc().init()
self.assertEqual(observer.observed, [])
o = KeyValueClass1.alloc().init()
o.addObserver_forKeyPath_options_context_(observer, u"key3", 0, 0)
try:
STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'drie')
self.assertEqual(o.key3, u"drie")
self.assertEqual(len(observer.observed), 1)
keyPath, object, change = observer.observed[0]
self.assertEqual(keyPath, u"key3")
self.assert_(object is o)
self.assertEqual(change, {NSKeyValueChangeKindKey: 1 })
finally:
o.removeObserver_forKeyPath_(observer, u'key3')
def testKVO3(self):
"""
Check if observations work for python-based keys on ObjC classes
"""
observer = KeyValueObserver.alloc().init()
self.assertEqual(observer.observed, [])
o = KeyValueClass1.alloc().init()
STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'three')
o.addObserver_forKeyPath_options_context_(observer, u"key3",
NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld,
0)
try:
STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'drie')
self.assertEqual(o.key3, u"drie")
self.assertEqual(len(observer.observed), 1)
keyPath, object, change = observer.observed[0]
self.assertEqual(keyPath, u"key3")
self.assert_(object is o)
self.assertEqual(change,
{
NSKeyValueChangeKindKey:1,
NSKeyValueChangeNewKey:u'drie',
NSKeyValueChangeOldKey:u'three'
})
finally:
o.removeObserver_forKeyPath_(observer, u'key3')
class PyKeyValueCodingExplicit (TestCase):
def testValueForKey(self):
o = KeyValueClass1Explicit.alloc().init()
o.addMultiple()
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"key1"), 1)
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"key2"), 2)
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"key3"), 3)
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"key4"), "4")
self.assertEqual(STUB.keyValue_forObject_key_(0, o, u"multiple"), o._values['multiple'])
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 0, o, u"nokey")
def testStoredValueForKey(self):
o = KeyValueClass1Explicit.alloc().init()
o.addMultiple()
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"key1"), 1)
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"key2"), 2)
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"key3"), 3)
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"key4"), "4")
self.assertEqual(STUB.keyValue_forObject_key_(2, o, u"multiple"), o._values['multiple'])
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 2, o, u"nokey")
def testValueForKeyPath(self):
o = KeyValueClass1Explicit.alloc().init()
o.addMultiple()
self.assertEqual(STUB.keyValue_forObject_key_(1, o, u"multiple"), o._values['multiple'])
self.assertEqual(STUB.keyValue_forObject_key_(1, o, u"multiple.level2"), o._values['multiple']._values['level2'])
self.assertEqual(STUB.keyValue_forObject_key_(1, o, u"multiple.level2.level3.keyA"),
o._values['multiple']._values['level2']._values['level3']._values['keyA'])
self.assertEqual(STUB.keyValue_forObject_key_(1, o, u"multiple.level2.level3.keyB"),
o._values['multiple']._values['level2']._values['level3']._values['keyB'])
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 1, o, u"multiple.level2.nokey")
@max_os_level('10.5')
def testValuesForKeys(self):
o = KeyValueClass1Explicit.alloc().init()
self.assertEqual(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"} )
self.assertRaises(KeyError, STUB.keyValue_forObject_key_, 3, o, [u"key1", u"key3", u"nosuchkey"])
def testTakeValueForKey(self):
o = KeyValueClass1Explicit.alloc().init()
self.assertEqual(o._values['key3'], 3)
STUB.setKeyValue_forObject_key_value_(0, o, u'key3', u'drie')
self.assertEqual(o._values['key3'], u"drie")
self.assertEqual(o._values['key4'], u"4")
STUB.setKeyValue_forObject_key_value_(0, o, u'key4', u'vier')
self.assert_(not hasattr(o, u"key4"))
self.assertEqual(o._values['key4'], u"viervierviervier")
o._values['key5'] = 1
STUB.setKeyValue_forObject_key_value_(0, o, u'key5', u'V')
self.assertEqual(o._values['key5'], u"VVVVV")
self.assert_(not hasattr(o, u'key9'))
self.assert_('key9' not in o._values)
STUB.setKeyValue_forObject_key_value_(0, o, u'key9', u'IX')
self.assert_(not hasattr(o, u'key9'))
self.assert_('key9' in o._values)
self.assertEqual(o._values['key9'], u'IX')
def testTakeStoredValueForKey(self):
o = KeyValueClass1Explicit.alloc().init()
self.assertEqual(o._values['key3'], 3)
STUB.setKeyValue_forObject_key_value_(2, o, u'key3', u'drie')
self.assertEqual(o._values['key3'], u"drie")
self.assertEqual(o._values['key4'], u"4")
STUB.setKeyValue_forObject_key_value_(2, o, u'key4', u'vier')
self.assertEqual(o._values['key4'], u"viervierviervier")
o.key5 = 1
STUB.setKeyValue_forObject_key_value_(2, o, u'key5', u'V')
self.assertEqual(o._values['key5'], u"VVVVV")
self.assert_('key9' not in o._values)
STUB.setKeyValue_forObject_key_value_(2, o, u'key9', u'IX')
self.assert_('key9' in o._values)
self.assertEqual(o._values['key9'], u'IX')
@max_os_level('10.5')
def testTakeValuesFromDictionary(self):
o = KeyValueClass1Explicit.alloc().init()
self.assertEqual(o._values['key3'], 3)
self.assertEqual(o._values['key4'], u"4")
o._values['key5'] = 1
self.assert_('key9' not in o._values)
STUB.setKeyValue_forObject_key_value_(3, o, None,
{
u'key3': u'drie',
u'key4': u'vier',
u'key5': u'V',
u'key9': u'IX',
})
self.assertEqual(o._values['key3'], u"drie")
self.assertEqual(o._values['key4'], u"viervierviervier")
self.assertEqual(o._values['key5'], u"VVVVV")
self.assertEqual(o._values['key9'], u'IX')
@max_os_level('10.5')
def testTakeValueForKeyPath(self):
o = KeyValueClass1Explicit.alloc().init()
o.addMultiple()
self.assertEqual(o._values['multiple']._values['level2']._values['level3']._values['keyA'], u"hello")
self.assertEqual(o._values['multiple']._values['level2']._values['level3']._values['keyB'], u"world")
STUB.setKeyValue_forObject_key_value_(1, o, u"multiple.level2.level3.keyA", u"KeyAValue")
self.assertEqual(o._values['multiple']._values['level2']._values['level3']._values['keyA'], u"KeyAValue")
STUB.setKeyValue_forObject_key_value_(1, o, u"multiple.level2.level3.keyB", 9.999)
self.assertEqual(o._values['multiple']._values['level2']._values['level3']._values['keyB'], 9.999)
class TestBaseExceptions (TestCase):
"""
Check that NSObject implementation of Key-Value coding raises the
exception that we expect it to raise.
"""
def testValueForKey(self):
o = NSObject.alloc().init()
self.assertRaises(KeyError, o.valueForKey_, u"unknownKey")
def testStoredValueForKey(self):
o = NSObject.alloc().init()
self.assertRaises(KeyError, o.storedValueForKey_, u"unknownKey")
def testTakeStoredValue(self):
o = NSObject.alloc().init()
self.assertRaises(KeyError,
o.takeStoredValue_forKey_, u"value", u"unknownKey")
if __name__ == "__main__":
main()
|