1from PyObjCTools.TestSupport import *
2import objc
3import collections
4
5NSObject = objc.lookUpClass('NSObject')
6NSIndexSet = objc.lookUpClass('NSIndexSet')
7NSMutableIndexSet = objc.lookUpClass('NSMutableIndexSet')
8
9class TestArrayPropertyHelper (NSObject):
10    array = objc.array_property()
11    roArray = objc.array_property(read_only=True)
12
13from PyObjCTest.test_object_property import OCObserve
14
15class TestArrayProperty (TestCase):
16    def _testMissing(self):
17        self.fail("Implement tests")
18
19    def testGetting(self):
20        # Check that default value is an empty value
21        # Check that value is a proxy object
22        o = TestArrayPropertyHelper.alloc().init()
23
24        v = o.array
25        self.failUnlessIsInstance(v, collections.MutableSequence)
26
27        self.failUnlessEqual(len(v), 0)
28
29        v.append(1)
30        self.failUnlessEqual(len(v), 1)
31
32        self.assertEquals(type(v).__name__, 'array_proxy')
33
34    def testSetting(self):
35        # Set value, check that
36        # (1) value gets copied
37        # (2) accessing the property result in proxy
38        observer = OCObserve.alloc().init()
39        l = [1, 2, 3]
40        o = TestArrayPropertyHelper.alloc().init()
41        observer.register(o, 'array')
42        try:
43            self.assertEquals(len(observer.values), 0)
44            self.assertEquals(len(o.array), 0)
45            self.assertEquals(len(observer.values), 0)
46            o.array = l
47            self.assertEquals(len(observer.values), 1)
48
49
50            self.assertEquals(len(o.array), 3)
51
52            # This shouldn't affect the property
53            l.append(4)
54            self.assertEquals(len(o.array), 3)
55
56            self.assertEquals(len(l), 4)
57            o.array.append(5)
58            self.assertEquals(len(l), 4)
59
60        finally:
61            observer.unregister(o, 'array')
62
63
64
65    def testGetSetItem(self):
66        # Use __getitem__, __setitem__ interface and check
67        # that the correct KVO events get emitted.
68        observer = OCObserve.alloc().init()
69        l = [1, 2, 3]
70        o = TestArrayPropertyHelper.alloc().init()
71        observer.register(o, 'array')
72
73        # FIXME: the call to len shouldn't be necessary
74        len(o.array)
75        try:
76            IS = NSIndexSet.alloc().initWithIndex_(0)
77            self.assertEquals(len(observer.values), 0)
78
79            o.array.append(1)
80
81            self.assertEquals(len(observer.values), 1)
82            self.assertEquals(observer.values[-1][-1]['indexes'], IS)
83            self.assertEquals(observer.values[-1][-1]['new'], [1])
84
85
86            self.assertEquals(o.array[0], 1)
87            o.array[0] = 4
88            self.assertEquals(o.array[0], 4)
89            self.assertEquals(len(observer.values), 2)
90            self.assertEquals(observer.values[-1][-1]['indexes'], IS)
91            self.assertEquals(observer.values[-1][-1]['old'], [1])
92            self.assertEquals(observer.values[-1][-1]['new'], [4])
93
94        finally:
95            observer.unregister(o, 'array')
96
97    def testGetSetSlice(self):
98        # Same as testGetSetItem, but using slice
99        observer = OCObserve.alloc().init()
100        l = [1, 2, 3]
101        o = TestArrayPropertyHelper.alloc().init()
102        observer.register(o, 'array')
103
104        try:
105            IS = NSIndexSet.alloc().initWithIndexesInRange_((0, 3))
106            IS2 = NSIndexSet.alloc().initWithIndexesInRange_((1, 2))
107            IS3 = NSMutableIndexSet.alloc().init()
108            IS3.addIndex_(0)
109            IS3.addIndex_(2)
110            self.assertEquals(len(observer.values), 0)
111
112            o.array = l
113
114            self.assertEquals(len(observer.values), 1)
115            self.assertNotIn('indexes', observer.values[-1][-1])
116            self.assertEquals(observer.values[-1][-1]['new'], [1, 2, 3])
117
118
119            self.assertEquals(o.array[0], 1)
120            o.array[1:3] = [4, 5]
121            self.assertEquals(o.array[1], 4)
122            self.assertEquals(o.array[2], 5)
123            self.assertEquals(len(observer.values), 2)
124            self.assertEquals(observer.values[-1][-1]['indexes'], IS2)
125            self.assertEquals(observer.values[-1][-1]['old'], [2, 3])
126            self.assertEquals(observer.values[-1][-1]['new'], [4, 5])
127
128            self.assertEquals(o.array[0], 1)
129            o.array[0:3:2] = [9, 10]
130            self.assertEquals(o.array[0], 9)
131            self.assertEquals(o.array[1], 4)
132            self.assertEquals(o.array[2], 10)
133            self.assertEquals(len(observer.values), 3)
134            self.assertEquals(observer.values[-1][-1]['indexes'], IS3)
135            self.assertEquals(observer.values[-1][-1]['old'], [1, 5])
136            self.assertEquals(observer.values[-1][-1]['new'], [9, 10])
137
138        finally:
139            observer.unregister(o, 'array')
140
141    def testInsert(self):
142        # Use insert method and check that the correct
143        # KVO events get emitted
144        # Same as testGetSetItem, but using slice
145        observer = OCObserve.alloc().init()
146        l = [1, 2, 3]
147        o = TestArrayPropertyHelper.alloc().init()
148        observer.register(o, 'array')
149
150        try:
151            IS = NSIndexSet.alloc().initWithIndex_(0)
152            IS1 = NSIndexSet.alloc().initWithIndex_(4)
153            self.assertEquals(len(observer.values), 0)
154
155            o.array = l
156
157            self.assertEquals(len(observer.values), 1)
158            self.assertNotIn('indexes', observer.values[-1][-1])
159
160            self.assertEquals(o.array[0], 1)
161
162
163            o.array.insert(0, 'a')
164            self.assertEquals(o.array[0], 'a')
165            self.assertEquals(len(o.array), 4)
166
167            self.assertEquals(len(observer.values), 2)
168            self.assertEquals(observer.values[-1][-1]['indexes'], IS)
169            self.assertNotIn('old', observer.values[-1][-1])
170            self.assertEquals(observer.values[-1][-1]['new'], ['a'])
171
172            o.array.insert(4, 'b')
173            self.assertEquals(o.array[4], 'b')
174            self.assertEquals(len(o.array), 5)
175
176            self.assertEquals(len(observer.values), 3)
177            self.assertEquals(observer.values[-1][-1]['indexes'], IS1)
178            self.assertNotIn('old', observer.values[-1][-1])
179            self.assertEquals(observer.values[-1][-1]['new'], ['b'])
180
181        finally:
182            observer.unregister(o, 'array')
183
184    def testPop(self):
185        # Use pop method and check that the correct
186        # KVO events get emitted
187        observer = OCObserve.alloc().init()
188        l = [1, 2, 3, 4]
189        o = TestArrayPropertyHelper.alloc().init()
190        observer.register(o, 'array')
191
192        try:
193            IS = NSIndexSet.alloc().initWithIndex_(0)
194            IS2 = NSIndexSet.alloc().initWithIndex_(2)
195            self.assertEquals(len(observer.values), 0)
196
197            o.array = l
198
199            self.assertEquals(len(observer.values), 1)
200            self.assertNotIn('indexes', observer.values[-1][-1])
201
202            self.assertEquals(o.array[0], 1)
203
204
205            v = o.array.pop(0)
206            self.assertEquals(v, 1)
207            self.assertEquals(o.array[0], 2)
208            self.assertEquals(len(o.array), 3)
209
210            self.assertEquals(len(observer.values), 2)
211            self.assertEquals(observer.values[-1][-1]['indexes'], IS)
212            self.assertNotIn('new', observer.values[-1][-1])
213            self.assertEquals(observer.values[-1][-1]['old'], [1])
214
215            v = o.array.pop(2)
216            self.assertEquals(v, 4)
217            self.assertEquals(len(o.array), 2)
218
219            self.assertEquals(len(observer.values), 3)
220            self.assertEquals(observer.values[-1][-1]['indexes'], IS2)
221            self.assertNotIn('new', observer.values[-1][-1])
222            self.assertEquals(observer.values[-1][-1]['old'], [4])
223
224        finally:
225            observer.unregister(o, 'array')
226
227    def testDelItem(self):
228        # Use __delitem__and check that the correct
229        # KVO events get emitted
230        observer = OCObserve.alloc().init()
231        l = [1, 2, 3, 4]
232        o = TestArrayPropertyHelper.alloc().init()
233        observer.register(o, 'array')
234
235        try:
236            IS = NSIndexSet.alloc().initWithIndex_(0)
237            IS2 = NSIndexSet.alloc().initWithIndex_(2)
238            self.assertEquals(len(observer.values), 0)
239
240            o.array = l
241
242            self.assertEquals(len(observer.values), 1)
243            self.assertNotIn('indexes', observer.values[-1][-1])
244
245            self.assertEquals(o.array[0], 1)
246
247
248            del o.array[0]
249            self.assertEquals(o.array[0], 2)
250            self.assertEquals(len(o.array), 3)
251
252            self.assertEquals(len(observer.values), 2)
253            self.assertEquals(observer.values[-1][-1]['indexes'], IS)
254            self.assertNotIn('new', observer.values[-1][-1])
255            self.assertEquals(observer.values[-1][-1]['old'], [1])
256
257            del o.array[2]
258            self.assertEquals(len(o.array), 2)
259
260            self.assertEquals(len(observer.values), 3)
261            self.assertEquals(observer.values[-1][-1]['indexes'], IS2)
262            self.assertNotIn('new', observer.values[-1][-1])
263            self.assertEquals(observer.values[-1][-1]['old'], [4])
264
265        finally:
266            observer.unregister(o, 'array')
267
268    def testDelSlice(self):
269        # As testDelItem, but using slices
270        observer = OCObserve.alloc().init()
271        l = [1, 2, 3, 4]
272        o = TestArrayPropertyHelper.alloc().init()
273        observer.register(o, 'array')
274
275        try:
276            IS = NSMutableIndexSet.alloc().init()
277            IS.addIndex_(0)
278            IS.addIndex_(2)
279            self.assertEquals(len(observer.values), 0)
280
281            o.array = l
282
283            self.assertEquals(len(observer.values), 1)
284            self.assertNotIn('indexes', observer.values[-1][-1])
285
286            self.assertEquals(o.array[0], 1)
287
288
289            del o.array[0:4:2]
290            self.assertEquals(o.array[0], 2)
291            self.assertEquals(o.array[1], 4)
292            self.assertEquals(len(o.array), 2)
293
294            self.assertEquals(len(observer.values), 2)
295            self.assertEquals(observer.values[-1][-1]['indexes'], IS)
296            self.assertNotIn('new', observer.values[-1][-1])
297            self.assertEquals(observer.values[-1][-1]['old'], [1, 3])
298
299        finally:
300            observer.unregister(o, 'array')
301
302    def testExtend(self):
303        observer = OCObserve.alloc().init()
304        l = [1, 2, 3, 4]
305        l2 = ['a', 'b', 'c']
306        o = TestArrayPropertyHelper.alloc().init()
307        observer.register(o, 'array')
308
309        try:
310            o.array = l
311
312            self.assertEquals(len(observer.values), 1)
313            self.assertEquals(o.array[0], 1)
314
315            o.array.extend(l2)
316
317            self.assertEquals(len(o.array), 7)
318            self.assertEquals(o.array[4], 'a')
319
320            self.assertEquals(len(observer.values), 2)
321            self.assertEquals(observer.values[-1][-1]['indexes'], NSIndexSet.alloc().initWithIndexesInRange_((4, 3)))
322            self.assertNotIn('old', observer.values[-1][-1])
323            self.assertEquals(observer.values[-1][-1]['new'], ['a', 'b', 'c'])
324
325        finally:
326            observer.unregister(o, 'array')
327
328    def testIAdd(self):
329        observer = OCObserve.alloc().init()
330        l = [1, 2, 3, 4]
331        l2 = ['a', 'b', 'c']
332        o = TestArrayPropertyHelper.alloc().init()
333        observer.register(o, 'array')
334
335        try:
336            o.array = l
337
338            self.assertEquals(len(observer.values), 1)
339            self.assertEquals(o.array[0], 1)
340
341            o.array += l2
342
343            self.assertEquals(len(o.array), 7)
344            self.assertEquals(o.array[4], 'a')
345
346            #self.assertEquals(len(observer.values), 3)
347            #self.assertEquals(observer.values[-2][-1]['indexes'], NSIndexSet.alloc().initWithIndexesInRange_((4, 3)))
348            #self.assertNotIn('old', observer.values[-2][-1])
349            #self.assertEquals(observer.values[-2][-1]['new'], ['a', 'b', 'c'])
350
351            self.assertEquals(len(observer.values), 2)
352            self.assertNotIn('indexes', observer.values[-1][-1])
353
354        finally:
355            observer.unregister(o, 'array')
356
357    def testIMul(self):
358        observer = OCObserve.alloc().init()
359        l = [1, 2]
360        o = TestArrayPropertyHelper.alloc().init()
361        observer.register(o, 'array')
362
363        try:
364            o.array = l
365
366            self.assertEquals(len(observer.values), 1)
367            self.assertEquals(o.array[0], 1)
368
369            o.array *= 3
370
371            self.assertEquals(len(o.array), 6)
372            self.assertEquals(o.array[0], 1)
373            self.assertEquals(o.array[1], 2)
374            self.assertEquals(o.array[2], 1)
375            self.assertEquals(o.array[3], 2)
376            self.assertEquals(o.array[4], 1)
377            self.assertEquals(o.array[5], 2)
378
379            #self.assertEquals(len(observer.values), 3)
380            #self.assertEquals(observer.values[-2][-1]['indexes'], NSIndexSet.alloc().initWithIndexesInRange_((2, 4)))
381            #self.assertNotIn('old', observer.values[-2][-1])
382            #self.assertEquals(observer.values[-2][-1]['new'], [1, 2, 1, 2])
383
384            self.assertEquals(len(observer.values), 2)
385            self.assertNotIn('indexes', observer.values[-1][-1])
386
387        finally:
388            observer.unregister(o, 'array')
389
390
391    def testSort(self):
392        # Use sort method and check that the correct
393        # KVO events get emitted
394        observer = OCObserve.alloc().init()
395        l = [2, 4, 1, 3]
396        o = TestArrayPropertyHelper.alloc().init()
397        observer.register(o, 'array')
398
399        try:
400            IS = NSIndexSet.alloc().initWithIndexesInRange_((0, 4))
401            self.assertEquals(len(observer.values), 0)
402
403            orig_l = l[:]
404            o.array = l
405
406
407            self.assertEquals(len(observer.values), 1)
408            self.assertNotIn('indexes', observer.values[-1][-1])
409
410            self.assertEquals(o.array[0], 2)
411
412            o.array.sort()
413
414            self.assertEquals(o.array[0], 1)
415            self.assertEquals(o.array[1], 2)
416            self.assertEquals(o.array[2], 3)
417            self.assertEquals(o.array[3], 4)
418            self.assertEquals(len(o.array), 4)
419
420            self.assertEquals(len(observer.values), 2)
421            self.assertEquals(observer.values[-1][-1]['indexes'], IS)
422            self.assertEquals(observer.values[-1][-1]['old'], l)
423            self.assertEquals(observer.values[-1][-1]['new'], [1,2,3,4])
424            self.assertEquals(orig_l, l)
425
426        finally:
427            observer.unregister(o, 'array')
428
429    def testReverse(self):
430        # Use reverse method and check that the correct
431        # KVO events get emitted
432        observer = OCObserve.alloc().init()
433        l = [2, 4, 1, 3]
434        o = TestArrayPropertyHelper.alloc().init()
435        observer.register(o, 'array')
436
437        try:
438            IS = NSIndexSet.alloc().initWithIndexesInRange_((0, 4))
439            self.assertEquals(len(observer.values), 0)
440
441            orig_l = l[:]
442            o.array = l
443
444
445            self.assertEquals(len(observer.values), 1)
446            self.assertNotIn('indexes', observer.values[-1][-1])
447
448            self.assertEquals(o.array[0], 2)
449
450            o.array.reverse()
451
452            self.assertEquals(o.array[0], 3)
453            self.assertEquals(o.array[1], 1)
454            self.assertEquals(o.array[2], 4)
455            self.assertEquals(o.array[3], 2)
456            self.assertEquals(len(o.array), 4)
457
458            self.assertEquals(len(observer.values), 2)
459            self.assertEquals(observer.values[-1][-1]['indexes'], IS)
460            self.assertEquals(observer.values[-1][-1]['old'], l)
461            self.assertEquals(observer.values[-1][-1]['new'], [3, 1, 4, 2])
462            self.assertEquals(orig_l, l)
463
464        finally:
465            observer.unregister(o, 'array')
466
467    def testObjCAccessors(self):
468        # Check that the right ObjC array accessors are defined and work properly
469        self.assertTrue(TestArrayPropertyHelper.instancesRespondToSelector_(b"setArray:"))
470        self.assertTrue(TestArrayPropertyHelper.instancesRespondToSelector_(b"array"))
471        self.assertTrue(TestArrayPropertyHelper.instancesRespondToSelector_(b"countOfArray"))
472        self.assertTrue(TestArrayPropertyHelper.instancesRespondToSelector_(b"objectInArrayAtIndex:"))
473        self.assertTrue(TestArrayPropertyHelper.instancesRespondToSelector_(b"insertObject:inArrayAtIndex:"))
474        self.assertTrue(TestArrayPropertyHelper.instancesRespondToSelector_(b"removeObjectFromArrayAtIndex:"))
475        self.assertTrue(TestArrayPropertyHelper.instancesRespondToSelector_(b"replaceObjectInArrayAtIndex:withObject:"))
476
477        o = TestArrayPropertyHelper.alloc().init()
478        self.assertEquals(0, o.pyobjc_instanceMethods.countOfArray())
479        self.assertRaises(AttributeError, getattr, o, 'countOfArray')
480
481        o.pyobjc_instanceMethods.insertObject_inArrayAtIndex_('a', 0)
482        self.assertEquals(1, o.pyobjc_instanceMethods.countOfArray())
483        self.assertEquals('a', o.array[0])
484        self.assertEquals('a', o.pyobjc_instanceMethods.objectInArrayAtIndex_(0))
485        o.pyobjc_instanceMethods.replaceObjectInArrayAtIndex_withObject_(0, 'b')
486        self.assertEquals('b', o.array[0])
487        o.pyobjc_instanceMethods.removeObjectFromArrayAtIndex_(0)
488        self.assertEquals(0, o.pyobjc_instanceMethods.countOfArray())
489
490
491    # Verify docs and/or implementation to check for other
492    # mutating methods
493
494    def testReadingMethods(self):
495        # Check that all read-only methods work as well
496
497        o = TestArrayPropertyHelper.alloc().init()
498        o.array = [1, 2, 3, 4]
499
500        self.assertNotIsInstance(o.array, list)
501
502        self.assertEquals(o.array, [1,2,3,4])
503        self.assertNotEquals(o.array, [1,2,3,4, 5])
504
505
506        self.assertTrue(o.array < [1,2,3,4,5])
507        self.assertTrue(o.array <= [1,2,3,4,5])
508        self.assertTrue(o.array <= [1,2,3,4])
509        self.assertTrue(o.array >= [1,2,3,4])
510        self.assertTrue(o.array > [1,2,3])
511
512        self.assertEquals(o.array.count(1), 1)
513        self.assertEquals(o.array.index(4), 3)
514
515    def testMutatingReadonlyProperty(self):
516        # Check that trying to mutate a read-only property
517        # will raise an exception
518        o = TestArrayPropertyHelper.alloc().init()
519
520        o._roArray = [1, 2, 3]
521
522        self.assertEquals(list(o.roArray), [1,2,3])
523
524        self.assertRaises(ValueError, o.roArray.append,1)
525        self.assertRaises(ValueError, o.roArray.extend, [1,2])
526        self.assertRaises(ValueError, o.roArray.sort)
527        self.assertRaises(ValueError, o.roArray.reverse)
528        try:
529            o.roArray[0] = 2
530        except ValueError:
531            pass
532        else:
533            self.fail("ValueError not raised")
534
535        try:
536            del o.roArray[0]
537        except ValueError:
538            pass
539        else:
540            self.fail("ValueError not raised")
541
542        try:
543            o.roArray += [4]
544        except ValueError:
545            pass
546        else:
547            self.fail("ValueError not raised")
548
549        try:
550            o.roArray *= 4
551        except ValueError:
552            pass
553        else:
554            self.fail("TypeError not raised")
555
556
557    def testMutatingReadonlyPropertyObjC(self):
558        # Check that trying to mutate a read-only property
559        # from ObjC will raise an exception
560        o = TestArrayPropertyHelper.alloc().init()
561        o._roArray = [1,2,3]
562        self.assertEquals(3, o.pyobjc_instanceMethods.countOfRoArray())
563        self.assertRaises(AttributeError, getattr, o, 'countOfRoArray')
564
565        try:
566            o.pyobjc_instanceMethods.insertObject_inRoArrayAtIndex_('a', 0)
567        except ValueError:
568            pass
569        else:
570            self.fail("ValueError not raised")
571
572        self.assertEquals(3, o.pyobjc_instanceMethods.countOfRoArray())
573        self.assertEquals(1, o.pyobjc_instanceMethods.objectInRoArrayAtIndex_(0))
574        try:
575            o.pyobjc_instanceMethods.replaceObjectInRoArrayAtIndex_withObject_(0, 'b')
576        except ValueError:
577            pass
578        else:
579            self.fail("ValueError not raised")
580
581        try:
582            o.pyobjc_instanceMethods.removeObjectFromRoArrayAtIndex_(0)
583        except ValueError:
584            pass
585        else:
586            self.fail("ValueError not raised")
587
588
589    def testAssingmentInteraction(self):
590        o = TestArrayPropertyHelper.alloc().init()
591        array = o.array
592
593        o.array.append(1)
594        self.assertEquals(len(o.array), 1)
595        self.assertEquals(len(array), 1)
596
597if __name__ == "__main__":
598    main()
599