blob: 84c92cc97045458b8014a355588e2f26f5e9d1a6 [file] [log] [blame]
Neal Norwitza98e7692005-11-24 21:58:51 +00001from test import test_support
Tim Peters2d8b7652002-08-01 02:23:06 +00002import random
Neal Norwitza98e7692005-11-24 21:58:51 +00003import sys
4import unittest
Tim Peters2d8b7652002-08-01 02:23:06 +00005
Neal Norwitza98e7692005-11-24 21:58:51 +00006verbose = test_support.verbose
Tim Peters2d8b7652002-08-01 02:23:06 +00007nerrors = 0
8
9def check(tag, expected, raw, compare=None):
10 global nerrors
11
12 if verbose:
13 print " checking", tag
14
15 orig = raw[:] # save input in case of error
16 if compare:
17 raw.sort(compare)
18 else:
19 raw.sort()
20
21 if len(expected) != len(raw):
22 print "error in", tag
23 print "length mismatch;", len(expected), len(raw)
24 print expected
25 print orig
26 print raw
27 nerrors += 1
28 return
29
30 for i, good in enumerate(expected):
31 maybe = raw[i]
32 if good is not maybe:
33 print "error in", tag
34 print "out of order at index", i, good, maybe
35 print expected
36 print orig
37 print raw
38 nerrors += 1
39 return
40
Neal Norwitza98e7692005-11-24 21:58:51 +000041class TestBase(unittest.TestCase):
42 def testStressfully(self):
43 # Try a variety of sizes at and around powers of 2, and at powers of 10.
44 sizes = [0]
45 for power in range(1, 10):
46 n = 2 ** power
47 sizes.extend(range(n-1, n+2))
48 sizes.extend([10, 100, 1000])
Tim Peters2d8b7652002-08-01 02:23:06 +000049
Neal Norwitza98e7692005-11-24 21:58:51 +000050 class Complains(object):
51 maybe_complain = True
Tim Peters2d8b7652002-08-01 02:23:06 +000052
Neal Norwitza98e7692005-11-24 21:58:51 +000053 def __init__(self, i):
54 self.i = i
Tim Peters2d8b7652002-08-01 02:23:06 +000055
Neal Norwitza98e7692005-11-24 21:58:51 +000056 def __lt__(self, other):
57 if Complains.maybe_complain and random.random() < 0.001:
58 if verbose:
59 print " complaining at", self, other
60 raise RuntimeError
61 return self.i < other.i
62
63 def __repr__(self):
64 return "Complains(%d)" % self.i
65
66 class Stable(object):
67 def __init__(self, key, i):
68 self.key = key
69 self.index = i
70
71 def __cmp__(self, other):
72 return cmp(self.key, other.key)
73
74 def __repr__(self):
75 return "Stable(%d, %d)" % (self.key, self.index)
76
77 for n in sizes:
78 x = range(n)
Tim Peters2d8b7652002-08-01 02:23:06 +000079 if verbose:
Neal Norwitza98e7692005-11-24 21:58:51 +000080 print "Testing size", n
Tim Peters2d8b7652002-08-01 02:23:06 +000081
Neal Norwitza98e7692005-11-24 21:58:51 +000082 s = x[:]
83 check("identity", x, s)
Tim Peters2d8b7652002-08-01 02:23:06 +000084
Neal Norwitza98e7692005-11-24 21:58:51 +000085 s = x[:]
86 s.reverse()
87 check("reversed", x, s)
Tim Peters2d8b7652002-08-01 02:23:06 +000088
Neal Norwitza98e7692005-11-24 21:58:51 +000089 s = x[:]
90 random.shuffle(s)
91 check("random permutation", x, s)
Tim Peters2d8b7652002-08-01 02:23:06 +000092
Neal Norwitza98e7692005-11-24 21:58:51 +000093 y = x[:]
94 y.reverse()
95 s = x[:]
96 check("reversed via function", y, s, lambda a, b: cmp(b, a))
Tim Peters2d8b7652002-08-01 02:23:06 +000097
Neal Norwitza98e7692005-11-24 21:58:51 +000098 if verbose:
99 print " Checking against an insane comparison function."
100 print " If the implementation isn't careful, this may segfault."
101 s = x[:]
102 s.sort(lambda a, b: int(random.random() * 3) - 1)
103 check("an insane function left some permutation", x, s)
Tim Peters2d8b7652002-08-01 02:23:06 +0000104
Neal Norwitza98e7692005-11-24 21:58:51 +0000105 x = [Complains(i) for i in x]
106 s = x[:]
107 random.shuffle(s)
108 Complains.maybe_complain = True
109 it_complained = False
110 try:
111 s.sort()
112 except RuntimeError:
113 it_complained = True
114 if it_complained:
115 Complains.maybe_complain = False
116 check("exception during sort left some permutation", x, s)
Tim Peters2d8b7652002-08-01 02:23:06 +0000117
Neal Norwitza98e7692005-11-24 21:58:51 +0000118 s = [Stable(random.randrange(10), i) for i in xrange(n)]
119 augmented = [(e, e.index) for e in s]
120 augmented.sort() # forced stable because ties broken by index
121 x = [e for e, i in augmented] # a stable sort of s
122 check("stability", x, s)
Tim Petersb9099c32002-11-12 22:08:10 +0000123
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000124#==============================================================================
Tim Petersb9099c32002-11-12 22:08:10 +0000125
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000126class TestBugs(unittest.TestCase):
Tim Petersb9099c32002-11-12 22:08:10 +0000127
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000128 def test_bug453523(self):
129 # bug 453523 -- list.sort() crasher.
130 # If this fails, the most likely outcome is a core dump.
131 # Mutations during a list sort should raise a ValueError.
Tim Petersb9099c32002-11-12 22:08:10 +0000132
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000133 class C:
134 def __lt__(self, other):
135 if L and random.random() < 0.75:
136 L.pop()
137 else:
138 L.append(3)
139 return random.random() < 0.5
Skip Montanaro4abd5f02003-01-02 20:51:08 +0000140
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000141 L = [C() for i in range(50)]
142 self.assertRaises(ValueError, L.sort)
Skip Montanaro4abd5f02003-01-02 20:51:08 +0000143
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000144 def test_cmpNone(self):
145 # Testing None as a comparison function.
146
147 L = range(50)
148 random.shuffle(L)
Skip Montanaro4abd5f02003-01-02 20:51:08 +0000149 L.sort(None)
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000150 self.assertEqual(L, range(50))
Skip Montanaro4abd5f02003-01-02 20:51:08 +0000151
Armin Rigo93677f02004-07-29 12:40:23 +0000152 def test_undetected_mutation(self):
153 # Python 2.4a1 did not always detect mutation
154 memorywaster = []
155 for i in range(20):
156 def mutating_cmp(x, y):
157 L.append(3)
158 L.pop()
159 return cmp(x, y)
160 L = [1,2]
161 self.assertRaises(ValueError, L.sort, mutating_cmp)
162 def mutating_cmp(x, y):
163 L.append(3)
164 del L[:]
165 return cmp(x, y)
166 self.assertRaises(ValueError, L.sort, mutating_cmp)
167 memorywaster = [memorywaster]
168
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000169#==============================================================================
Skip Montanaro4abd5f02003-01-02 20:51:08 +0000170
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000171class TestDecorateSortUndecorate(unittest.TestCase):
172
173 def test_decorated(self):
174 data = 'The quick Brown fox Jumped over The lazy Dog'.split()
175 copy = data[:]
176 random.shuffle(data)
177 data.sort(key=str.lower)
178 copy.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
179
180 def test_baddecorator(self):
181 data = 'The quick Brown fox Jumped over The lazy Dog'.split()
182 self.assertRaises(TypeError, data.sort, None, lambda x,y: 0)
183
184 def test_stability(self):
185 data = [(random.randrange(100), i) for i in xrange(200)]
186 copy = data[:]
187 data.sort(key=lambda (x,y): x) # sort on the random first field
188 copy.sort() # sort using both fields
189 self.assertEqual(data, copy) # should get the same result
190
191 def test_cmp_and_key_combination(self):
192 # Verify that the wrapper has been removed
193 def compare(x, y):
194 self.assertEqual(type(x), str)
195 self.assertEqual(type(x), str)
196 return cmp(x, y)
197 data = 'The quick Brown fox Jumped over The lazy Dog'.split()
198 data.sort(cmp=compare, key=str.lower)
199
200 def test_badcmp_with_key(self):
201 # Verify that the wrapper has been removed
202 data = 'The quick Brown fox Jumped over The lazy Dog'.split()
203 self.assertRaises(TypeError, data.sort, "bad", str.lower)
204
Raymond Hettinger37e13632003-11-28 21:43:02 +0000205 def test_key_with_exception(self):
206 # Verify that the wrapper has been removed
207 data = range(-2,2)
208 dup = data[:]
209 self.assertRaises(ZeroDivisionError, data.sort, None, lambda x: 1/x)
210 self.assertEqual(data, dup)
211
Michael W. Hudson1df0f652003-12-04 11:25:46 +0000212 def test_key_with_mutation(self):
213 data = range(10)
214 def k(x):
215 del data[:]
216 data[:] = range(20)
217 return x
218 self.assertRaises(ValueError, data.sort, key=k)
219
220 def test_key_with_mutating_del(self):
221 data = range(10)
222 class SortKiller(object):
223 def __init__(self, x):
224 pass
225 def __del__(self):
226 del data[:]
227 data[:] = range(20)
228 self.assertRaises(ValueError, data.sort, key=SortKiller)
229
230 def test_key_with_mutating_del_and_exception(self):
231 data = range(10)
232 ## dup = data[:]
233 class SortKiller(object):
234 def __init__(self, x):
235 if x > 2:
236 raise RuntimeError
237 def __del__(self):
238 del data[:]
239 data[:] = range(20)
240 self.assertRaises(RuntimeError, data.sort, key=SortKiller)
241 ## major honking subtlety: we *can't* do:
242 ##
243 ## self.assertEqual(data, dup)
244 ##
245 ## because there is a reference to a SortKiller in the
246 ## traceback and by the time it dies we're outside the call to
247 ## .sort() and so the list protection gimmicks are out of
248 ## date (this cost some brain cells to figure out...).
249
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000250 def test_reverse(self):
251 data = range(100)
252 random.shuffle(data)
253 data.sort(reverse=True)
254 self.assertEqual(data, range(99,-1,-1))
Raymond Hettinger0a9b9da2003-10-29 06:54:43 +0000255 self.assertRaises(TypeError, data.sort, "wrong type")
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000256
257 def test_reverse_stability(self):
258 data = [(random.randrange(100), i) for i in xrange(200)]
259 copy1 = data[:]
260 copy2 = data[:]
261 data.sort(cmp=lambda x,y: cmp(x[0],y[0]), reverse=True)
262 copy1.sort(cmp=lambda x,y: cmp(y[0],x[0]))
263 self.assertEqual(data, copy1)
264 copy2.sort(key=lambda x: x[0], reverse=True)
265 self.assertEqual(data, copy2)
266
267#==============================================================================
268
269def test_main(verbose=None):
270 test_classes = (
Neal Norwitza98e7692005-11-24 21:58:51 +0000271 TestBase,
Raymond Hettinger42b1ba32003-10-16 03:41:09 +0000272 TestDecorateSortUndecorate,
273 TestBugs,
274 )
275
276 test_support.run_unittest(*test_classes)
277
278 # verify reference counting
279 if verbose and hasattr(sys, "gettotalrefcount"):
280 import gc
281 counts = [None] * 5
282 for i in xrange(len(counts)):
283 test_support.run_unittest(*test_classes)
284 gc.collect()
285 counts[i] = sys.gettotalrefcount()
286 print counts
287
288if __name__ == "__main__":
289 test_main(verbose=True)