1"""
2Testcases for NSArchive-ing python objects.
3
4(Implementation is incomplete)
5"""
6import os
7
8import sys, copy_reg
9
10from PyObjCTools.TestSupport import *
11
12from PyObjCTest.fnd import NSArchiver, NSUnarchiver
13from PyObjCTest.fnd import NSKeyedArchiver, NSKeyedUnarchiver
14from PyObjCTest.fnd import NSData, NSArray, NSDictionary
15from PyObjCTest.fnd import NSMutableArray, NSMutableDictionary
16
17#
18# First set of tests: the stdlib tests for pickling, this
19# should test everything but mixed Python/Objective-C
20# object-graphs.
21#
22
23import test.pickletester
24
25MyList = test.pickletester.MyList
26
27# Quick hack to add a proper __repr__ to class C in
28# pickletester, makes it a lot easier to debug.
29def C__repr__(self):
30    return '<%s instance at %#x: %r>'%(
31        self.__class__.__name__, id(self), self.__dict__)
32test.pickletester.C.__repr__ = C__repr__
33del C__repr__
34
35def a_function():
36    pass
37
38class a_classic_class:
39    pass
40
41class a_newstyle_class (object):
42    pass
43
44def make_instance(state):
45    o = a_reducing_class()
46    o.__dict__.update(state)
47    return o
48
49class a_reducing_class (object):
50    def __reduce__(self):
51        return make_instance, (self.__dict__,)
52
53
54if int(os.uname()[2].split('.')[0]) >= 9:
55
56    # For some reason NSCoding support doesn't work on OSX 10.4 yet, ignore these
57    # tests for now
58    class TestKeyedArchiveSimple (TestCase):
59        def setUp(self):
60            self.archiverClass = NSKeyedArchiver
61            self.unarchiverClass = NSKeyedUnarchiver
62
63        def testBasicObjects(self):
64            buf = self.archiverClass.archivedDataWithRootObject_(a_function)
65            self.assertIsInstance(buf, NSData)
66            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
67            self.assertIs(v, a_function)
68
69            buf = self.archiverClass.archivedDataWithRootObject_(a_classic_class)
70            self.assertIsInstance(buf, NSData)
71            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
72            self.assertIs(v, a_classic_class)
73
74            buf = self.archiverClass.archivedDataWithRootObject_(a_newstyle_class)
75            self.assertIsInstance(buf, NSData)
76            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
77            self.assertIs(v, a_newstyle_class)
78
79            o = a_classic_class()
80            o.x = 42
81            buf = self.archiverClass.archivedDataWithRootObject_(o)
82            self.assertIsInstance(buf, NSData)
83            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
84            self.assertIsInstance(v, a_classic_class)
85            self.assertEquals(o.x, 42)
86
87            buf = self.archiverClass.archivedDataWithRootObject_(u"hello")
88            self.assertIsInstance(buf, NSData)
89            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
90            self.assertIsInstance(v, unicode)
91
92            buf = self.archiverClass.archivedDataWithRootObject_("hello")
93            self.assertIsInstance(buf, NSData)
94            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
95            self.assertIsInstance(v, str)
96            self.assertEquals(v, "hello")
97
98            buf = self.archiverClass.archivedDataWithRootObject_(sys.maxint * 4)
99            self.assertIsInstance(buf, NSData)
100            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
101            self.assertIsInstance(v, long)
102            self.assertEquals(v, sys.maxint * 4)
103
104            buf = self.archiverClass.archivedDataWithRootObject_(sys.maxint ** 4)
105            self.assertIsInstance(buf, NSData)
106            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
107            self.assertIsInstance(v, long)
108            self.assertEquals(v, sys.maxint ** 4)
109
110        def testSimpleLists(self):
111            o = []
112            buf = self.archiverClass.archivedDataWithRootObject_(o)
113            self.assertIsInstance(buf, NSData)
114            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
115            self.assertIsInstance(v, list)
116            self.assertEquals(v, o)
117
118            o = [u"hello", 42]
119            buf = self.archiverClass.archivedDataWithRootObject_(o)
120            self.assertIsInstance(buf, NSData)
121            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
122            self.assertIsInstance(v, list)
123            self.assertEquals(v, o)
124
125        def testSimpleTuples(self):
126            o = ()
127            buf = self.archiverClass.archivedDataWithRootObject_(o)
128            self.assertIsInstance(buf, NSData)
129            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
130            self.assertIsInstance(v, tuple)
131            self.assertEquals(v, o)
132
133            o = (u"hello", 42)
134            buf = self.archiverClass.archivedDataWithRootObject_(o)
135            self.assertIsInstance(buf, NSData)
136            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
137            self.assertIsInstance(v, tuple)
138            self.assertEquals(v, o)
139
140        def testSimpleDicts(self):
141            o = {}
142            buf = self.archiverClass.archivedDataWithRootObject_(o)
143            self.assertIsInstance(buf, NSData)
144            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
145            self.assertIsInstance(v, dict)
146            self.assertEquals(v, o)
147
148            o = {u"hello": u"bar", 42: 1.5 }
149            buf = self.archiverClass.archivedDataWithRootObject_(o)
150            self.assertIsInstance(buf, NSData)
151            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
152            self.assertIsInstance(v, dict)
153            self.assertEquals(v, o)
154
155        def testNestedDicts(self):
156            o = {
157                    u"hello": { 1:2 },
158                    u"world": u"foobar"
159                }
160            buf = self.archiverClass.archivedDataWithRootObject_(o)
161            self.assertIsInstance(buf, NSData)
162            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
163            self.assertIsInstance(v, dict)
164            self.assertEquals(v, o)
165
166            o = {}
167            o[u'self'] = o
168            buf = self.archiverClass.archivedDataWithRootObject_(o)
169            self.assertIsInstance(buf, NSData)
170            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
171            self.assertIsInstance(v, dict)
172            self.assertIs(v[u'self'], v)
173
174        def testNestedSequences(self):
175            o = [ 1, 2, 3, (5, (u'a', u'b'), 6), {1:2} ]
176            o[-1] = o
177
178            buf = self.archiverClass.archivedDataWithRootObject_(o)
179            self.assertIsInstance(buf, NSData)
180            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
181            self.assertIsInstance(v, list)
182            self.assertIs(v[-1], v)
183            self.assertEquals(v[:-1], o[:-1])
184
185        def testNestedInstance(self):
186            o = a_classic_class()
187            o.value = o
188
189            buf = self.archiverClass.archivedDataWithRootObject_(o)
190            self.assertIsInstance(buf, NSData)
191            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
192
193            self.assertIsInstance(v, a_classic_class)
194            self.assertIs(v.value, v)
195
196        def dont_testNestedInstanceWithReduce(self):
197            # Test recursive instantation with a __reduce__ method
198            #
199            # This test is disabled because pickle doesn't support
200            # this (and we don't either)
201            o = a_reducing_class()
202            o.value = o
203
204            import pickle
205            b = pickle.dumps(o)
206            o2 = pickle.loads(b)
207            print "+++", o2.value is o2
208
209            buf = self.archiverClass.archivedDataWithRootObject_(o)
210            self.assertIsInstance(buf, NSData)
211            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
212
213            self.assertIsInstance(v, a_reducing_class)
214            print type(v.value)
215            print v.value
216            print v
217            self.assertIs(v.value, v)
218
219        def testRecusiveNesting(self):
220            l = []
221            d = {1:l}
222            i = a_classic_class()
223            i.attr = d
224            l.append(i)
225
226            buf = self.archiverClass.archivedDataWithRootObject_(l)
227            self.assertIsInstance(buf, NSData)
228            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
229
230            self.assertEquals(len(v), 1)
231            self.assertEquals(dir(v[0]), dir(i))
232            self.assertEquals(v[0].attr.keys(), [1])
233            self.assertIs(v[0].attr[1], v)
234
235            buf = self.archiverClass.archivedDataWithRootObject_(d)
236            self.assertIsInstance(buf, NSData)
237            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
238            self.assertIs(v[1][0].attr, v)
239
240
241
242        def testTupleOfObjects(self):
243            o = a_classic_class()
244            t = (o, o, o)
245
246            buf = self.archiverClass.archivedDataWithRootObject_(t)
247            self.assertIsInstance(buf, NSData)
248            v = self.unarchiverClass.unarchiveObjectWithData_(buf)
249
250            self.assertIsInstance(v, tuple)
251            self.assertEquals(len(v), 3)
252            self.assertIsInstance(v[0], a_classic_class)
253            self.assertIs(v[0], v[1])
254            self.assertIs(v[0], v[2])
255
256    class TestArchiveSimple (TestKeyedArchiveSimple):
257        def setUp(self):
258            self.archiverClass = NSArchiver
259            self.unarchiverClass = NSUnarchiver
260
261
262    class TestKeyedArchivePlainPython (TestCase, test.pickletester.AbstractPickleTests):
263        # Ensure that we don't run every test case three times
264        def setUp(self):
265            self._protocols = test.pickletester.protocols
266            test.pickletester.protocols = (2,)
267
268        def tearDown(self):
269            test.pickletester.protocols = self._protocols
270
271
272        def dumps(self, arg, proto=0, fast=0):
273            # Ignore proto and fast
274            return NSKeyedArchiver.archivedDataWithRootObject_(arg)
275
276        def loads(self, buf):
277            return NSKeyedUnarchiver.unarchiveObjectWithData_(buf)
278
279
280        # Disable a number of methods, these test things we're not interested in.
281        # (Most of these look at the generated byte-stream, as we're not writing data in pickle's
282        # format such tests are irrelevant to archiving support)
283
284        @onlyIf(0, "python unittest not relevant for archiving")
285        def test_load_classic_instance(self): pass
286
287        @onlyIf(0, "python unittest not relevant for archiving")
288        def test_insecure_strings(self): pass
289
290        @onlyIf(0, "python unittest not relevant for archiving")
291        def test_load_from_canned_string(self): pass
292
293        @onlyIf(0, "python unittest not relevant for archiving")
294        def test_maxint64(self): pass
295
296        @onlyIf(0, "python unittest not relevant for archiving")
297        def test_dict_chunking(self): pass
298
299        @onlyIf(0, "python unittest not relevant for archiving")
300        def test_float_format(self): pass
301
302        @onlyIf(0, "python unittest not relevant for archiving")
303        def test_garyp(self): pass
304
305        @onlyIf(0, "python unittest not relevant for archiving")
306        def test_list_chunking(self): pass
307
308        @onlyIf(0, "python unittest not relevant for archiving")
309        def test_singletons(self): pass
310
311        @onlyIf(0, "python unittest not relevant for archiving")
312        def test_simple_newobj(self): pass
313
314        @onlyIf(0, "python unittest not relevant for archiving")
315        def test_short_tuples(self): pass
316
317        @onlyIf(0, "python unittest not relevant for archiving")
318        def test_proto(self): pass
319
320        @onlyIf(0, "python unittest not relevant for archiving")
321        def test_long1(self): pass
322
323        @onlyIf(0, "python unittest not relevant for archiving")
324        def test_long4(self): pass
325
326        @onlyIf(0, "python unittest not relevant for archiving")
327        def test_get(self): pass
328
329        @onlyIf(0, "python unittest not relevant for archiving")
330        def test_load_from_data0(self): pass
331
332        @onlyIf(0, "python unittest not relevant for archiving")
333        def test_load_from_data1(self): pass
334
335        @onlyIf(0, "python unittest not relevant for archiving")
336        def test_load_from_data2(self): pass
337
338        @onlyIf(0, "python unittest not relevant for archiving")
339        def test_unpickle_from_2x(self): pass
340
341        @onlyIf(0, "python unittest not relevant for archiving")
342        def test_pickle_to_2x(self): pass
343
344        @onlyIf(0, "python unittest not relevant for archiving")
345        def test_bad_getattr(self): pass
346
347        @onlyIf(0, "python unittest not relevant for archiving")
348        def test_unicode(self): pass
349
350
351        def test_long(self):
352            # The real test_long method takes way to much time, test a subset
353            x = 12345678910111213141516178920L << (256*8)
354            buf = self.dumps(x)
355            v = self.loads(buf)
356            self.assertEquals(v, x)
357
358            x = -x
359
360            buf = self.dumps(x)
361            v = self.loads(buf)
362
363            self.assertEquals(v, x)
364
365            for val in (0L, 1L, long(sys.maxint), long(sys.maxint * 128)):
366                for x in val, -val:
367                    buf = self.dumps(x)
368                    v = self.loads(buf)
369                    self.assertEquals(v, x)
370
371
372
373
374
375        # Overriden tests for extension codes, the test code checks
376        # the actual byte stream.
377        def produce_global_ext(self, extcode, opcode):
378            e = test.pickletester.ExtensionSaver(extcode)
379            try:
380                copy_reg.add_extension(__name__, "MyList", extcode)
381                x = MyList([1, 2, 3])
382                x.foo = 42
383                x.bar = "hello"
384
385                s1 = self.dumps(x, 1)
386                y = self.loads(s1)
387                self.assertEqual(list(x), list(y))
388                self.assertEqual(x.__dict__, y.__dict__)
389            finally:
390                e.restore()
391
392        #
393        # The test_reduce* methods iterate over various protocol
394        # versions. Override to only look at protocol version 2.
395        #
396        def test_reduce_overrides_default_reduce_ex(self):
397            for proto in 2,:
398                x = test.pickletester.REX_one()
399                self.assertEqual(x._reduce_called, 0)
400                s = self.dumps(x, proto)
401                self.assertEqual(x._reduce_called, 1)
402                y = self.loads(s)
403                self.assertEqual(y._reduce_called, 0)
404
405        def test_reduce_ex_called(self):
406            for proto in 2,:
407                x = test.pickletester.REX_two()
408                self.assertEqual(x._proto, None)
409                s = self.dumps(x, proto)
410                self.assertEqual(x._proto, proto)
411                y = self.loads(s)
412                self.assertEqual(y._proto, None)
413
414        def test_reduce_ex_overrides_reduce(self):
415            for proto in 2,:
416                x = test.pickletester.REX_three()
417                self.assertEqual(x._proto, None)
418                s = self.dumps(x, proto)
419                self.assertEqual(x._proto, proto)
420                y = self.loads(s)
421                self.assertEqual(y._proto, None)
422
423        def test_reduce_ex_calls_base(self):
424            for proto in 2,:
425                x = test.pickletester.REX_four()
426                self.assertEqual(x._proto, None)
427                s = self.dumps(x, proto)
428                self.assertEqual(x._proto, proto)
429                y = self.loads(s)
430                self.assertEqual(y._proto, proto)
431
432        def test_reduce_calls_base(self):
433            for proto in 2,:
434                x = test.pickletester.REX_five()
435                self.assertEqual(x._reduce_called, 0)
436                s = self.dumps(x, proto)
437                self.assertEqual(x._reduce_called, 1)
438                y = self.loads(s)
439                self.assertEqual(y._reduce_called, 1)
440
441    class TestArchivePlainPython (TestKeyedArchivePlainPython):
442        def setUp(self):
443            self._protocols = test.pickletester.protocols
444            test.pickletester.protocols = (2,)
445
446        def tearDown(self):
447            test.pickletester.protocols = self._protocols
448
449
450        def dumps(self, arg, proto=0, fast=0):
451            # Ignore proto and fast
452            return NSArchiver.archivedDataWithRootObject_(arg)
453
454        def loads(self, buf):
455            return NSUnarchiver.unarchiveObjectWithData_(buf)
456
457
458    #
459    # Disable testing of plain Archiving for now, need full support
460    # for keyed-archiving first, then worry about adding "classic"
461    # archiving.
462    #
463    #class TestArchivePlainPython (TestKeyedArchivePlainPython):
464    #    def dumps(self, arg, proto=0, fast=0):
465    #        # Ignore proto and fast
466    #        return NSArchiver.archivedDataWithRootObject_(arg)
467    #
468    #    def loads(self, buf):
469    #        return NSUnarchiver.unarchiveObjectWithData_(buf)
470
471
472    #
473    # Second set of tests: test if archiving a graph that
474    # contains both python and objective-C objects works correctly.
475    #
476    class TestKeyedArchiveMixedGraphs (TestCase):
477        def dumps(self, arg, proto=0, fast=0):
478            # Ignore proto and fast
479            return NSKeyedArchiver.archivedDataWithRootObject_(arg)
480
481        def loads(self, buf):
482            return NSKeyedUnarchiver.unarchiveObjectWithData_(buf)
483
484        def test_list1(self):
485            o1 = a_classic_class()
486            o2 = a_newstyle_class()
487            o2.lst = NSArray.arrayWithObject_(o1)
488            l = NSArray.arrayWithArray_([o1, o2, [o1, o2]])
489
490            buf = self.dumps(l)
491            self.assertIsInstance(buf, NSData)
492
493            out = self.loads(buf)
494            self.assertIsInstance(out, NSArray)
495            self.assertEquals(len(out), 3)
496
497            p1 = out[0]
498            p2 = out[1]
499            p3 = out[2]
500
501            self.assertIsInstance(p1, a_classic_class)
502            self.assertIsInstance(p2, a_newstyle_class)
503            self.assertIsInstance(p3, list)
504            self.assertIs(p3[0], p1)
505            self.assertIs(p3[1], p2)
506            self.assertIsInstance(p2.lst , NSArray)
507            self.assertIs(p2.lst[0], p1)
508
509
510    class TestArchiveMixedGraphs (TestKeyedArchiveMixedGraphs):
511        def dumps(self, arg, proto=0, fast=0):
512            # Ignore proto and fast
513            return NSArchiver.archivedDataWithRootObject_(arg)
514
515        def loads(self, buf):
516            return NSUnarchiver.unarchiveObjectWithData_(buf)
517
518
519
520    #
521    # And finally some tests to check if archiving of Python
522    # subclasses of NSObject works correctly.
523    #
524    class TestArchivePythonObjCSubclass (TestCase):
525        pass
526
527if __name__ == "__main__":
528    main()
529