test_setutils.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. # -*- coding: utf-8 -*-
  2. import platform
  3. from pytest import raises
  4. from boltons.setutils import IndexedSet, _MISSING, complement
  5. _IS_26 = platform.python_version().startswith('2.6')
  6. def test_indexed_set_basic():
  7. zero2nine = IndexedSet(range(10))
  8. five2nine = zero2nine & IndexedSet(range(5, 15))
  9. x = IndexedSet(five2nine)
  10. x |= set([10])
  11. assert list(zero2nine) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  12. assert set(zero2nine) == set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  13. assert list(five2nine) == [5, 6, 7, 8, 9]
  14. assert x == IndexedSet([5, 6, 7, 8, 9, 10])
  15. assert x[-1] == 10
  16. assert zero2nine ^ five2nine == IndexedSet([0, 1, 2, 3, 4])
  17. assert x[:3] == IndexedSet([5, 6, 7])
  18. assert x[2:4:-1] == IndexedSet([8, 7])
  19. def test_indexed_set_rsub():
  20. "From #252"
  21. assert (set('abc') - IndexedSet('bcd')) == set(['a'])
  22. assert (IndexedSet('abc') - IndexedSet('bcd')) == IndexedSet(['a'])
  23. assert (frozenset('abc') - IndexedSet('bcd')) == frozenset(['a'])
  24. def test_indexed_set_mutate():
  25. thou = IndexedSet(range(1000))
  26. assert (thou.pop(), thou.pop()) == (999, 998)
  27. assert (thou.pop(499), thou.pop(499)) == (499, 500)
  28. ref = [495, 496, 497, 498, 501, 502, 503, 504, 505, 506]
  29. assert [thou[i] for i in range(495, 505)] == ref
  30. assert len(thou) == 996
  31. while len(thou) > 600:
  32. dead_idx_len = len(thou.dead_indices)
  33. dead_idx_count = thou._dead_index_count
  34. thou.pop(0)
  35. new_dead_idx_len = len(thou.dead_indices)
  36. if new_dead_idx_len < dead_idx_len:
  37. assert dead_idx_count > 0
  38. # 124, 109, 95
  39. assert len(thou) == 600
  40. assert thou._dead_index_count == 67
  41. assert not any([thou[i] is _MISSING for i in range(len(thou))])
  42. thou &= IndexedSet(range(500, 503))
  43. assert thou == IndexedSet([501, 502])
  44. return
  45. def big_popper():
  46. # more of a benchmark than a test
  47. from os import urandom
  48. import time
  49. big_set = IndexedSet(range(100000))
  50. rands = [ord(r) for r in urandom(len(big_set))]
  51. start_time, start_size = time.time(), len(big_set)
  52. while len(big_set) > 10000:
  53. if len(big_set) % 10000 == 0:
  54. print(len(big_set) / 10000)
  55. rand = rands.pop()
  56. big_set.pop(rand)
  57. big_set.pop(-rand)
  58. end_time, end_size = time.time(), len(big_set)
  59. print()
  60. print('popped %s items in %s seconds' % (start_size - end_size,
  61. end_time - start_time))
  62. def test_complement_set():
  63. '''exercises a bunch of code-paths but doesn't really confirm math identities'''
  64. assert complement(complement(set())) == set()
  65. sab = set('ab')
  66. sbc = set('bc')
  67. cab = complement('ab')
  68. cbc = complement('bc')
  69. cc = complement('c')
  70. sc = set('c')
  71. u = complement(set())
  72. assert repr(sab) in repr(cab)
  73. # non-mutating tests
  74. assert cab != cbc
  75. assert complement(cab) == sab
  76. assert complement(cbc) == sbc
  77. assert 'a' not in cab
  78. assert 'c' in cab
  79. assert (sab & cbc) == (sab - sbc) # set theory invariant
  80. assert not (cab < sab) # complement never subset of set
  81. if not _IS_26: assert not (sab < cab)
  82. assert not (cbc < sab)
  83. assert not (cbc < cab) # not subsets of each other
  84. if not _IS_26: assert sab < cc
  85. assert cab < (cab | cbc)
  86. assert (cab | cbc) > cab
  87. assert cc > sab
  88. assert not (cab > sab)
  89. assert not cab.isdisjoint(cc) # complements never disjoint
  90. assert cab.isdisjoint(sab)
  91. assert not cab.isdisjoint(sc)
  92. assert (cab | sab) == u
  93. assert (cab | cc) == u
  94. assert (cab | cbc) == complement('b')
  95. assert (sab | cab) == (cbc | sbc)
  96. assert (sab & cab) == (cbc & sbc)
  97. assert (sab ^ cab) == (cbc ^ sbc)
  98. assert cab - cc == sc
  99. assert cab - sab == cab
  100. assert sab - cab == sab
  101. assert (cab ^ cbc | set('b')) == (sab | sbc)
  102. everything = complement(frozenset())
  103. assert everything in everything # https://en.wikipedia.org/wiki/Russell%27s_paradox
  104. assert bool(cab)
  105. assert not complement(u)
  106. # destructive testing
  107. cab ^= sab
  108. cab ^= sab
  109. cab &= sab
  110. cab &= cbc
  111. cab |= sab
  112. cab |= cbc
  113. cab -= sab
  114. cab.add(5)
  115. cab.remove(5)
  116. cab.update(sab)
  117. cab.discard(sab)
  118. cab.update(cbc)
  119. cab.add(complement(frozenset())) # frozen complement can be a member of complement set
  120. assert len({complement(frozenset()): 1, complement(frozenset()): 2}) == 1 # hash works
  121. with raises(NotImplementedError): cab.pop()
  122. with raises(NotImplementedError): len(cab)
  123. with raises(NotImplementedError): iter(cab)
  124. ~cab
  125. cab.complement()
  126. cab.complemented()
  127. class OpOverloader(object):
  128. # tests that operators properly return NotImplemented so they will defer to
  129. # another class implementation if available
  130. def __and__(self, other): return self
  131. __rand__ = __iand__ = __or__ = __ror__ = __ior__ = __xor__ = __rxor__ = __sub__ = __isub__ = __and__
  132. def __le__(self, other): return True
  133. __lt__ = __ge__ = __gt__ = __le__
  134. ops = OpOverloader()
  135. def opsmash(a, b):
  136. a &= b; a |= b; a -= b; a ^= b
  137. a > b; a >= b; a < b; a <= b
  138. return (((a & b) | b) - b) ^ b
  139. with raises(TypeError): opsmash(cab, object())
  140. assert opsmash(ops, cab) == ops
  141. assert opsmash(cab, ops) == ops
  142. def test_iset_index_method():
  143. original_list = list(range(8, 20)) + list(range(8))
  144. indexed_list = IndexedSet()
  145. for i in original_list:
  146. indexed_list.add(i)
  147. for i in original_list:
  148. index = indexed_list.index(i)
  149. # if we're removing them in order, the target value should always be at index 0
  150. assert index == 0
  151. indexed_list.pop(index)
  152. indexed_list = IndexedSet(range(10))
  153. for i in reversed(range(10)):
  154. if i % 2:
  155. continue
  156. index = indexed_list.index(i)
  157. assert i == indexed_list.pop(index)
  158. indexed_list = IndexedSet(range(32))
  159. for i in list(indexed_list):
  160. if i % 3:
  161. index = indexed_list.index(i)
  162. assert i == indexed_list.pop(index)
  163. indexed_list = IndexedSet(range(10))
  164. for i in range(10):
  165. if i < 3:
  166. continue
  167. index = indexed_list.index(i)
  168. assert i == indexed_list.pop(index)
  169. indexed_list = IndexedSet(range(32))
  170. for i in list(indexed_list):
  171. if i % 3:
  172. index = indexed_list.index(i)
  173. assert i == indexed_list.pop(index)