summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_range.py
diff options
context:
space:
mode:
authorMark Dickinson <dickinsm@gmail.com>2009-11-15 09:57:26 (GMT)
committerMark Dickinson <dickinsm@gmail.com>2009-11-15 09:57:26 (GMT)
commitd550c9a281fd9a909f84a4f29d2d9a0be9187685 (patch)
treeffcdd96d617595bd98f7e88122a7a5a7836a7bed /Lib/test/test_range.py
parent4c7eaee5db8bd4b5b55e3102fb171e45ad073117 (diff)
downloadcpython-d550c9a281fd9a909f84a4f29d2d9a0be9187685.zip
cpython-d550c9a281fd9a909f84a4f29d2d9a0be9187685.tar.gz
cpython-d550c9a281fd9a909f84a4f29d2d9a0be9187685.tar.bz2
Issue #7298: Fix a variety of problems leading to wrong results with
the fast versions of range.__reversed__ and range iteration. Also fix wrong results and a refleak for PyLong version of range.__reversed__. Thanks Eric Smith for reviewing, and for suggesting improved tests.
Diffstat (limited to 'Lib/test/test_range.py')
-rw-r--r--Lib/test/test_range.py61
1 files changed, 61 insertions, 0 deletions
diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py
index 638d943..7e7b91f 100644
--- a/Lib/test/test_range.py
+++ b/Lib/test/test_range.py
@@ -3,12 +3,49 @@
import test.support, unittest
import sys
import pickle
+import itertools
import warnings
warnings.filterwarnings("ignore", "integer argument expected",
DeprecationWarning, "unittest")
+# pure Python implementations (3 args only), for comparison
+def pyrange(start, stop, step):
+ if (start - stop) // step < 0:
+ # replace stop with next element in the sequence of integers
+ # that are congruent to start modulo step.
+ stop += (start - stop) % step
+ while start != stop:
+ yield start
+ start += step
+
+def pyrange_reversed(start, stop, step):
+ stop += (start - stop) % step
+ return pyrange(stop - step, start - step, -step)
+
+
class RangeTest(unittest.TestCase):
+ def assert_iterators_equal(self, xs, ys, test_id, limit=None):
+ # check that an iterator xs matches the expected results ys,
+ # up to a given limit.
+ if limit is not None:
+ xs = itertools.islice(xs, limit)
+ ys = itertools.islice(ys, limit)
+ sentinel = object()
+ pairs = itertools.zip_longest(xs, ys, fillvalue=sentinel)
+ for i, (x, y) in enumerate(pairs):
+ if x == y:
+ continue
+ elif x == sentinel:
+ self.fail('{}: iterator ended unexpectedly '
+ 'at position {}; expected {}'.format(test_id, i, y))
+ elif y == sentinel:
+ self.fail('{}: unexpected excess element {} at '
+ 'position {}'.format(test_id, x, i))
+ else:
+ self.fail('{}: wrong element at position {};'
+ 'expected {}, got {}'.format(test_id, i, y, x))
+
def test_range(self):
self.assertEqual(list(range(3)), [0, 1, 2])
self.assertEqual(list(range(1, 5)), [1, 2, 3, 4])
@@ -134,6 +171,30 @@ class RangeTest(unittest.TestCase):
self.assertFalse(-1 in r)
self.assertFalse(1 in r)
+ def test_range_iterators(self):
+ # exercise 'fast' iterators, that use a rangeiterobject internally.
+ # see issue 7298
+ limits = [base + jiggle
+ for M in (2**32, 2**64)
+ for base in (-M, -M//2, 0, M//2, M)
+ for jiggle in (-2, -1, 0, 1, 2)]
+ test_ranges = [(start, end, step)
+ for start in limits
+ for end in limits
+ for step in (-2**63, -2**31, -2, -1, 1, 2)]
+
+ for start, end, step in test_ranges:
+ iter1 = range(start, end, step)
+ iter2 = pyrange(start, end, step)
+ test_id = "range({}, {}, {})".format(start, end, step)
+ # check first 100 entries
+ self.assert_iterators_equal(iter1, iter2, test_id, limit=100)
+
+ iter1 = reversed(range(start, end, step))
+ iter2 = pyrange_reversed(start, end, step)
+ test_id = "reversed(range({}, {}, {}))".format(start, end, step)
+ self.assert_iterators_equal(iter1, iter2, test_id, limit=100)
+
def test_main():
test.support.run_unittest(RangeTest)