/Lib/test/test_sort.py

http://unladen-swallow.googlecode.com/ · Python · 290 lines · 220 code · 48 blank · 22 comment · 32 complexity · 918568e164a2488abd9008ab79ec9441 MD5 · raw file

  1. from test import test_support
  2. import random
  3. import sys
  4. import unittest
  5. verbose = test_support.verbose
  6. nerrors = 0
  7. def check(tag, expected, raw, compare=None):
  8. global nerrors
  9. if verbose:
  10. print " checking", tag
  11. orig = raw[:] # save input in case of error
  12. if compare:
  13. raw.sort(compare)
  14. else:
  15. raw.sort()
  16. if len(expected) != len(raw):
  17. print "error in", tag
  18. print "length mismatch;", len(expected), len(raw)
  19. print expected
  20. print orig
  21. print raw
  22. nerrors += 1
  23. return
  24. for i, good in enumerate(expected):
  25. maybe = raw[i]
  26. if good is not maybe:
  27. print "error in", tag
  28. print "out of order at index", i, good, maybe
  29. print expected
  30. print orig
  31. print raw
  32. nerrors += 1
  33. return
  34. class TestBase(unittest.TestCase):
  35. def testStressfully(self):
  36. # Try a variety of sizes at and around powers of 2, and at powers of 10.
  37. sizes = [0]
  38. for power in range(1, 10):
  39. n = 2 ** power
  40. sizes.extend(range(n-1, n+2))
  41. sizes.extend([10, 100, 1000])
  42. class Complains(object):
  43. maybe_complain = True
  44. def __init__(self, i):
  45. self.i = i
  46. def __lt__(self, other):
  47. if Complains.maybe_complain and random.random() < 0.001:
  48. if verbose:
  49. print " complaining at", self, other
  50. raise RuntimeError
  51. return self.i < other.i
  52. def __repr__(self):
  53. return "Complains(%d)" % self.i
  54. class Stable(object):
  55. def __init__(self, key, i):
  56. self.key = key
  57. self.index = i
  58. def __cmp__(self, other):
  59. return cmp(self.key, other.key)
  60. __hash__ = None # Silence Py3k warning
  61. def __repr__(self):
  62. return "Stable(%d, %d)" % (self.key, self.index)
  63. for n in sizes:
  64. x = range(n)
  65. if verbose:
  66. print "Testing size", n
  67. s = x[:]
  68. check("identity", x, s)
  69. s = x[:]
  70. s.reverse()
  71. check("reversed", x, s)
  72. s = x[:]
  73. random.shuffle(s)
  74. check("random permutation", x, s)
  75. y = x[:]
  76. y.reverse()
  77. s = x[:]
  78. check("reversed via function", y, s, lambda a, b: cmp(b, a))
  79. if verbose:
  80. print " Checking against an insane comparison function."
  81. print " If the implementation isn't careful, this may segfault."
  82. s = x[:]
  83. s.sort(lambda a, b: int(random.random() * 3) - 1)
  84. check("an insane function left some permutation", x, s)
  85. x = [Complains(i) for i in x]
  86. s = x[:]
  87. random.shuffle(s)
  88. Complains.maybe_complain = True
  89. it_complained = False
  90. try:
  91. s.sort()
  92. except RuntimeError:
  93. it_complained = True
  94. if it_complained:
  95. Complains.maybe_complain = False
  96. check("exception during sort left some permutation", x, s)
  97. s = [Stable(random.randrange(10), i) for i in xrange(n)]
  98. augmented = [(e, e.index) for e in s]
  99. augmented.sort() # forced stable because ties broken by index
  100. x = [e for e, i in augmented] # a stable sort of s
  101. check("stability", x, s)
  102. #==============================================================================
  103. class TestBugs(unittest.TestCase):
  104. def test_bug453523(self):
  105. # bug 453523 -- list.sort() crasher.
  106. # If this fails, the most likely outcome is a core dump.
  107. # Mutations during a list sort should raise a ValueError.
  108. class C:
  109. def __lt__(self, other):
  110. if L and random.random() < 0.75:
  111. L.pop()
  112. else:
  113. L.append(3)
  114. return random.random() < 0.5
  115. L = [C() for i in range(50)]
  116. self.assertRaises(ValueError, L.sort)
  117. def test_cmpNone(self):
  118. # Testing None as a comparison function.
  119. L = range(50)
  120. random.shuffle(L)
  121. L.sort(None)
  122. self.assertEqual(L, range(50))
  123. def test_undetected_mutation(self):
  124. # Python 2.4a1 did not always detect mutation
  125. memorywaster = []
  126. for i in range(20):
  127. def mutating_cmp(x, y):
  128. L.append(3)
  129. L.pop()
  130. return cmp(x, y)
  131. L = [1,2]
  132. self.assertRaises(ValueError, L.sort, mutating_cmp)
  133. def mutating_cmp(x, y):
  134. L.append(3)
  135. del L[:]
  136. return cmp(x, y)
  137. self.assertRaises(ValueError, L.sort, mutating_cmp)
  138. memorywaster = [memorywaster]
  139. #==============================================================================
  140. class TestDecorateSortUndecorate(unittest.TestCase):
  141. def test_decorated(self):
  142. data = 'The quick Brown fox Jumped over The lazy Dog'.split()
  143. copy = data[:]
  144. random.shuffle(data)
  145. data.sort(key=str.lower)
  146. copy.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
  147. def test_baddecorator(self):
  148. data = 'The quick Brown fox Jumped over The lazy Dog'.split()
  149. self.assertRaises(TypeError, data.sort, None, lambda x,y: 0)
  150. def test_stability(self):
  151. data = [(random.randrange(100), i) for i in xrange(200)]
  152. copy = data[:]
  153. data.sort(key=lambda (x,y): x) # sort on the random first field
  154. copy.sort() # sort using both fields
  155. self.assertEqual(data, copy) # should get the same result
  156. def test_cmp_and_key_combination(self):
  157. # Verify that the wrapper has been removed
  158. def compare(x, y):
  159. self.assertEqual(type(x), str)
  160. self.assertEqual(type(x), str)
  161. return cmp(x, y)
  162. data = 'The quick Brown fox Jumped over The lazy Dog'.split()
  163. data.sort(cmp=compare, key=str.lower)
  164. def test_badcmp_with_key(self):
  165. # Verify that the wrapper has been removed
  166. data = 'The quick Brown fox Jumped over The lazy Dog'.split()
  167. self.assertRaises(TypeError, data.sort, "bad", str.lower)
  168. def test_key_with_exception(self):
  169. # Verify that the wrapper has been removed
  170. data = range(-2,2)
  171. dup = data[:]
  172. self.assertRaises(ZeroDivisionError, data.sort, None, lambda x: 1/x)
  173. self.assertEqual(data, dup)
  174. def test_key_with_mutation(self):
  175. data = range(10)
  176. def k(x):
  177. del data[:]
  178. data[:] = range(20)
  179. return x
  180. self.assertRaises(ValueError, data.sort, key=k)
  181. def test_key_with_mutating_del(self):
  182. data = range(10)
  183. class SortKiller(object):
  184. def __init__(self, x):
  185. pass
  186. def __del__(self):
  187. del data[:]
  188. data[:] = range(20)
  189. self.assertRaises(ValueError, data.sort, key=SortKiller)
  190. def test_key_with_mutating_del_and_exception(self):
  191. data = range(10)
  192. ## dup = data[:]
  193. class SortKiller(object):
  194. def __init__(self, x):
  195. if x > 2:
  196. raise RuntimeError
  197. def __del__(self):
  198. del data[:]
  199. data[:] = range(20)
  200. self.assertRaises(RuntimeError, data.sort, key=SortKiller)
  201. ## major honking subtlety: we *can't* do:
  202. ##
  203. ## self.assertEqual(data, dup)
  204. ##
  205. ## because there is a reference to a SortKiller in the
  206. ## traceback and by the time it dies we're outside the call to
  207. ## .sort() and so the list protection gimmicks are out of
  208. ## date (this cost some brain cells to figure out...).
  209. def test_reverse(self):
  210. data = range(100)
  211. random.shuffle(data)
  212. data.sort(reverse=True)
  213. self.assertEqual(data, range(99,-1,-1))
  214. self.assertRaises(TypeError, data.sort, "wrong type")
  215. def test_reverse_stability(self):
  216. data = [(random.randrange(100), i) for i in xrange(200)]
  217. copy1 = data[:]
  218. copy2 = data[:]
  219. data.sort(cmp=lambda x,y: cmp(x[0],y[0]), reverse=True)
  220. copy1.sort(cmp=lambda x,y: cmp(y[0],x[0]))
  221. self.assertEqual(data, copy1)
  222. copy2.sort(key=lambda x: x[0], reverse=True)
  223. self.assertEqual(data, copy2)
  224. #==============================================================================
  225. def test_main(verbose=None):
  226. test_classes = (
  227. TestBase,
  228. TestDecorateSortUndecorate,
  229. TestBugs,
  230. )
  231. test_support.run_unittest(*test_classes)
  232. # verify reference counting
  233. if verbose and hasattr(sys, "gettotalrefcount"):
  234. import gc
  235. counts = [None] * 5
  236. for i in xrange(len(counts)):
  237. test_support.run_unittest(*test_classes)
  238. gc.collect()
  239. counts[i] = sys.gettotalrefcount()
  240. print counts
  241. if __name__ == "__main__":
  242. test_main(verbose=True)