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