1from __future__ import unicode_literals
2from PyObjCTools.TestSupport import *
3
4import objc
5import sys
6from PyObjCTest.instanceVariables import ClassWithVariables
7
8NSObject = objc.lookUpClass('NSObject')
9NSAutoreleasePool = objc.lookUpClass('NSAutoreleasePool')
10
11class Base (object):
12    def __init__(self, ondel):
13        self.ondel = ondel
14
15    def __del__ (self):
16        self.ondel()
17
18class OCBase (NSObject):
19    def init_(self, ondel):
20        self.ondel = ondel
21
22    def __del__ (self):
23        self.ondel()
24
25class TestClass (NSObject):
26    idVar = objc.ivar('idVar')
27    idVar2 = objc.ivar('idVar2', b'@')
28    intVar = objc.ivar('intVar', objc._C_INT)
29    doubleVar = objc.ivar('doubleVar', objc._C_DBL)
30
31
32class TestInstanceVariables(TestCase):
33    def setUp(self):
34        self.object = TestClass.alloc().init()
35
36    def testID(self):
37        # Check that we can set and query attributes of type 'id'
38        self.assertEqual(self.object.idVar, None)
39        self.assertEqual(self.object.idVar2, None)
40
41        o = NSObject.alloc().init()
42
43        self.object.idVar = o
44        self.object.idVar2 = o
45
46        self.assertIs(self.object.idVar, o)
47        self.assertIs(self.object.idVar2, o)
48
49        self.object.idVar = "hello"
50        self.assertEqual(self.object.idVar, "hello")
51
52    def testInt(self):
53        # Check that we can set and query attributes of type 'int'
54        self.assertEqual(self.object.intVar, 0)
55
56        self.assertRaises(ValueError, lambda x: setattr(self.object, 'intVar', x), "h")
57
58        self.object.intVar = 42
59        self.assertEqual(self.object.intVar, 42)
60
61    def testDouble(self):
62        # Check that we can set and query attributes of type 'double'
63
64        # Can't rely on this for doubles...
65        #self.assertEqual(self.object.doubleVar, 0.0)
66        self.assertRaises(ValueError, lambda x: setattr(self.object, 'doubleVar', x), "h")
67        self.object.doubleVar = 42.0
68        self.assertAlmostEqual(self.object.doubleVar, 42.0)
69
70    def testLeak(self):
71        # Check that plain python objects are correctly released when
72        # they are no longer the value of an attribute
73        pool = NSAutoreleasePool.alloc().init()
74        self.deleted = 0
75        self.object.idVar = Base(lambda : setattr(self, 'deleted', 1))
76        self.object.idVar = None
77        del pool
78        self.assertEqual(self.deleted, 1)
79
80    def testLeak2(self):
81
82        self.deleted = 0
83
84        pool = NSAutoreleasePool.alloc().init()
85
86        self.object.idVar = Base(lambda : setattr(self, 'deleted', 1))
87        del self.object
88        del pool
89        self.assertEqual(self.deleted, 1)
90
91    def testOCLeak(self):
92        # Check that Objective-C objects are correctly released when
93        # they are no longer the value of an attribute
94        pool = NSAutoreleasePool.alloc().init()
95        self.deleted = 0
96        self.object.idVar = OCBase.alloc().init_(lambda : setattr(self, 'deleted', 1))
97        self.object.idVar = None
98        del pool
99        self.assertEqual(self.deleted, 1)
100
101    def testOCLeak2(self):
102        pool = NSAutoreleasePool.alloc().init()
103        self.deleted = 0
104        self.object.idVar = OCBase.alloc().init_(lambda : setattr(self, 'deleted', 1))
105        del self.object
106        del pool
107        self.assertEqual(self.deleted, 1)
108
109    def testDelete(self):
110        self.assertRaises(TypeError, delattr, self.object.idVar)
111
112
113class TestAllInstanceVariables (TestCase):
114    # Some tests for accessing any instance variable, even those not
115    # declared in python.
116
117    def testReading(self):
118        obj = ClassWithVariables.alloc().init()
119
120        getter = objc.getInstanceVariable
121
122        cls = getter(obj, 'isa')
123        self.assertIs(cls, type(obj))
124
125        self.assertEqual(getter(obj, 'intValue'), 42)
126        self.assertIsInstance(getter(obj, 'intValue'), int)
127
128        self.assertEqual(getter(obj, 'floatValue'), -10.055)
129        self.assertIsInstance(getter(obj, 'floatValue'), float)
130
131        self.assertEqual(getter(obj, 'charValue'), ord('a'))
132        self.assertIsInstance(getter(obj, 'charValue'), int)
133
134        self.assertEqual(getter(obj, 'strValue'), b"hello world")
135        self.assertIsInstance(getter(obj, 'strValue'), bytes)
136
137        self.assertIsInstance(getter(obj, 'objValue'), NSObject)
138
139        self.assertIsNone(getter(obj, 'nilValue'))
140
141        self.assertEqual(getter(obj, 'pyValue'), slice(1, 10, 4))
142        self.assertIsInstance(getter(obj, 'pyValue'), slice)
143
144        self.assertEqual(getter(obj, 'rectValue'), ((1, 2), (3, 4)))
145
146        self.assertRaises(AttributeError, getter, obj, "noSuchMember")
147
148    def testWriting(self):
149        obj = ClassWithVariables.alloc().init()
150
151        getter = objc.getInstanceVariable
152        setter = objc.setInstanceVariable
153
154        self.assertEqual(getter(obj, 'intValue'), 42)
155        setter(obj, 'intValue', 99)
156        self.assertEqual(getter(obj, 'intValue'), 99)
157
158        self.assertEqual(getter(obj, 'floatValue'), -10.055)
159        setter(obj, 'floatValue', 0.5)
160        self.assertEqual(getter(obj, 'floatValue'), 0.5)
161
162        self.assertEqual(getter(obj, 'charValue'), ord('a'))
163        setter(obj, 'charValue', b'b')
164        self.assertEqual(getter(obj, 'charValue'), ord('b'))
165        setter(obj, 'charValue', 10)
166        self.assertEqual(getter(obj, 'charValue'), 10)
167
168        self.assertEqual(getter(obj, 'strValue'), b"hello world")
169        setter(obj, 'strValue', b"foo bar")
170        self.assertEqual(getter(obj, 'strValue'), b"foo bar")
171        setter(obj, 'strValue', None)
172        self.assertEqual(getter(obj, 'strValue'), None)
173
174        o = NSObject.new()
175        self.assertIsNot(getter(obj, 'objValue'), o)
176        self.assertRaises(TypeError, setter, 'objValue', o)
177        self.assertIsNot(getter(obj, 'objValue'), o)
178        setter(obj, 'objValue', o, True)
179        self.assertIs(getter(obj, 'objValue'), o)
180
181        o2 = NSObject.new()
182        o2.retain()
183        self.assertIsNot(getter(obj, 'objValue'), o2)
184        setter(obj, 'objValue', o2, False)
185        self.assertIs(getter(obj, 'objValue'), o2)
186
187        self.assertEqual(getter(obj, 'pyValue'), slice(1, 10, 4))
188        setter(obj, 'pyValue', [1,2,3])
189        self.assertEqual(getter(obj, 'pyValue'), [1,2,3])
190
191        self.assertEqual(getter(obj, 'rectValue'), ((1, 2), (3, 4)))
192        setter(obj, 'rectValue', ((-4, -8), (2, 7)))
193        self.assertEqual(getter(obj, 'rectValue'), ((-4, -8), (2, 7)))
194
195        self.assertRaises(AttributeError, setter, obj, "noSuchMember", 'foo')
196
197    def testClassMod(self):
198        # It's scary as hell, but updating the class of an object does "work"
199        # (for some perverted interpretation of the word)
200
201        class DummyClass (NSObject):
202            __slots__ = ()
203
204        o = NSObject.alloc().init()
205        self.assertIsInstance(o, NSObject)
206        self.assertIsNotInstance(o, DummyClass)
207
208        objc.setInstanceVariable(o, "isa", DummyClass)
209        self.assertIsInstance(o, DummyClass)
210
211    def testDir(self):
212        obj = ClassWithVariables.alloc().init()
213
214        # Note: cannot check the exact contents of dir(), who knows
215        # what NSObject defines...
216        v = objc.listInstanceVariables(obj)
217        self.assertIn(('charValue', objc._C_CHR), v)
218        self.assertIn(('intValue', objc._C_INT), v)
219        self.assertIn(('isa', objc._C_CLASS), v)
220
221
222    def testAnonymousIvar(self):
223
224        class AnonIvarClass (NSObject):
225
226            var = objc.ivar()
227            var2 = objc.ivar(type=objc._C_DBL)
228
229            outlet = objc.IBOutlet()
230            self.assertTrue(outlet.__isOutlet__)
231            self.assertFalse(outlet.__isSlot__)
232
233        o = AnonIvarClass.alloc().init()
234        o.var = NSObject.alloc().init()
235
236        self.assertIsInstance(o.var, NSObject)
237
238        o.var2 = 4
239        self.assertIsInstance(o.var2, float)
240
241    def testNamedOutlet(self):
242        class NamedOutlet (NSObject):
243            outlet1 = objc.IBOutlet()
244            outlet2 = objc.IBOutlet("my_outlet")
245
246        all_outlets = {}
247
248        for name, tp in objc.listInstanceVariables(NamedOutlet):
249            all_outlets[name] = tp
250
251        self.assertEqual(all_outlets['outlet1'], objc._C_ID)
252        self.assertEqual(all_outlets['my_outlet'], objc._C_ID)
253
254        o = NamedOutlet.alloc().init()
255        self.assertTrue(hasattr(o, 'outlet1'))
256        self.assertTrue(hasattr(o, 'outlet2'))
257
258class TestStructConvenience (TestCase):
259    def test_using_convenience(self):
260        for name, typestr in [
261                ('bool', objc._C_BOOL),
262                ('char', objc._C_CHR),
263                ('int', objc._C_INT),
264                ('short', objc._C_SHT),
265                ('long', objc._C_LNG),
266                ('long_long', objc._C_LNG_LNG),
267                ('unsigned_char', objc._C_UCHR),
268                ('unsigned_int', objc._C_UINT),
269                ('unsigned_short', objc._C_USHT),
270                ('unsigned_long', objc._C_ULNG),
271                ('unsigned_long_long', objc._C_ULNG_LNG),
272                ('float', objc._C_FLT),
273                ('double', objc._C_DBL),
274                ('BOOL', objc._C_NSBOOL),
275                ('UniChar', objc._C_UNICHAR),
276                ('char_text', objc._C_CHAR_AS_TEXT),
277                ('char_int', objc._C_CHAR_AS_INT),
278            ]:
279            self.assertHasAttr(objc.ivar, name)
280            v = getattr(objc.ivar, name)()
281            self.assertIsInstance(v, objc.ivar)
282            self.assertEqual(v.__typestr__, typestr)
283            self.assertEqual(v.__name__, None)
284            self.assertFalse(v.__isOutlet__)
285            self.assertFalse(v.__isSlot__)
286
287            v = getattr(objc.ivar, name)('my_var')
288            self.assertIsInstance(v, objc.ivar)
289            self.assertEqual(v.__typestr__, typestr)
290            self.assertEqual(v.__name__, 'my_var')
291            self.assertFalse(v.__isOutlet__)
292            self.assertFalse(v.__isSlot__)
293
294if __name__ == '__main__':
295    main()
296