1""" 2Tests to ensure that NSDictionary and NSMutableDictionary conform 3to the python dict interface 4 5These tests are basicly a port of the Python 3.2 tests for dictionaries 6and dictionary views. 7 8TODO: 9- Port all tests 10- Do the same for python 2.x 11- Do the same for sets (NSSet) and lists (NSArray) 12""" 13from PyObjCTools.TestSupport import * 14import objc 15 16# Import some of the stdlib tests 17from test import mapping_tests 18 19NSDictionary = objc.lookUpClass('NSDictionary') 20NSMutableDictionary = objc.lookUpClass('NSMutableDictionary') 21 22class TestNSDictionaryInterface (TestCase): 23 def dictClass(self): 24 return NSDictionary 25 26 def createDictionary(self, **kwds): 27 return NSDictionary.dictionaryWithDictionary_(kwds) 28 29 def testConstructor(self): 30 value = self.dictClass()() 31 self.assertEqual(value, {}) 32 33 def testBool(self): 34 self.assertIs(not self.createDictionary(), True) 35 self.assertTrue(self.createDictionary(foo='1')) 36 self.assertIs(bool(self.createDictionary()), False) 37 self.assertIs(bool(self.createDictionary(foo='1')), True) 38 39 def testKeys(self): 40 d = self.createDictionary() 41 self.assertEqual(set(d.keys()), set()) 42 43 d = self.createDictionary(a=1, b=2) 44 k = d.keys() 45 46 self.assertIn('a', d) 47 self.assertIn('b', d) 48 self.assertIn('a', k) 49 self.assertIn('b', k) 50 51 self.assertIsNotInstance(k, list) 52 53 self.assertEqual(repr(self.createDictionary(a=1).keys()), 54 "<nsdict_keys(['a'])>") 55 56 def testValues(self): 57 d = self.createDictionary() 58 self.assertEqual(set(d.values()), set()) 59 60 d = self.createDictionary(a=1) 61 62 self.assertEqual(set(d.values()), {1}) 63 self.assertRaises(TypeError, d.values, None) 64 self.assertIsNotInstance(d.values(), list) 65 66 self.assertEqual(repr(self.createDictionary(a=1).values()), 67 "<nsdict_values([1])>") 68 69 def testItems(self): 70 d = self.createDictionary() 71 self.assertEqual(set(d.items()), set()) 72 73 d = self.createDictionary(a=1) 74 self.assertEqual(set(d.items()), {('a', 1)}) 75 self.assertRaises(TypeError, d.items, None) 76 self.assertEqual(repr(self.createDictionary(a=1).items()), 77 "<nsdict_items([('a', 1)])>") 78 79 def testContains(self): 80 d = self.createDictionary() 81 self.assertNotIn('a', d) 82 self.assertFalse('a' in d) 83 self.assertTrue('a' not in d) 84 85 d = self.createDictionary(a=1, b=2) 86 self.assertIn('a', d) 87 self.assertIn('b', d) 88 self.assertNotIn('c', d) 89 90 self.assertRaises(TypeError, d.__contains__) 91 92 def testLen(self): 93 d = self.createDictionary() 94 self.assertEqual(len(d), 0) 95 96 d = self.createDictionary(a=1, b=2) 97 self.assertEqual(len(d), 2) 98 99 def testGetItem(self): 100 d = self.createDictionary(a=1, b=2) 101 self.assertEqual(d['a'], 1) 102 self.assertEqual(d['b'], 2) 103 104 self.assertRaises(TypeError, d.__getitem__) 105 106 def testFromKeys(self): 107 d = self.dictClass().fromkeys('abc') 108 self.assertEqual(d, {'a':None, 'b':None, 'c':None}) 109 110 d = self.createDictionary() 111 self.assertIsNot(d.fromkeys('abc'), d) 112 self.assertEqual(d.fromkeys('abc'), {'a':None, 'b':None, 'c':None}) 113 self.assertEqual(d.fromkeys((4,5),0), {4:0, 5:0}) 114 self.assertEqual(d.fromkeys([]), {}) 115 116 def g(): 117 yield 1 118 119 self.assertEqual(d.fromkeys(g()), {1:None}) 120 self.assertRaises(TypeError, {}.fromkeys, 3) 121 122 d = self.dictClass()(zip(range(6), range(6))) 123 self.assertEqual(dict.fromkeys(d, 0), dict(zip(range(6), [0]*6))) 124 125 def _testCopy(self): 126 self.fail("Decide what to do w.r.t. -copy and -mutableCopy") 127 128 def testGet(self): 129 d = self.createDictionary() 130 self.assertIs(d.get('c'), None) 131 self.assertEqual(d.get('c', 3), 3) 132 133 134 d = self.createDictionary(a=1, b=2) 135 self.assertIs(d.get('c'), None) 136 self.assertEqual(d.get('c', 3), 3) 137 self.assertEqual(d.get('a'), 1) 138 self.assertEqual(d.get('a', 3), 1) 139 self.assertRaises(TypeError, d.get) 140 self.assertRaises(TypeError, d.get, None, None, None) 141 142 143 def testEq(self): 144 self.assertEqual(self.createDictionary(), self.createDictionary()) 145 self.assertEqual(self.createDictionary(a=1), self.createDictionary(a=1)) 146 147 class Exc(Exception): pass 148 149 class BadCmp(object): 150 def __eq__(self, other): 151 raise Exc() 152 def __hash__(self): 153 return 1 154 155 d1 = {BadCmp(): 1} 156 d2 = {BadCmp(): 1} 157 158 with self.assertRaises(Exc): 159 d1 == d2 160 161 162 def testKeysContained(self): 163 self.helper_keys_contained(lambda x: x.keys()) 164 self.helper_keys_contained(lambda x: x.items()) 165 166 def helper_keys_contained(self, fn): 167 # Test rich comparisons against dict key views, which should behave the 168 # same as sets. 169 170 empty = fn(self.createDictionary()) 171 empty2 = fn(self.createDictionary()) 172 smaller = fn(self.createDictionary(a=1, b=2)) 173 larger = fn(self.createDictionary(a=1, b=2, c=3)) 174 larger2 = fn(self.createDictionary(a=1, b=2, c=3)) 175 larger3 = fn(self.createDictionary(d=1, b=2, c=3)) 176 177 self.assertTrue(smaller < larger) 178 self.assertTrue(smaller <= larger) 179 self.assertTrue(larger > smaller) 180 self.assertTrue(larger >= smaller) 181 182 self.assertFalse(smaller >= larger) 183 self.assertFalse(smaller > larger) 184 self.assertFalse(larger <= smaller) 185 self.assertFalse(larger < smaller) 186 187 self.assertFalse(smaller < larger3) 188 self.assertFalse(smaller <= larger3) 189 self.assertFalse(larger3 > smaller) 190 self.assertFalse(larger3 >= smaller) 191 192 # Inequality strictness 193 self.assertTrue(larger2 >= larger) 194 self.assertTrue(larger2 <= larger) 195 self.assertFalse(larger2 > larger) 196 self.assertFalse(larger2 < larger) 197 198 self.assertTrue(larger == larger2) 199 self.assertTrue(smaller != larger) 200 201 # There is an optimization on the zero-element case. 202 self.assertTrue(empty == empty2) 203 self.assertFalse(empty != empty2) 204 self.assertFalse(empty == smaller) 205 self.assertTrue(empty != smaller) 206 207 # With the same size, an elementwise compare happens 208 self.assertTrue(larger != larger3) 209 self.assertFalse(larger == larger3) 210 211 def testErrorsInViewContainmentCheck(self): 212 class C: 213 def __eq__(self, other): 214 raise RuntimeError() 215 216 d1 = self.dictClass().dictionaryWithDictionary_({1: C()}) 217 d2 = self.dictClass().dictionaryWithDictionary_({1: C()}) 218 219 with self.assertRaises(RuntimeError): 220 d1.items() == d2.items() 221 with self.assertRaises(RuntimeError): 222 d1.items() != d2.items() 223 with self.assertRaises(RuntimeError): 224 d1.items() <= d2.items() 225 with self.assertRaises(RuntimeError): 226 d1.items() >= d2.items() 227 228 d3 = self.dictClass().dictionaryWithDictionary_({1: C(), 2: C()}) 229 with self.assertRaises(RuntimeError): 230 d2.items() < d3.items() 231 with self.assertRaises(RuntimeError): 232 d3.items() > d2.items() 233 234 k1 = self.dictClass().dictionaryWithDictionary_({1:1, 2:2}).keys() 235 k2 = self.dictClass().dictionaryWithDictionary_({1:1, 2:2, 3:3}).keys() 236 k3 = self.dictClass().dictionaryWithDictionary_({4:4}).keys() 237 238 self.assertEqual(k1 - k2, set()) 239 self.assertEqual(k1 - k3, {1,2}) 240 self.assertEqual(k2 - k1, {3}) 241 self.assertEqual(k3 - k1, {4}) 242 self.assertEqual(k1 & k2, {1,2}) 243 self.assertEqual(k1 & k3, set()) 244 self.assertEqual(k1 | k2, {1,2,3}) 245 self.assertEqual(k1 ^ k2, {3}) 246 self.assertEqual(k1 ^ k3, {1,2,4}) 247 248 def testDictviewSetOperationsOnItems(self): 249 k1 = self.dictClass().dictionaryWithDictionary_({1:1, 2:2}).items() 250 k2 = self.dictClass().dictionaryWithDictionary_({1:1, 2:2, 3:3}).items() 251 k3 = self.dictClass().dictionaryWithDictionary_({4:4}).items() 252 253 self.assertEqual(k1 - k2, set()) 254 self.assertEqual(k1 - k3, {(1,1), (2,2)}) 255 self.assertEqual(k2 - k1, {(3,3)}) 256 self.assertEqual(k3 - k1, {(4,4)}) 257 self.assertEqual(k1 & k2, {(1,1), (2,2)}) 258 self.assertEqual(k1 & k3, set()) 259 self.assertEqual(k1 | k2, {(1,1), (2,2), (3,3)}) 260 self.assertEqual(k1 ^ k2, {(3,3)}) 261 self.assertEqual(k1 ^ k3, {(1,1), (2,2), (4,4)}) 262 263 def testDictviewMixedSetOperations(self): 264 # Just a few for .keys() 265 self.assertTrue(self.dictClass().dictionaryWithDictionary_({1:1}).keys() == {1}) 266 self.assertTrue({1} == self.dictClass().dictionaryWithDictionary_({1:1}).keys()) 267 self.assertEqual(self.dictClass().dictionaryWithDictionary_({1:1}).keys() | {2}, {1, 2}) 268 self.assertEqual({2} | self.dictClass().dictionaryWithDictionary_({1:1}).keys(), {1, 2}) 269 # And a few for .items() 270 self.assertTrue(self.dictClass().dictionaryWithDictionary_({1:1}).items() == {(1,1)}) 271 self.assertTrue({(1,1)} == self.dictClass().dictionaryWithDictionary_({1:1}).items()) 272 self.assertEqual(self.dictClass().dictionaryWithDictionary_({1:1}).items() | {2}, {(1,1), 2}) 273 self.assertEqual({2} | self.dictClass().dictionaryWithDictionary_({1:1}).items(), {(1,1), 2}) 274 275 276class TestNSMutableDictionaryInterface (TestNSDictionaryInterface): 277 def dictClass(self): 278 return NSMutableDictionary 279 280 def createDictionary(self, **kwds): 281 return NSMutableDictionary.dictionaryWithDictionary_(kwds) 282 283 def testKeysMutable(self): 284 d = self.createDictionary() 285 self.assertEqual(set(d.keys()), set()) 286 287 d = self.createDictionary(a=1, b=2) 288 k = d.keys() 289 290 291 self.assertIn('a', d) 292 self.assertIn('b', d) 293 self.assertIn('a', k) 294 self.assertIn('b', k) 295 296 self.assertIsNotInstance(k, list) 297 298 self.assertNotIn('c', d) 299 self.assertNotIn('c', k) 300 301 d['c'] = 3 302 self.assertIn('c', d) 303 self.assertIn('c', k) 304 305 def testValuesMutable(self): 306 d = self.createDictionary() 307 self.assertEqual(set(d.values()), set()) 308 309 d = self.createDictionary(a=1) 310 v = d.values() 311 312 self.assertEqual(set(v), {1}) 313 314 d['b'] = 2 315 self.assertEqual(set(v), {1, 2}) 316 317 def testItemsMutable(self): 318 d = self.createDictionary() 319 self.assertEqual(set(d.items()), set()) 320 321 d = self.createDictionary(a=1) 322 i = d.items() 323 324 self.assertEqual(set(i), {('a', 1)}) 325 326 d['b'] = 2 327 328 self.assertEqual(set(i), {('a', 1), ('b', 2)}) 329 330 def testGetItem(self): 331 d = self.createDictionary(a=1, b=2) 332 self.assertEqual(d['a'], 1) 333 self.assertEqual(d['b'], 2) 334 335 d['c'] = 3 336 d['a'] = 4 337 self.assertEqual(d['c'], 3) 338 self.assertEqual(d['a'], 4) 339 del d['b'] 340 self.assertEqual(d, {'a': 4, 'c': 3}) 341 342 self.assertRaises(TypeError, d.__getitem__) 343 344 class Exc (Exception): pass 345 346 class BadEq(object): 347 def __eq__(self, other): 348 raise Exc() 349 def __hash__(self): 350 return hash(23) 351 352 d = self.createDictionary() 353 d[BadEq()] = 42 354 self.assertRaises(KeyError, d.__getitem__, 23) 355 356 class BadHash(object): 357 fail = False 358 def __hash__(self): 359 if self.fail: 360 raise Exc() 361 else: 362 return 42 363 364 d = self.createDictionary() 365 x = BadHash() 366 d[x] = 42 367 x.fail = True 368 369 # FIXME 370 #self.assertRaises(Exc, d.__getitem__, x) 371 372 def testClear(self): 373 d = self.createDictionary(a=1, b=2, c=3) 374 d.clear() 375 376 self.assertEqual(d, {}) 377 378 self.assertRaises(TypeError, d.clear, None) 379 380 def testUpdate(self): 381 d = self.createDictionary() 382 d.update({1:100}) 383 d.update({2:20}) 384 d.update({1:1, 2:2, 3:3}) 385 self.assertEqual(d, {1:1, 2:2, 3:3}) 386 387 d.update() 388 self.assertEqual(d, {1:1, 2:2, 3:3}) 389 390 self.assertRaises((TypeError, AttributeError), d.update, None) 391 392 class SimpleUserDict: 393 def __init__(self): 394 self.d = {1:1, 2:2, 3:3} 395 def keys(self): 396 return self.d.keys() 397 def __getitem__(self, i): 398 return self.d[i] 399 400 d = self.createDictionary() 401 d.update(SimpleUserDict()) 402 self.assertEqual(d, {1:1, 2:2, 3:3}) 403 404 class Exc(Exception): pass 405 406 d.clear() 407 408 class FailingUserDict: 409 def keys(self): 410 raise Exc() 411 412 self.assertRaises(Exc, d.update, FailingUserDict()) 413 414 415 class FailingUserDict: 416 def keys(self): 417 class BogonIter: 418 def __init__(self): 419 self.i = 1 420 def __iter__(self): 421 return self 422 def __next__(self): 423 if self.i: 424 self.i = 0 425 return 'a' 426 raise Exc 427 return BogonIter() 428 def __getitem__(self, key): 429 return key 430 431 self.assertRaises(Exc, d.update, FailingUserDict()) 432 433 434 class FailingUserDict: 435 def keys(self): 436 class BogonIter: 437 def __init__(self): 438 self.i = ord('a') 439 def __iter__(self): 440 return self 441 def __next__(self): 442 if self.i <= ord('z'): 443 rtn = chr(self.i) 444 self.i += 1 445 return rtn 446 raise StopIteration 447 return BogonIter() 448 def __getitem__(self, key): 449 raise Exc 450 self.assertRaises(Exc, d.update, FailingUserDict()) 451 452 class badseq(object): 453 def __iter__(self): 454 return self 455 def __next__(self): 456 raise Exc() 457 458 self.assertRaises(Exc, {}.update, badseq()) 459 self.assertRaises(ValueError, {}.update, [(1, 2, 3)]) 460 461 462 def setDefault(self): 463 d = self.createDictionary() 464 self.assertIs(d.setdefault('key0'), None) 465 d.setdefault('key0', []) 466 self.assertIs(d.setdefault('key0'), None) 467 d.setdefault('key', []).append(3) 468 self.assertEqual(d['key'][0], 3) 469 d.setdefault('key', []).append(4) 470 self.assertEqual(len(d['key']), 2) 471 self.assertRaises(TypeError, d.setdefault) 472 473 class Exc (Exception): pass 474 475 class BadHash(object): 476 fail = False 477 def __hash__(self): 478 if self.fail: 479 raise Exc() 480 else: 481 return 42 482 483 x = BadHash() 484 d[x] = 42 485 x.fail = True 486 self.assertRaises(Exc, d.setdefault, x, []) 487 488 def testPopitem(self): 489 for copymode in -1, +1: 490 # -1: b has same structure as a 491 # +1: b is a.copy() 492 for log2size in range(12): 493 size = 2**log2size 494 a = self.createDictionary() 495 b = self.createDictionary() 496 for i in range(size): 497 a[repr(i)] = i 498 if copymode < 0: 499 b[repr(i)] = i 500 if copymode > 0: 501 b = a.mutableCopy() 502 for i in range(size): 503 ka, va = ta = a.popitem() 504 self.assertEqual(va, int(ka)) 505 kb, vb = tb = b.popitem() 506 self.assertEqual(vb, int(kb)) 507 self.assertFalse(copymode < 0 and ta != tb) 508 self.assertFalse(a) 509 self.assertFalse(b) 510 511 d = self.createDictionary() 512 self.assertRaises(KeyError, d.popitem) 513 514 515 def testPop(self): 516 d = self.createDictionary() 517 k, v = 'abc', 'def' 518 d[k] = v 519 self.assertRaises(KeyError, d.pop, 'ghi') 520 521 self.assertEqual(d.pop(k), v) 522 self.assertEqual(len(d), 0) 523 524 self.assertRaises(KeyError, d.pop, k) 525 526 self.assertEqual(d.pop(k, v), v) 527 d[k] = v 528 self.assertEqual(d.pop(k, 1), v) 529 530 self.assertRaises(TypeError, d.pop) 531 532 class Exc(Exception): pass 533 534 class BadHash(object): 535 fail = False 536 def __hash__(self): 537 if self.fail: 538 raise Exc() 539 else: 540 return 42 541 542 #x = BadHash() 543 #d[x] = 42 544 #x.fail = True 545 #self.assertRaises(Exc, d.pop, x) 546 547class DictSetTest (TestCase): 548 testclass = NSDictionary 549 550 def testDictKeys(self): 551 d = self.testclass.dictionaryWithDictionary_({1: 10, "a": "ABC"}) 552 553 keys = d.keys() 554 555 self.assertEqual(len(keys), 2) 556 self.assertEqual(set(keys), {1, "a"}) 557 self.assertEqual(keys, {1, "a"}) 558 self.assertNotEqual(keys, {1, "a", "b"}) 559 self.assertNotEqual(keys, {1, "b"}) 560 self.assertNotEqual(keys, {1}) 561 self.assertNotEqual(keys, 42) 562 self.assertIn(1, keys) 563 self.assertIn("a", keys) 564 self.assertNotIn(10, keys) 565 self.assertNotIn("Z", keys) 566 self.assertEqual(d.keys(), d.keys()) 567 568 e = self.testclass.dictionaryWithDictionary_({1: 11, "a": "def"}) 569 self.assertEqual(d.keys(), e.keys()) 570 571 def testDictItems(self): 572 d = self.testclass.dictionaryWithDictionary_({1: 10, "a": "ABC"}) 573 items = d.items() 574 575 self.assertEqual(len(items), 2) 576 self.assertEqual(set(items), {(1, 10), ("a", "ABC")}) 577 self.assertEqual(items, {(1, 10), ("a", "ABC")}) 578 self.assertNotEqual(items, {(1, 10), ("a", "ABC"), "junk"}) 579 self.assertNotEqual(items, {(1, 10), ("a", "def")}) 580 self.assertNotEqual(items, {(1, 10)}) 581 self.assertNotEqual(items, 42) 582 self.assertIn((1, 10), items) 583 self.assertIn(("a", "ABC"), items) 584 self.assertNotIn((1, 11), items) 585 self.assertNotIn(1, items) 586 self.assertNotIn((), items) 587 self.assertNotIn((1,), items) 588 self.assertNotIn((1, 2, 3), items) 589 self.assertEqual(d.items(), d.items()) 590 591 e = d.copy() 592 self.assertEqual(d.items(), e.items()) 593 594 595 596class DictSetTest (DictSetTest): 597 testclass = NSMutableDictionary 598 599 def testDictKeysMutable(self): 600 d = self.testclass.dictionaryWithDictionary_({1: 10, "a": "ABC"}) 601 e = self.testclass.dictionaryWithDictionary_({1: 11, "a": "def"}) 602 self.assertEqual(d.keys(), e.keys()) 603 604 del e["a"] 605 self.assertNotEqual(d.keys(), e.keys()) 606 607 def testDictItemsMutable(self): 608 d = self.testclass.dictionaryWithDictionary_({1: 10, "a": "ABC"}) 609 610 e = d.copy() 611 self.assertEqual(d.items(), e.items()) 612 d["a"] = "def" 613 self.assertNotEqual(d.items(), e.items()) 614 615 def testDictMixedKeysItems(self): 616 d = self.testclass.dictionaryWithDictionary_( 617 {(1, 1): 11, (2, 2): 22}) 618 e = self.testclass.dictionaryWithDictionary_( 619 {1: 1, 2: 2}) 620 self.assertEqual(d.keys(), e.items()) 621 self.assertNotEqual(d.items(), e.keys()) 622 623 def testDictValues(self): 624 d = self.testclass.dictionaryWithDictionary_( 625 {1: 10, "a": "ABC"}) 626 values = d.values() 627 self.assertEqual(set(values), {10, "ABC"}) 628 self.assertEqual(len(values), 2) 629 630 631 632 633 634class GeneralMappingTestsNSMutableDictionary ( 635 mapping_tests.BasicTestMappingProtocol): 636 type2test = NSMutableDictionary 637 638 639import collections 640 641class TestABC (TestCase): 642 def testDictABC(self): 643 self.assertTrue(issubclass(NSDictionary, collections.Mapping)) 644 self.assertTrue(issubclass(NSMutableDictionary, collections.Mapping)) 645 self.assertTrue(issubclass(NSMutableDictionary, collections.MutableMapping)) 646 647 def testViewABC(self): 648 d = NSDictionary.dictionary() 649 self.assertTrue(isinstance(d.keys(), collections.KeysView)) 650 self.assertTrue(isinstance(d.values(), collections.ValuesView)) 651 self.assertTrue(isinstance(d.items(), collections.ItemsView)) 652 653 d = NSMutableDictionary.dictionary() 654 self.assertTrue(isinstance(d.keys(), collections.KeysView)) 655 self.assertTrue(isinstance(d.values(), collections.ValuesView)) 656 self.assertTrue(isinstance(d.items(), collections.ItemsView)) 657 658if __name__ == "__main__": 659 main() 660