1from __future__ import absolute_import 2import sys 3import unittest 4from test import test_support 5from UserList import UserList 6 7# We do a bit of trickery here to be able to test both the C implementation 8# and the Python implementation of the module. 9 10# Make it impossible to import the C implementation anymore. 11sys.modules['_bisect'] = 0 12# We must also handle the case that bisect was imported before. 13if 'bisect' in sys.modules: 14 del sys.modules['bisect'] 15 16# Now we can import the module and get the pure Python implementation. 17import bisect as py_bisect 18 19# Restore everything to normal. 20del sys.modules['_bisect'] 21del sys.modules['bisect'] 22 23# This is now the module with the C implementation. 24import bisect as c_bisect 25 26 27class Range(object): 28 """A trivial xrange()-like object without any integer width limitations.""" 29 def __init__(self, start, stop): 30 self.start = start 31 self.stop = stop 32 self.last_insert = None 33 34 def __len__(self): 35 return self.stop - self.start 36 37 def __getitem__(self, idx): 38 n = self.stop - self.start 39 if idx < 0: 40 idx += n 41 if idx >= n: 42 raise IndexError(idx) 43 return self.start + idx 44 45 def insert(self, idx, item): 46 self.last_insert = idx, item 47 48 49class TestBisect(unittest.TestCase): 50 module = None 51 52 def setUp(self): 53 self.precomputedCases = [ 54 (self.module.bisect_right, [], 1, 0), 55 (self.module.bisect_right, [1], 0, 0), 56 (self.module.bisect_right, [1], 1, 1), 57 (self.module.bisect_right, [1], 2, 1), 58 (self.module.bisect_right, [1, 1], 0, 0), 59 (self.module.bisect_right, [1, 1], 1, 2), 60 (self.module.bisect_right, [1, 1], 2, 2), 61 (self.module.bisect_right, [1, 1, 1], 0, 0), 62 (self.module.bisect_right, [1, 1, 1], 1, 3), 63 (self.module.bisect_right, [1, 1, 1], 2, 3), 64 (self.module.bisect_right, [1, 1, 1, 1], 0, 0), 65 (self.module.bisect_right, [1, 1, 1, 1], 1, 4), 66 (self.module.bisect_right, [1, 1, 1, 1], 2, 4), 67 (self.module.bisect_right, [1, 2], 0, 0), 68 (self.module.bisect_right, [1, 2], 1, 1), 69 (self.module.bisect_right, [1, 2], 1.5, 1), 70 (self.module.bisect_right, [1, 2], 2, 2), 71 (self.module.bisect_right, [1, 2], 3, 2), 72 (self.module.bisect_right, [1, 1, 2, 2], 0, 0), 73 (self.module.bisect_right, [1, 1, 2, 2], 1, 2), 74 (self.module.bisect_right, [1, 1, 2, 2], 1.5, 2), 75 (self.module.bisect_right, [1, 1, 2, 2], 2, 4), 76 (self.module.bisect_right, [1, 1, 2, 2], 3, 4), 77 (self.module.bisect_right, [1, 2, 3], 0, 0), 78 (self.module.bisect_right, [1, 2, 3], 1, 1), 79 (self.module.bisect_right, [1, 2, 3], 1.5, 1), 80 (self.module.bisect_right, [1, 2, 3], 2, 2), 81 (self.module.bisect_right, [1, 2, 3], 2.5, 2), 82 (self.module.bisect_right, [1, 2, 3], 3, 3), 83 (self.module.bisect_right, [1, 2, 3], 4, 3), 84 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0), 85 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 1), 86 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 1), 87 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 3), 88 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 3), 89 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 6), 90 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 6), 91 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 10), 92 (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 10), 93 94 (self.module.bisect_left, [], 1, 0), 95 (self.module.bisect_left, [1], 0, 0), 96 (self.module.bisect_left, [1], 1, 0), 97 (self.module.bisect_left, [1], 2, 1), 98 (self.module.bisect_left, [1, 1], 0, 0), 99 (self.module.bisect_left, [1, 1], 1, 0), 100 (self.module.bisect_left, [1, 1], 2, 2), 101 (self.module.bisect_left, [1, 1, 1], 0, 0), 102 (self.module.bisect_left, [1, 1, 1], 1, 0), 103 (self.module.bisect_left, [1, 1, 1], 2, 3), 104 (self.module.bisect_left, [1, 1, 1, 1], 0, 0), 105 (self.module.bisect_left, [1, 1, 1, 1], 1, 0), 106 (self.module.bisect_left, [1, 1, 1, 1], 2, 4), 107 (self.module.bisect_left, [1, 2], 0, 0), 108 (self.module.bisect_left, [1, 2], 1, 0), 109 (self.module.bisect_left, [1, 2], 1.5, 1), 110 (self.module.bisect_left, [1, 2], 2, 1), 111 (self.module.bisect_left, [1, 2], 3, 2), 112 (self.module.bisect_left, [1, 1, 2, 2], 0, 0), 113 (self.module.bisect_left, [1, 1, 2, 2], 1, 0), 114 (self.module.bisect_left, [1, 1, 2, 2], 1.5, 2), 115 (self.module.bisect_left, [1, 1, 2, 2], 2, 2), 116 (self.module.bisect_left, [1, 1, 2, 2], 3, 4), 117 (self.module.bisect_left, [1, 2, 3], 0, 0), 118 (self.module.bisect_left, [1, 2, 3], 1, 0), 119 (self.module.bisect_left, [1, 2, 3], 1.5, 1), 120 (self.module.bisect_left, [1, 2, 3], 2, 1), 121 (self.module.bisect_left, [1, 2, 3], 2.5, 2), 122 (self.module.bisect_left, [1, 2, 3], 3, 2), 123 (self.module.bisect_left, [1, 2, 3], 4, 3), 124 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0), 125 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 0), 126 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 1), 127 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 1), 128 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 3), 129 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 3), 130 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 6), 131 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 6), 132 (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 10) 133 ] 134 135 def test_precomputed(self): 136 for func, data, elem, expected in self.precomputedCases: 137 self.assertEqual(func(data, elem), expected) 138 self.assertEqual(func(UserList(data), elem), expected) 139 140 def test_negative_lo(self): 141 # Issue 3301 142 mod = self.module 143 self.assertRaises(ValueError, mod.bisect_left, [1, 2, 3], 5, -1, 3), 144 self.assertRaises(ValueError, mod.bisect_right, [1, 2, 3], 5, -1, 3), 145 self.assertRaises(ValueError, mod.insort_left, [1, 2, 3], 5, -1, 3), 146 self.assertRaises(ValueError, mod.insort_right, [1, 2, 3], 5, -1, 3), 147 148 def test_large_range(self): 149 # Issue 13496 150 mod = self.module 151 n = sys.maxsize 152 try: 153 data = xrange(n-1) 154 except OverflowError: 155 self.skipTest("can't create a xrange() object of size `sys.maxsize`") 156 self.assertEqual(mod.bisect_left(data, n-3), n-3) 157 self.assertEqual(mod.bisect_right(data, n-3), n-2) 158 self.assertEqual(mod.bisect_left(data, n-3, n-10, n), n-3) 159 self.assertEqual(mod.bisect_right(data, n-3, n-10, n), n-2) 160 161 def test_large_pyrange(self): 162 # Same as above, but without C-imposed limits on range() parameters 163 mod = self.module 164 n = sys.maxsize 165 data = Range(0, n-1) 166 self.assertEqual(mod.bisect_left(data, n-3), n-3) 167 self.assertEqual(mod.bisect_right(data, n-3), n-2) 168 self.assertEqual(mod.bisect_left(data, n-3, n-10, n), n-3) 169 self.assertEqual(mod.bisect_right(data, n-3, n-10, n), n-2) 170 x = n - 100 171 mod.insort_left(data, x, x - 50, x + 50) 172 self.assertEqual(data.last_insert, (x, x)) 173 x = n - 200 174 mod.insort_right(data, x, x - 50, x + 50) 175 self.assertEqual(data.last_insert, (x + 1, x)) 176 177 def test_random(self, n=25): 178 from random import randrange 179 for i in xrange(n): 180 data = [randrange(0, n, 2) for j in xrange(i)] 181 data.sort() 182 elem = randrange(-1, n+1) 183 ip = self.module.bisect_left(data, elem) 184 if ip < len(data): 185 self.assertTrue(elem <= data[ip]) 186 if ip > 0: 187 self.assertTrue(data[ip-1] < elem) 188 ip = self.module.bisect_right(data, elem) 189 if ip < len(data): 190 self.assertTrue(elem < data[ip]) 191 if ip > 0: 192 self.assertTrue(data[ip-1] <= elem) 193 194 def test_optionalSlicing(self): 195 for func, data, elem, expected in self.precomputedCases: 196 for lo in xrange(4): 197 lo = min(len(data), lo) 198 for hi in xrange(3,8): 199 hi = min(len(data), hi) 200 ip = func(data, elem, lo, hi) 201 self.assertTrue(lo <= ip <= hi) 202 if func is self.module.bisect_left and ip < hi: 203 self.assertTrue(elem <= data[ip]) 204 if func is self.module.bisect_left and ip > lo: 205 self.assertTrue(data[ip-1] < elem) 206 if func is self.module.bisect_right and ip < hi: 207 self.assertTrue(elem < data[ip]) 208 if func is self.module.bisect_right and ip > lo: 209 self.assertTrue(data[ip-1] <= elem) 210 self.assertEqual(ip, max(lo, min(hi, expected))) 211 212 def test_backcompatibility(self): 213 self.assertEqual(self.module.bisect, self.module.bisect_right) 214 215 def test_keyword_args(self): 216 data = [10, 20, 30, 40, 50] 217 self.assertEqual(self.module.bisect_left(a=data, x=25, lo=1, hi=3), 2) 218 self.assertEqual(self.module.bisect_right(a=data, x=25, lo=1, hi=3), 2) 219 self.assertEqual(self.module.bisect(a=data, x=25, lo=1, hi=3), 2) 220 self.module.insort_left(a=data, x=25, lo=1, hi=3) 221 self.module.insort_right(a=data, x=25, lo=1, hi=3) 222 self.module.insort(a=data, x=25, lo=1, hi=3) 223 self.assertEqual(data, [10, 20, 25, 25, 25, 30, 40, 50]) 224 225class TestBisectPython(TestBisect): 226 module = py_bisect 227 228class TestBisectC(TestBisect): 229 module = c_bisect 230 231#============================================================================== 232 233class TestInsort(unittest.TestCase): 234 module = None 235 236 def test_vsBuiltinSort(self, n=500): 237 from random import choice 238 for insorted in (list(), UserList()): 239 for i in xrange(n): 240 digit = choice("0123456789") 241 if digit in "02468": 242 f = self.module.insort_left 243 else: 244 f = self.module.insort_right 245 f(insorted, digit) 246 self.assertEqual(sorted(insorted), insorted) 247 248 def test_backcompatibility(self): 249 self.assertEqual(self.module.insort, self.module.insort_right) 250 251 def test_listDerived(self): 252 class List(list): 253 data = [] 254 def insert(self, index, item): 255 self.data.insert(index, item) 256 257 lst = List() 258 self.module.insort_left(lst, 10) 259 self.module.insort_right(lst, 5) 260 self.assertEqual([5, 10], lst.data) 261 262class TestInsortPython(TestInsort): 263 module = py_bisect 264 265class TestInsortC(TestInsort): 266 module = c_bisect 267 268#============================================================================== 269 270 271class LenOnly: 272 "Dummy sequence class defining __len__ but not __getitem__." 273 def __len__(self): 274 return 10 275 276class GetOnly: 277 "Dummy sequence class defining __getitem__ but not __len__." 278 def __getitem__(self, ndx): 279 return 10 280 281class CmpErr: 282 "Dummy element that always raises an error during comparison" 283 def __cmp__(self, other): 284 raise ZeroDivisionError 285 286class TestErrorHandling(unittest.TestCase): 287 module = None 288 289 def test_non_sequence(self): 290 for f in (self.module.bisect_left, self.module.bisect_right, 291 self.module.insort_left, self.module.insort_right): 292 self.assertRaises(TypeError, f, 10, 10) 293 294 def test_len_only(self): 295 for f in (self.module.bisect_left, self.module.bisect_right, 296 self.module.insort_left, self.module.insort_right): 297 self.assertRaises(AttributeError, f, LenOnly(), 10) 298 299 def test_get_only(self): 300 for f in (self.module.bisect_left, self.module.bisect_right, 301 self.module.insort_left, self.module.insort_right): 302 self.assertRaises(AttributeError, f, GetOnly(), 10) 303 304 def test_cmp_err(self): 305 seq = [CmpErr(), CmpErr(), CmpErr()] 306 for f in (self.module.bisect_left, self.module.bisect_right, 307 self.module.insort_left, self.module.insort_right): 308 self.assertRaises(ZeroDivisionError, f, seq, 10) 309 310 def test_arg_parsing(self): 311 for f in (self.module.bisect_left, self.module.bisect_right, 312 self.module.insort_left, self.module.insort_right): 313 self.assertRaises(TypeError, f, 10) 314 315class TestErrorHandlingPython(TestErrorHandling): 316 module = py_bisect 317 318class TestErrorHandlingC(TestErrorHandling): 319 module = c_bisect 320 321#============================================================================== 322 323libreftest = """ 324Example from the Library Reference: Doc/library/bisect.rst 325 326The bisect() function is generally useful for categorizing numeric data. 327This example uses bisect() to look up a letter grade for an exam total 328(say) based on a set of ordered numeric breakpoints: 85 and up is an `A', 32975..84 is a `B', etc. 330 331 >>> grades = "FEDCBA" 332 >>> breakpoints = [30, 44, 66, 75, 85] 333 >>> from bisect import bisect 334 >>> def grade(total): 335 ... return grades[bisect(breakpoints, total)] 336 ... 337 >>> grade(66) 338 'C' 339 >>> map(grade, [33, 99, 77, 44, 12, 88]) 340 ['E', 'A', 'B', 'D', 'F', 'A'] 341 342""" 343 344#------------------------------------------------------------------------------ 345 346__test__ = {'libreftest' : libreftest} 347 348def test_main(verbose=None): 349 from test import test_bisect 350 351 test_classes = [TestBisectPython, TestBisectC, 352 TestInsortPython, TestInsortC, 353 TestErrorHandlingPython, TestErrorHandlingC] 354 355 test_support.run_unittest(*test_classes) 356 test_support.run_doctest(test_bisect, verbose) 357 358 # verify reference counting 359 if verbose and hasattr(sys, "gettotalrefcount"): 360 import gc 361 counts = [None] * 5 362 for i in xrange(len(counts)): 363 test_support.run_unittest(*test_classes) 364 gc.collect() 365 counts[i] = sys.gettotalrefcount() 366 print counts 367 368if __name__ == "__main__": 369 test_main(verbose=True) 370