1from __future__ import unicode_literals
2from PyObjCTools.TestSupport import *
3import objc
4import copy
5from PyObjCTest.fnd import *
6
7objc.registerMetaDataForSelector(
8        b"NSObject", b"validateValue:forKey:error:",
9        dict(
10            arguments={
11                2: dict(type_modifier=objc._C_INOUT),
12                4: dict(type_modifier=objc._C_OUT),
13            },
14        ))
15
16
17class OCCopy (NSObject):
18    def copy(self):
19        return self.copyWithZone_(None)
20
21    def copyWithZone_(self, zone):
22        v = OCCopy.allocWithZone_(zone).init()
23        return v
24
25class OCObserve (NSObject):
26    def init(self):
27        self = super(OCObserve, self).init()
28        self.values = []
29        self.registrations = []
30        return self
31
32    @property
33    def seen(self):
34        return { v[1]: v[2]['new'] for v in self.values }
35
36
37    def register(self, object, keypath):
38        object.addObserver_forKeyPath_options_context_(
39                self, keypath, 0x3, None)
40        self.registrations.append((object, keypath))
41
42    def unregister(self, object, keypath):
43        object.removeObserver_forKeyPath_(self, keypath)
44
45    def observeValueForKeyPath_ofObject_change_context_(
46            self, keypath, object, change, context):
47
48        # We don't get to keep the 'change' dictionary, make
49        # a copy (it gets reused in future calls)
50        new_change = {}
51        for k in change:
52            v = change[k]
53            if isinstance(v, (list, tuple, set)):
54                v = copy.copy(v)
55            new_change[k] = v
56        self.values.append((object, keypath, new_change))
57
58    def __enter__(self):
59        return self
60
61    def __exit__(self, type, value, traceback):
62        for o, k in self.registrations:
63            self.unregister(o, k)
64        self.registrations = []
65
66
67class TestObjectProperty (TestCase):
68    def testCreation(self):
69        class OCTestObjectProperty1 (NSObject):
70            p1 = objc.object_property()
71            p2 = objc.object_property(copy=True)
72            p3 = objc.object_property(read_only=True)
73            p4 = objc.object_property(ivar='myp4')
74            p5 = objc.object_property(typestr=objc._C_INT)
75            p6 = objc.object_property(typestr=objc._C_DBL)
76
77
78        o = OCTestObjectProperty1.alloc().init()
79
80        self.assertTrue(o.respondsToSelector(b'p1'))
81        self.assertTrue(o.respondsToSelector(b'setP1:'))
82
83        v = OCCopy.alloc().init()
84        o.p1 = v
85        self.assertIs(o.p1, v)
86        self.assertIs(o._p1, v)
87
88        self.assertTrue(o.respondsToSelector(b'p2'))
89        self.assertTrue(o.respondsToSelector(b'setP2:'))
90
91        o.p2 = v
92        self.assertIsInstance(o.p2, OCCopy)
93        self.assertIsNot(o.p2, v)
94        self.assertIsNot(o._p2, v)
95
96        self.assertTrue(o.respondsToSelector(b'p3'))
97        self.assertFalse(o.respondsToSelector(b'setP3:'))
98
99        o._p3 = v
100        self.assertIs(o.p3, v)
101
102
103        self.assertTrue(o.respondsToSelector(b'p4'))
104        self.assertTrue(o.respondsToSelector(b'setP4:'))
105
106        o.p4 = v
107        self.assertIs(o.p4, v)
108        self.assertIs(o.myp4, v)
109
110        self.assertTrue(o.respondsToSelector(b'p5'))
111        self.assertTrue(o.respondsToSelector(b'setP5:'))
112        self.assertTrue(o.respondsToSelector(b'p6'))
113        self.assertTrue(o.respondsToSelector(b'setP6:'))
114        s = o.methodSignatureForSelector_(b'p5')
115        self.assertEqual(s.methodReturnType(), objc._C_INT)
116        s = o.methodSignatureForSelector_(b'p6')
117        self.assertEqual(s.methodReturnType(), objc._C_DBL)
118
119    def testDepends(self):
120        class OCTestObjectProperty2 (NSObject):
121            p1 = objc.object_property()
122            p2 = objc.object_property()
123            p3 = objc.object_property(read_only=True, depends_on=['p1', 'p2'])
124
125            @p3.getter
126            def p3(self):
127                return (self.p1 or '', self.p2 or '')
128
129        class OCTestObjectProperty2b (OCTestObjectProperty2):
130            p4 = objc.object_property()
131
132            @OCTestObjectProperty2.p3.getter
133            def p3(self):
134                return (self.p4 or '', self.p2 or '', self.p1 or '')
135            p3.depends_on('p4')
136
137            p5 = objc.object_property(read_only=True)
138
139            @p5.getter
140            def p5(self):
141                return "-%s-"%(self.p4,)
142            p5.depends_on('p4')
143
144        observer1 = OCObserve.alloc().init()
145        observer2 = OCObserve.alloc().init()
146        object1 = OCTestObjectProperty2.alloc().init()
147        object2 = OCTestObjectProperty2b.alloc().init()
148
149        v = type(object1).keyPathsForValuesAffectingP3()
150        self.assertIsInstance(v, objc.lookUpClass('NSSet'))
151        self.assertEqual(v, {'p1', 'p2'})
152
153        v = type(object2).keyPathsForValuesAffectingP3()
154        self.assertIsInstance(v, objc.lookUpClass('NSSet'))
155        self.assertEqual(v, {'p1', 'p2', 'p4'})
156
157        self.assertTrue(object1.respondsToSelector('p1'))
158        self.assertTrue(object1.respondsToSelector('setP1:'))
159        self.assertTrue(object1.respondsToSelector('p2'))
160        self.assertTrue(object1.respondsToSelector('setP2:'))
161        self.assertTrue(object1.respondsToSelector('p3'))
162        self.assertFalse(object1.respondsToSelector('setP3:'))
163
164        self.assertTrue(object2.respondsToSelector('p1'))
165        self.assertTrue(object2.respondsToSelector('setP1:'))
166        self.assertTrue(object2.respondsToSelector('p2'))
167        self.assertTrue(object2.respondsToSelector('setP2:'))
168        self.assertTrue(object2.respondsToSelector('p3'))
169        self.assertFalse(object2.respondsToSelector('setP3:'))
170        self.assertTrue(object2.respondsToSelector('p4'))
171        self.assertTrue(object2.respondsToSelector('setP4:'))
172
173        observer1.register(object1, 'p1')
174        observer1.register(object1, 'p2')
175        observer1.register(object1, 'p3')
176
177        observer2.register(object2, 'p1')
178        observer2.register(object2, 'p2')
179        observer2.register(object2, 'p3')
180        observer2.register(object2, 'p4')
181        observer2.register(object2, 'p5')
182
183        try:
184            self.assertEqual(observer1.values, [])
185            self.assertEqual(observer2.values, [])
186
187            object1.p1 = "a"
188            object1.p2 = "b"
189            self.assertEqual(object1.p3, ("a", "b"))
190            self.assertEqual(object1.pyobjc_instanceMethods.p3(), ("a", "b"))
191
192
193            object2.p1 = "a"
194            object2.p2 = "b"
195            object2.p4 = "c"
196            self.assertEqual(object2.p3, ("c", "b", "a"))
197            self.assertEqual(object2.pyobjc_instanceMethods.p3(), ("c", "b", "a"))
198            self.assertEqual(object2.pyobjc_instanceMethods.p4(), "c")
199
200            #seen = { v[1]: v[2]['new'] for v in observer1.values }
201            self.assertEqual(observer1.seen,
202                {'p1': 'a', 'p2': 'b', 'p3': ('a', 'b') })
203
204            #seen = { v[1]: v[2]['new'] for v in observer2.values }
205            self.assertEqual(observer2.seen,
206                {'p1': 'a', 'p2': 'b', 'p3': ('c', 'b', 'a'), 'p4': 'c', 'p5': '-c-' })
207
208        finally:
209            observer1.unregister(object1, 'p1')
210            observer1.unregister(object1, 'p2')
211            observer1.unregister(object1, 'p3')
212
213            observer2.unregister(object2, 'p1')
214            observer2.unregister(object2, 'p2')
215            observer2.unregister(object2, 'p3')
216            observer2.unregister(object2, 'p4')
217            observer2.unregister(object2, 'p5')
218
219    def testDepends2(self):
220        class OCTestObjectProperty2B (NSObject):
221            p1 = objc.object_property()
222            @p1.getter
223            def p1(self):
224                return self._p1
225            @p1.setter
226            def p1(self, v):
227                self._p1 = v
228
229            p2 = objc.object_property()
230            @p2.getter
231            def p2(self):
232                return self._p2
233            @p2.setter
234            def p2(self, v):
235                self._p2 = v
236
237            p3 = objc.object_property(read_only=True, depends_on=['p1', 'p2'])
238
239            @p3.getter
240            def p3(self):
241                return (self.p1 or '', self.p2 or '')
242
243        class OCTestObjectProperty2Bb (OCTestObjectProperty2B):
244            p4 = objc.object_property()
245
246            @OCTestObjectProperty2B.p1.getter
247            def p1(self):
248                return self._p1
249
250            @OCTestObjectProperty2B.p3.getter
251            def p3(self):
252                return (self.p4 or '', self.p2 or '', self.p1 or '')
253            p3.depends_on('p4')
254
255        observer1 = OCObserve.alloc().init()
256        observer2 = OCObserve.alloc().init()
257        object1 = OCTestObjectProperty2B.alloc().init()
258        object2 = OCTestObjectProperty2Bb.alloc().init()
259
260        v = type(object1).keyPathsForValuesAffectingP3()
261        self.assertIsInstance(v, objc.lookUpClass('NSSet'))
262        self.assertEqual(v, {'p1', 'p2'})
263
264        v = type(object2).keyPathsForValuesAffectingP3()
265        self.assertIsInstance(v, objc.lookUpClass('NSSet'))
266        self.assertEqual(v, {'p1', 'p2', 'p4'})
267
268        self.assertTrue(object1.respondsToSelector('p1'))
269        self.assertTrue(object1.respondsToSelector('setP1:'))
270        self.assertTrue(object1.respondsToSelector('p2'))
271        self.assertTrue(object1.respondsToSelector('setP2:'))
272        self.assertTrue(object1.respondsToSelector('p3'))
273        self.assertFalse(object1.respondsToSelector('setP3:'))
274
275        self.assertTrue(object2.respondsToSelector('p1'))
276        self.assertTrue(object2.respondsToSelector('setP1:'))
277        self.assertTrue(object2.respondsToSelector('p2'))
278        self.assertTrue(object2.respondsToSelector('setP2:'))
279        self.assertTrue(object2.respondsToSelector('p3'))
280        self.assertFalse(object2.respondsToSelector('setP3:'))
281        self.assertTrue(object2.respondsToSelector('p4'))
282        self.assertTrue(object2.respondsToSelector('setP4:'))
283
284        observer1.register(object1, 'p1')
285        observer1.register(object1, 'p2')
286        observer1.register(object1, 'p3')
287
288        observer2.register(object2, 'p1')
289        observer2.register(object2, 'p2')
290        observer2.register(object2, 'p3')
291        observer2.register(object2, 'p4')
292
293        try:
294            self.assertEqual(observer1.values, [])
295            self.assertEqual(observer2.values, [])
296
297            object1.p1 = "a"
298            object1.p2 = "b"
299            self.assertEqual(object1.p3, ("a", "b"))
300            self.assertEqual(object1.pyobjc_instanceMethods.p3(), ("a", "b"))
301
302
303            object2.p1 = "a"
304            object2.p2 = "b"
305            object2.p4 = "c"
306            self.assertEqual(object2.p3, ("c", "b", "a"))
307            self.assertEqual(object2.pyobjc_instanceMethods.p3(), ("c", "b", "a"))
308            self.assertEqual(object2.pyobjc_instanceMethods.p4(), "c")
309
310            #seen = { v[1]: v[2]['new'] for v in observer1.values }
311            self.assertEqual(observer1.seen,
312                {'p1': 'a', 'p2': 'b', 'p3': ('a', 'b') })
313
314            #seen = { v[1]: v[2]['new'] for v in observer2.values }
315            self.assertEqual(observer2.seen,
316               {'p1': 'a', 'p2': 'b', 'p3': ('c', 'b', 'a'), 'p4': 'c' })
317
318        finally:
319            observer1.unregister(object1, 'p1')
320            observer1.unregister(object1, 'p2')
321            observer1.unregister(object1, 'p3')
322
323            observer2.unregister(object2, 'p1')
324            observer2.unregister(object2, 'p2')
325            observer2.unregister(object2, 'p3')
326            observer2.unregister(object2, 'p4')
327
328    def testMethods(self):
329        l = []
330
331        class OCTestObjectProperty4 (NSObject):
332
333            p1 = objc.object_property()
334
335            @p1.getter
336            def p1(self):
337                l.append(('get',))
338                return self._p1 + '!'
339
340            @p1.setter
341            def p1(self, v):
342                l.append(('set', v))
343                self._p1 = v + '?'
344
345
346            @p1.validate
347            def p1(self, value, error):
348                if value == 1:
349                    return (True, value, None)
350                else:
351                    return (False, 2, "snake")
352
353        class OCTestObjectProperty4b (OCTestObjectProperty4):
354            @OCTestObjectProperty4.p1.validate
355            def p1(self, value, error):
356                if value == 2:
357                    return (True, value, None)
358                else:
359                    return (False, 2, "monty")
360
361
362        o = OCTestObjectProperty4.alloc().init()
363        o.p1 = 'f'
364        self.assertEqual(o.p1, 'f?!')
365        self.assertEqual(o._p1, 'f?')
366        self.assertEqual(l, [('set', 'f'), ('get',)])
367
368        ok, value, error = o.validateValue_forKey_error_(
369                1, 'p1', None)
370        self.assertTrue(ok)
371        self.assertEqual(value, 1)
372        self.assertEqual(error, None)
373
374        ok, value, error = o.validateValue_forKey_error_(
375                9, 'p1', None)
376        self.assertFalse(ok)
377        self.assertEqual(value, 2)
378        self.assertEqual(error, "snake")
379
380        l = []
381        o = OCTestObjectProperty4b.alloc().init()
382        o.p1 = 'f'
383        self.assertEqual(o.p1, 'f?!')
384        self.assertEqual(o._p1, 'f?')
385        self.assertEqual(l, [('set', 'f'), ('get',)])
386
387        ok, value, error = o.validateValue_forKey_error_(
388                2, 'p1', None)
389        self.assertTrue(ok)
390        self.assertEqual(value, 2)
391        self.assertEqual(error, None)
392
393        ok, value, error = o.validateValue_forKey_error_(
394                9, 'p1', None)
395        self.assertFalse(ok)
396        self.assertEqual(value, 2)
397        self.assertEqual(error, "monty")
398
399    def testNative(self):
400        l = []
401        class OCTestObjectProperty7 (NSObject):
402            p1 = objc.object_property()
403
404            @p1.getter
405            def p1(self):
406                l.append('get')
407                return self._p1
408
409            @p1.setter
410            def p1(self, value):
411                l.append('set')
412                self._p1 = value
413
414
415        o = OCTestObjectProperty7.alloc().init()
416        o.setValue_forKey_(42, 'p1')
417        self.assertEqual(o._p1, 42)
418
419        o._p1 = "monkey"
420        v = o.valueForKey_('p1')
421        self.assertEqual(v, "monkey")
422
423        self.assertEqual(l, ["set", "get"])
424
425
426    def testDynamic(self):
427        class OCTestObjectProperty8 (NSObject):
428            p1 = objc.object_property(dynamic=True)
429            p2 = objc.object_property(dynamic=True, typestr=objc._C_NSBOOL)
430
431        self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"p1"))
432        self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"setP1:"))
433        self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"isP2"))
434        self.assertFalse(OCTestObjectProperty8.instancesRespondToSelector_(b"setP2:"))
435
436        v = [42]
437        def getter(self):
438            return v[0]
439        def setter(self, value):
440            v[0] = value
441
442        OCTestObjectProperty8.p1 = getter
443        OCTestObjectProperty8.setP1_ = setter
444
445        v2 = [False]
446        def getter2(self):
447            return v2[0]
448        def setter2(self, value):
449            v2[0] = bool(value)
450        OCTestObjectProperty8.isP2 = getter2
451        OCTestObjectProperty8.setP2_ = setter2
452
453
454        self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"p1"))
455        self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"setP1:"))
456        self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"isP2"))
457        self.assertTrue(OCTestObjectProperty8.instancesRespondToSelector_(b"setP2:"))
458
459        o = OCTestObjectProperty8.alloc().init()
460        self.assertIsInstance(OCTestObjectProperty8.p1, objc.object_property)
461        self.assertIsInstance(OCTestObjectProperty8.p2, objc.object_property)
462
463
464        self.assertEqual(o.p1, 42)
465        self.assertEqual(o.p2, False)
466        o.p1 = 99
467        o.p2 = True
468        self.assertEqual(o.p1, 99)
469        self.assertEqual(v[0], 99)
470        self.assertEqual(o.p2, True)
471        self.assertEqual(v2[0], True)
472
473
474    def testReadOnly(self):
475
476        class OCTestObjectProperty3 (NSObject):
477            p1 = objc.object_property(read_only=True)
478
479        o = OCTestObjectProperty3.alloc().init()
480
481        self.assertRaises(ValueError, setattr, o, 'p1', 42)
482
483    def testSubclass(self):
484
485        class OCTestObjectProperty5 (NSObject):
486            p1 = objc.object_property(read_only=True)
487            p2 = objc.object_property()
488            p3 = objc.object_property(read_only=True, typestr=objc._C_NSBOOL)
489
490        class OCTestObjectProperty6 (OCTestObjectProperty5):
491            @OCTestObjectProperty5.p1.setter
492            def p1(self, value):
493                self._p1 = value
494
495            @OCTestObjectProperty5.p2.setter
496            def p2(self, value):
497                self._p2 = value * 2
498
499            @OCTestObjectProperty5.p3.getter
500            def p3(self):
501                return not super(OCTestObjectProperty6, self).p3
502
503        base = OCTestObjectProperty5.alloc().init()
504        self.assertRaises(ValueError, setattr, base, 'p1', 1)
505        self.assertRaises(ValueError, setattr, base, 'p3', 1)
506        base.p2 = 'b'
507        self.assertEqual(base.p2, 'b')
508
509        sub = OCTestObjectProperty6.alloc().init()
510        sub.p1 = 1
511        sub.p2 = 'a'
512        sub._p3 = False
513        self.assertEqual(sub.p1, 1)
514        self.assertEqual(sub.p2, 'aa')
515        self.assertEqual(sub.p3, True)
516
517        self.assertTrue(base.respondsToSelector_(b'p2'))
518        self.assertFalse(base.respondsToSelector_(b'setP1:'))
519        self.assertTrue(base.respondsToSelector_(b'isP3'))
520        self.assertFalse(base.respondsToSelector_(b'p3'))
521
522        self.assertTrue(sub.respondsToSelector_(b'p2'))
523        self.assertTrue(sub.respondsToSelector_(b'setP1:'))
524        self.assertTrue(sub.respondsToSelector_(b'isP3'))
525        self.assertFalse(sub.respondsToSelector_(b'p3'))
526
527        try:
528            del sub.p3
529        except TypeError:
530            pass
531        else:
532            self.fail("Deleting an object_property shouldn't be possible")
533
534    def testDefaultSetterWithoutIvar(self):
535
536        try:
537            class OCTestObjectProperty7 (NSObject):
538                p1 = objc.object_property(ivar=objc.NULL)
539        except ValueError:
540            pass
541
542        else:
543            self.fail("ValueError not raised")
544
545        try:
546            class OCTestObjectProperty8 (NSObject):
547                p1 = objc.object_property(ivar=objc.NULL, read_only=True)
548        except ValueError:
549            pass
550
551        else:
552            self.fail("ValueError not raised")
553
554
555        try:
556            class OCTestObjectProperty9 (NSObject):
557                p1 = objc.object_property(read_only=True)
558
559                @p1.setter
560                def p1(self, v):
561                    pass
562
563        except ValueError:
564            pass
565
566        else:
567            self.fail("ValueError not raised")
568
569        try:
570            class OCTestObjectProperty9 (NSObject):
571                p1 = objc.object_property(read_only=True)
572
573                @p1.validate
574                def p1(self, v):
575                    pass
576
577        except ValueError:
578            pass
579
580        else:
581            self.fail("ValueError not raised")
582
583class TestBoolProperty (TestCase):
584    def testDefault(self):
585        class OCTestBoolProperty1 (NSObject):
586            p1 = objc.bool_property()
587
588        o = OCTestBoolProperty1.alloc().init()
589        self.assertEqual(o.p1, False)
590
591        o.p1 = [1, 2]
592        self.assertEqual(o.p1, True)
593
594if __name__ == "__main__":
595    main()
596