1from PyObjCTools.TestSupport import *
2import objc
3from PyObjCTest.fnd import *
4
5objc.registerMetaDataForSelector(
6        b"NSObject", b"validateValue:forKey:error:",
7        dict(
8            arguments={
9                2: dict(type_modifier=objc._C_INOUT),
10                4: dict(type_modifier=objc._C_OUT),
11            },
12        ))
13
14
15class OCCopy (NSObject):
16    def copy(self):
17        return self.copyWithZone_(None)
18
19    def copyWithZone_(self, zone):
20        v = OCCopy.allocWithZone_(zone).init()
21        return v
22
23class OCObserve (NSObject):
24    def init(self):
25        self = super(OCObserve, self).init()
26        self.values = []
27        self.registrations = []
28        return self
29
30    def register(self, object, keypath):
31        object.addObserver_forKeyPath_options_context_(
32                self, keypath, 0x3, None)
33        self.registrations.append((object, keypath))
34
35    def unregister(self, object, keypath):
36        object.removeObserver_forKeyPath_(self, keypath)
37
38    def observeValueForKeyPath_ofObject_change_context_(
39            self, keypath, object, change, context):
40        self.values.append((object, keypath, change))
41
42    def __enter__(self):
43        return self
44
45    def __exit__(self, type, value, traceback):
46        for o, k in self.registrations:
47            self.unregister(o, k)
48        self.registrations = []
49
50
51class TestObjectProperty (TestCase):
52    def testCreation(self):
53        class OCTestObjectProperty1 (NSObject):
54            p1 = objc.object_property()
55            p2 = objc.object_property(copy=True)
56            p3 = objc.object_property(read_only=True)
57            p4 = objc.object_property(ivar='myp4')
58            p5 = objc.object_property(typestr=objc._C_INT)
59            p6 = objc.object_property(typestr=objc._C_DBL)
60
61
62        o = OCTestObjectProperty1.alloc().init()
63
64        self.assertTrue(o.respondsToSelector(b'p1'))
65        self.assertTrue(o.respondsToSelector(b'setP1:'))
66
67        v = OCCopy.alloc().init()
68        o.p1 = v
69        self.assertIs(o.p1, v)
70        self.assertIs(o._p1, v)
71
72        self.assertTrue(o.respondsToSelector(b'p2'))
73        self.assertTrue(o.respondsToSelector(b'setP2:'))
74
75        o.p2 = v
76        self.assertIsInstance(o.p2, OCCopy)
77        self.assertIsNot(o.p2, v)
78        self.assertIsNot(o._p2, v)
79
80        self.assertTrue(o.respondsToSelector(b'p3'))
81        self.assertFalse(o.respondsToSelector(b'setP3:'))
82
83        o._p3 = v
84        self.assertIs(o.p3, v)
85
86
87        self.assertTrue(o.respondsToSelector(b'p4'))
88        self.assertTrue(o.respondsToSelector(b'setP4:'))
89
90        o.p4 = v
91        self.assertIs(o.p4, v)
92        self.assertIs(o.myp4, v)
93
94        self.assertTrue(o.respondsToSelector(b'p5'))
95        self.assertTrue(o.respondsToSelector(b'setP5:'))
96        self.assertTrue(o.respondsToSelector(b'p6'))
97        self.assertTrue(o.respondsToSelector(b'setP6:'))
98        s = o.methodSignatureForSelector_(b'p5')
99        self.assertEquals(s.methodReturnType(), objc._C_INT)
100        s = o.methodSignatureForSelector_(b'p6')
101        self.assertEquals(s.methodReturnType(), objc._C_DBL)
102
103    def testDepends(self):
104        class OCTestObjectProperty2 (NSObject):
105            p1 = objc.object_property()
106            p2 = objc.object_property()
107            p3 = objc.object_property(read_only=True, depends_on=['p1', 'p2'])
108
109            @p3.getter
110            def p3(self):
111                return (self.p1, self.p2)
112
113        observer = OCObserve.alloc().init()
114        object = OCTestObjectProperty2.alloc().init()
115
116        observer.register(object, 'p1')
117        observer.register(object, 'p2')
118        observer.register(object, 'p3')
119        try:
120
121            self.assertEquals(observer.values, [])
122
123            object.p1 = "a"
124            object.p2 = "b"
125            self.assertEquals(object.p3, ("a", "b"))
126
127            self.assertEquals(len(observer.values), 4)
128
129            if observer.values[0][1] == 'p1':
130                self.assertEquals(observer.values[1][1], 'p3')
131            else:
132                self.assertEquals(observer.values[0][1], 'p3')
133                self.assertEquals(observer.values[1][1], 'p1')
134
135            if observer.values[2][1] == 'p2':
136                self.assertEquals(observer.values[3][1], 'p3')
137            else:
138                self.assertEquals(observer.values[2][1], 'p3')
139                self.assertEquals(observer.values[3][1], 'p2')
140
141        finally:
142            observer.unregister(object, 'p1')
143            observer.unregister(object, 'p2')
144            observer.unregister(object, 'p3')
145
146    def testMethods(self):
147        l = []
148
149        class OCTestObjectProperty4 (NSObject):
150
151            p1 = objc.object_property()
152
153            @p1.getter
154            def p1(self):
155                l.append(('get',))
156                return self._p1 + '!'
157
158            @p1.setter
159            def p1(self, v):
160                l.append(('set', v))
161                self._p1 = v + '?'
162
163
164            @p1.validate
165            def p1(self, value, error):
166                if value == 1:
167                    return (True, value, None)
168                else:
169                    return (False, 2, u"snake")
170
171
172        o = OCTestObjectProperty4.alloc().init()
173        o.p1 = 'f'
174        self.assertEquals(o.p1, 'f?!')
175        self.assertEquals(o._p1, 'f?')
176        self.assertEquals(l, [('set', 'f'), ('get',)])
177
178        ok, value, error = o.validateValue_forKey_error_(
179                1, 'p1', None)
180        self.assertTrue(ok)
181        self.assertEquals(value, 1)
182        self.assertEquals(error, None)
183
184        ok, value, error = o.validateValue_forKey_error_(
185                9, 'p1', None)
186        self.assertFalse(ok)
187        self.assertEquals(value, 2)
188        self.assertEquals(error, u"snake")
189
190    def testNative(self):
191        l = []
192        class OCTestObjectProperty7 (NSObject):
193            p1 = objc.object_property()
194
195            @p1.getter
196            def p1(self):
197                l.append('get')
198                return self._p1
199
200            @p1.setter
201            def p1(self, value):
202                l.append('set')
203                self._p1 = value
204
205
206        o = OCTestObjectProperty7.alloc().init()
207        o.setValue_forKey_(42, 'p1')
208        self.assertEquals(o._p1, 42)
209
210        o._p1 = u"monkey"
211        v = o.valueForKey_('p1')
212        self.assertEquals(v, u"monkey")
213
214        self.assertEquals(l, ["set", "get"])
215
216
217    def testDynamic(self):
218        class OCTestObjectProperty8 (NSObject):
219            p1 = objc.object_property(dynamic=True)
220
221        self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"p1"))
222        self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"setP1:"))
223
224        v = [42]
225        def getter(self):
226            return v[0]
227        def setter(self, value):
228            v[0] = value
229
230        OCTestObjectProperty8.p1 = getter
231        OCTestObjectProperty8.setP1_ = setter
232
233        self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"p1"))
234        self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"setP1:"))
235
236        o = OCTestObjectProperty8.alloc().init()
237        self.assertIsInstance(OCTestObjectProperty8.p1, objc.object_property)
238
239
240        self.assertEquals(o.p1, 42)
241        o.p1 = 99
242        self.assertEquals(o.p1, 99)
243        self.assertEquals(v[0], 99)
244
245
246    def testReadOnly(self):
247
248        class OCTestObjectProperty3 (NSObject):
249            p1 = objc.object_property(read_only=True)
250
251        o = OCTestObjectProperty3.alloc().init()
252
253        self.assertRaises(ValueError, setattr, o, 'p1', 42)
254
255    def testSubclass(self):
256
257        class OCTestObjectProperty5 (NSObject):
258            p1 = objc.object_property(read_only=True)
259            p2 = objc.object_property()
260
261        class OCTestObjectProperty6 (OCTestObjectProperty5):
262            @OCTestObjectProperty5.p1.setter
263            def p1(self, value):
264                self._p1 = value
265
266        base = OCTestObjectProperty5.alloc().init()
267        self.assertRaises(ValueError, setattr, base, 'p1', 1)
268        base.p2 = 'b'
269        self.assertEquals(base.p2, 'b')
270
271        sub = OCTestObjectProperty6.alloc().init()
272        sub.p1 = 1
273        sub.p2 = 'a'
274        self.assertEquals(sub.p1, 1)
275        self.assertEquals(sub.p2, 'a')
276
277if __name__ == "__main__":
278    main()
279