/Lib/test/test_bisect.py

http://unladen-swallow.googlecode.com/ · Python · 317 lines · 257 code · 47 blank · 13 comment · 32 complexity · e01cbd4662cb6d3cd8d4c025920e1654 MD5 · raw file

  1. import sys
  2. import unittest
  3. from test import test_support
  4. from UserList import UserList
  5. # We do a bit of trickery here to be able to test both the C implementation
  6. # and the Python implementation of the module.
  7. # Make it impossible to import the C implementation anymore.
  8. sys.modules['_bisect'] = 0
  9. # We must also handle the case that bisect was imported before.
  10. if 'bisect' in sys.modules:
  11. del sys.modules['bisect']
  12. # Now we can import the module and get the pure Python implementation.
  13. import bisect as py_bisect
  14. # Restore everything to normal.
  15. del sys.modules['_bisect']
  16. del sys.modules['bisect']
  17. # This is now the module with the C implementation.
  18. import bisect as c_bisect
  19. class TestBisect(unittest.TestCase):
  20. module = None
  21. def setUp(self):
  22. self.precomputedCases = [
  23. (self.module.bisect_right, [], 1, 0),
  24. (self.module.bisect_right, [1], 0, 0),
  25. (self.module.bisect_right, [1], 1, 1),
  26. (self.module.bisect_right, [1], 2, 1),
  27. (self.module.bisect_right, [1, 1], 0, 0),
  28. (self.module.bisect_right, [1, 1], 1, 2),
  29. (self.module.bisect_right, [1, 1], 2, 2),
  30. (self.module.bisect_right, [1, 1, 1], 0, 0),
  31. (self.module.bisect_right, [1, 1, 1], 1, 3),
  32. (self.module.bisect_right, [1, 1, 1], 2, 3),
  33. (self.module.bisect_right, [1, 1, 1, 1], 0, 0),
  34. (self.module.bisect_right, [1, 1, 1, 1], 1, 4),
  35. (self.module.bisect_right, [1, 1, 1, 1], 2, 4),
  36. (self.module.bisect_right, [1, 2], 0, 0),
  37. (self.module.bisect_right, [1, 2], 1, 1),
  38. (self.module.bisect_right, [1, 2], 1.5, 1),
  39. (self.module.bisect_right, [1, 2], 2, 2),
  40. (self.module.bisect_right, [1, 2], 3, 2),
  41. (self.module.bisect_right, [1, 1, 2, 2], 0, 0),
  42. (self.module.bisect_right, [1, 1, 2, 2], 1, 2),
  43. (self.module.bisect_right, [1, 1, 2, 2], 1.5, 2),
  44. (self.module.bisect_right, [1, 1, 2, 2], 2, 4),
  45. (self.module.bisect_right, [1, 1, 2, 2], 3, 4),
  46. (self.module.bisect_right, [1, 2, 3], 0, 0),
  47. (self.module.bisect_right, [1, 2, 3], 1, 1),
  48. (self.module.bisect_right, [1, 2, 3], 1.5, 1),
  49. (self.module.bisect_right, [1, 2, 3], 2, 2),
  50. (self.module.bisect_right, [1, 2, 3], 2.5, 2),
  51. (self.module.bisect_right, [1, 2, 3], 3, 3),
  52. (self.module.bisect_right, [1, 2, 3], 4, 3),
  53. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0),
  54. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 1),
  55. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 1),
  56. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 3),
  57. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 3),
  58. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 6),
  59. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 6),
  60. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 10),
  61. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 10),
  62. (self.module.bisect_left, [], 1, 0),
  63. (self.module.bisect_left, [1], 0, 0),
  64. (self.module.bisect_left, [1], 1, 0),
  65. (self.module.bisect_left, [1], 2, 1),
  66. (self.module.bisect_left, [1, 1], 0, 0),
  67. (self.module.bisect_left, [1, 1], 1, 0),
  68. (self.module.bisect_left, [1, 1], 2, 2),
  69. (self.module.bisect_left, [1, 1, 1], 0, 0),
  70. (self.module.bisect_left, [1, 1, 1], 1, 0),
  71. (self.module.bisect_left, [1, 1, 1], 2, 3),
  72. (self.module.bisect_left, [1, 1, 1, 1], 0, 0),
  73. (self.module.bisect_left, [1, 1, 1, 1], 1, 0),
  74. (self.module.bisect_left, [1, 1, 1, 1], 2, 4),
  75. (self.module.bisect_left, [1, 2], 0, 0),
  76. (self.module.bisect_left, [1, 2], 1, 0),
  77. (self.module.bisect_left, [1, 2], 1.5, 1),
  78. (self.module.bisect_left, [1, 2], 2, 1),
  79. (self.module.bisect_left, [1, 2], 3, 2),
  80. (self.module.bisect_left, [1, 1, 2, 2], 0, 0),
  81. (self.module.bisect_left, [1, 1, 2, 2], 1, 0),
  82. (self.module.bisect_left, [1, 1, 2, 2], 1.5, 2),
  83. (self.module.bisect_left, [1, 1, 2, 2], 2, 2),
  84. (self.module.bisect_left, [1, 1, 2, 2], 3, 4),
  85. (self.module.bisect_left, [1, 2, 3], 0, 0),
  86. (self.module.bisect_left, [1, 2, 3], 1, 0),
  87. (self.module.bisect_left, [1, 2, 3], 1.5, 1),
  88. (self.module.bisect_left, [1, 2, 3], 2, 1),
  89. (self.module.bisect_left, [1, 2, 3], 2.5, 2),
  90. (self.module.bisect_left, [1, 2, 3], 3, 2),
  91. (self.module.bisect_left, [1, 2, 3], 4, 3),
  92. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0),
  93. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 0),
  94. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 1),
  95. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 1),
  96. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 3),
  97. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 3),
  98. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 6),
  99. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 6),
  100. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 10)
  101. ]
  102. def test_precomputed(self):
  103. for func, data, elem, expected in self.precomputedCases:
  104. self.assertEqual(func(data, elem), expected)
  105. self.assertEqual(func(UserList(data), elem), expected)
  106. def test_negative_lo(self):
  107. # Issue 3301
  108. mod = self.module
  109. self.assertRaises(ValueError, mod.bisect_left, [1, 2, 3], 5, -1, 3),
  110. self.assertRaises(ValueError, mod.bisect_right, [1, 2, 3], 5, -1, 3),
  111. self.assertRaises(ValueError, mod.insort_left, [1, 2, 3], 5, -1, 3),
  112. self.assertRaises(ValueError, mod.insort_right, [1, 2, 3], 5, -1, 3),
  113. def test_random(self, n=25):
  114. from random import randrange
  115. for i in xrange(n):
  116. data = [randrange(0, n, 2) for j in xrange(i)]
  117. data.sort()
  118. elem = randrange(-1, n+1)
  119. ip = self.module.bisect_left(data, elem)
  120. if ip < len(data):
  121. self.failUnless(elem <= data[ip])
  122. if ip > 0:
  123. self.failUnless(data[ip-1] < elem)
  124. ip = self.module.bisect_right(data, elem)
  125. if ip < len(data):
  126. self.failUnless(elem < data[ip])
  127. if ip > 0:
  128. self.failUnless(data[ip-1] <= elem)
  129. def test_optionalSlicing(self):
  130. for func, data, elem, expected in self.precomputedCases:
  131. for lo in xrange(4):
  132. lo = min(len(data), lo)
  133. for hi in xrange(3,8):
  134. hi = min(len(data), hi)
  135. ip = func(data, elem, lo, hi)
  136. self.failUnless(lo <= ip <= hi)
  137. if func is self.module.bisect_left and ip < hi:
  138. self.failUnless(elem <= data[ip])
  139. if func is self.module.bisect_left and ip > lo:
  140. self.failUnless(data[ip-1] < elem)
  141. if func is self.module.bisect_right and ip < hi:
  142. self.failUnless(elem < data[ip])
  143. if func is self.module.bisect_right and ip > lo:
  144. self.failUnless(data[ip-1] <= elem)
  145. self.assertEqual(ip, max(lo, min(hi, expected)))
  146. def test_backcompatibility(self):
  147. self.assertEqual(self.module.bisect, self.module.bisect_right)
  148. def test_keyword_args(self):
  149. data = [10, 20, 30, 40, 50]
  150. self.assertEqual(self.module.bisect_left(a=data, x=25, lo=1, hi=3), 2)
  151. self.assertEqual(self.module.bisect_right(a=data, x=25, lo=1, hi=3), 2)
  152. self.assertEqual(self.module.bisect(a=data, x=25, lo=1, hi=3), 2)
  153. self.module.insort_left(a=data, x=25, lo=1, hi=3)
  154. self.module.insort_right(a=data, x=25, lo=1, hi=3)
  155. self.module.insort(a=data, x=25, lo=1, hi=3)
  156. self.assertEqual(data, [10, 20, 25, 25, 25, 30, 40, 50])
  157. class TestBisectPython(TestBisect):
  158. module = py_bisect
  159. class TestBisectC(TestBisect):
  160. module = c_bisect
  161. #==============================================================================
  162. class TestInsort(unittest.TestCase):
  163. module = None
  164. def test_vsBuiltinSort(self, n=500):
  165. from random import choice
  166. for insorted in (list(), UserList()):
  167. for i in xrange(n):
  168. digit = choice("0123456789")
  169. if digit in "02468":
  170. f = self.module.insort_left
  171. else:
  172. f = self.module.insort_right
  173. f(insorted, digit)
  174. self.assertEqual(sorted(insorted), insorted)
  175. def test_backcompatibility(self):
  176. self.assertEqual(self.module.insort, self.module.insort_right)
  177. def test_listDerived(self):
  178. class List(list):
  179. data = []
  180. def insert(self, index, item):
  181. self.data.insert(index, item)
  182. lst = List()
  183. self.module.insort_left(lst, 10)
  184. self.module.insort_right(lst, 5)
  185. self.assertEqual([5, 10], lst.data)
  186. class TestInsortPython(TestInsort):
  187. module = py_bisect
  188. class TestInsortC(TestInsort):
  189. module = c_bisect
  190. #==============================================================================
  191. class LenOnly:
  192. "Dummy sequence class defining __len__ but not __getitem__."
  193. def __len__(self):
  194. return 10
  195. class GetOnly:
  196. "Dummy sequence class defining __getitem__ but not __len__."
  197. def __getitem__(self, ndx):
  198. return 10
  199. class CmpErr:
  200. "Dummy element that always raises an error during comparison"
  201. def __cmp__(self, other):
  202. raise ZeroDivisionError
  203. class TestErrorHandling(unittest.TestCase):
  204. module = None
  205. def test_non_sequence(self):
  206. for f in (self.module.bisect_left, self.module.bisect_right,
  207. self.module.insort_left, self.module.insort_right):
  208. self.assertRaises(TypeError, f, 10, 10)
  209. def test_len_only(self):
  210. for f in (self.module.bisect_left, self.module.bisect_right,
  211. self.module.insort_left, self.module.insort_right):
  212. self.assertRaises(AttributeError, f, LenOnly(), 10)
  213. def test_get_only(self):
  214. for f in (self.module.bisect_left, self.module.bisect_right,
  215. self.module.insort_left, self.module.insort_right):
  216. self.assertRaises(AttributeError, f, GetOnly(), 10)
  217. def test_cmp_err(self):
  218. seq = [CmpErr(), CmpErr(), CmpErr()]
  219. for f in (self.module.bisect_left, self.module.bisect_right,
  220. self.module.insort_left, self.module.insort_right):
  221. self.assertRaises(ZeroDivisionError, f, seq, 10)
  222. def test_arg_parsing(self):
  223. for f in (self.module.bisect_left, self.module.bisect_right,
  224. self.module.insort_left, self.module.insort_right):
  225. self.assertRaises(TypeError, f, 10)
  226. class TestErrorHandlingPython(TestErrorHandling):
  227. module = py_bisect
  228. class TestErrorHandlingC(TestErrorHandling):
  229. module = c_bisect
  230. #==============================================================================
  231. libreftest = """
  232. Example from the Library Reference: Doc/library/bisect.rst
  233. The bisect() function is generally useful for categorizing numeric data.
  234. This example uses bisect() to look up a letter grade for an exam total
  235. (say) based on a set of ordered numeric breakpoints: 85 and up is an `A',
  236. 75..84 is a `B', etc.
  237. >>> grades = "FEDCBA"
  238. >>> breakpoints = [30, 44, 66, 75, 85]
  239. >>> from bisect import bisect
  240. >>> def grade(total):
  241. ... return grades[bisect(breakpoints, total)]
  242. ...
  243. >>> grade(66)
  244. 'C'
  245. >>> map(grade, [33, 99, 77, 44, 12, 88])
  246. ['E', 'A', 'B', 'D', 'F', 'A']
  247. """
  248. #------------------------------------------------------------------------------
  249. __test__ = {'libreftest' : libreftest}
  250. def test_main(verbose=None):
  251. from test import test_bisect
  252. test_classes = [TestBisectPython, TestBisectC,
  253. TestInsortPython, TestInsortC,
  254. TestErrorHandlingPython, TestErrorHandlingC]
  255. test_support.run_unittest(*test_classes)
  256. test_support.run_doctest(test_bisect, verbose)
  257. # verify reference counting
  258. if verbose and hasattr(sys, "gettotalrefcount"):
  259. import gc
  260. counts = [None] * 5
  261. for i in xrange(len(counts)):
  262. test_support.run_unittest(*test_classes)
  263. gc.collect()
  264. counts[i] = sys.gettotalrefcount()
  265. print counts
  266. if __name__ == "__main__":
  267. test_main(verbose=True)