blob: 660e1773de1bb94d2b1d172473d39e0192e3a27c [file] [log] [blame]
Alexandre Vassalotti30ece442008-05-11 19:39:48 +00001# Some simple queue module tests, plus some failure conditions
Tim Petersafe52972004-08-20 02:37:25 +00002# to ensure the Queue locks remain stable.
Georg Brandla6168f92008-05-25 07:20:14 +00003import Queue
Mark Hammond3b959db2002-04-19 00:11:32 +00004import sys
5import threading
6import time
Georg Brandld22b4662008-02-02 11:39:29 +00007import unittest
8from test import test_support
Mark Hammond3b959db2002-04-19 00:11:32 +00009
Tim Petersafe52972004-08-20 02:37:25 +000010QUEUE_SIZE = 5
Mark Hammond3b959db2002-04-19 00:11:32 +000011
Tim Petersafe52972004-08-20 02:37:25 +000012# A thread to run a function that unclogs a blocked Queue.
Mark Hammond3b959db2002-04-19 00:11:32 +000013class _TriggerThread(threading.Thread):
14 def __init__(self, fn, args):
15 self.fn = fn
16 self.args = args
17 self.startedEvent = threading.Event()
18 threading.Thread.__init__(self)
Tim Petersafe52972004-08-20 02:37:25 +000019
Mark Hammond3b959db2002-04-19 00:11:32 +000020 def run(self):
Tim Peters8d7626c2004-08-20 03:27:12 +000021 # The sleep isn't necessary, but is intended to give the blocking
22 # function in the main thread a chance at actually blocking before
23 # we unclog it. But if the sleep is longer than the timeout-based
24 # tests wait in their blocking functions, those tests will fail.
25 # So we give them much longer timeout values compared to the
26 # sleep here (I aimed at 10 seconds for blocking functions --
27 # they should never actually wait that long - they should make
28 # progress as soon as we call self.fn()).
29 time.sleep(0.1)
Mark Hammond3b959db2002-04-19 00:11:32 +000030 self.startedEvent.set()
31 self.fn(*self.args)
32
Tim Peters8d7626c2004-08-20 03:27:12 +000033
Georg Brandld22b4662008-02-02 11:39:29 +000034# Execute a function that blocks, and in a separate thread, a function that
35# triggers the release. Returns the result of the blocking function. Caution:
36# block_func must guarantee to block until trigger_func is called, and
37# trigger_func must guarantee to change queue state so that block_func can make
38# enough progress to return. In particular, a block_func that just raises an
39# exception regardless of whether trigger_func is called will lead to
40# timing-dependent sporadic failures, and one of those went rarely seen but
41# undiagnosed for years. Now block_func must be unexceptional. If block_func
42# is supposed to raise an exception, call do_exceptional_blocking_test()
43# instead.
44
45class BlockingTestMixin:
46
47 def do_blocking_test(self, block_func, block_args, trigger_func, trigger_args):
48 self.t = _TriggerThread(trigger_func, trigger_args)
49 self.t.start()
50 self.result = block_func(*block_args)
51 # If block_func returned before our thread made the call, we failed!
52 if not self.t.startedEvent.isSet():
53 self.fail("blocking function '%r' appeared not to block" %
54 block_func)
55 self.t.join(10) # make sure the thread terminates
56 if self.t.isAlive():
57 self.fail("trigger function '%r' appeared to not return" %
58 trigger_func)
59 return self.result
60
61 # Call this instead if block_func is supposed to raise an exception.
62 def do_exceptional_blocking_test(self,block_func, block_args, trigger_func,
63 trigger_args, expected_exception_class):
64 self.t = _TriggerThread(trigger_func, trigger_args)
65 self.t.start()
Tim Peters8d7626c2004-08-20 03:27:12 +000066 try:
Georg Brandld22b4662008-02-02 11:39:29 +000067 try:
68 block_func(*block_args)
69 except expected_exception_class:
70 raise
71 else:
72 self.fail("expected exception of kind %r" %
73 expected_exception_class)
74 finally:
75 self.t.join(10) # make sure the thread terminates
76 if self.t.isAlive():
77 self.fail("trigger function '%r' appeared to not return" %
78 trigger_func)
79 if not self.t.startedEvent.isSet():
80 self.fail("trigger thread ended but event never set")
81
82
83class BaseQueueTest(unittest.TestCase, BlockingTestMixin):
84 def setUp(self):
85 self.cum = 0
86 self.cumlock = threading.Lock()
87
88 def simple_queue_test(self, q):
Georg Brandl3c3d9ac2008-02-06 23:45:51 +000089 if not q.empty():
Georg Brandld22b4662008-02-02 11:39:29 +000090 raise RuntimeError, "Call this function with an empty queue"
91 # I guess we better check things actually queue correctly a little :)
92 q.put(111)
93 q.put(333)
94 q.put(222)
95 target_order = dict(Queue = [111, 333, 222],
96 LifoQueue = [222, 333, 111],
97 PriorityQueue = [111, 222, 333])
98 actual_order = [q.get(), q.get(), q.get()]
99 self.assertEquals(actual_order, target_order[q.__class__.__name__],
100 "Didn't seem to queue the correct data!")
101 for i in range(QUEUE_SIZE-1):
102 q.put(i)
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000103 self.assert_(not q.empty(), "Queue should not be empty")
104 self.assert_(not q.full(), "Queue should not be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000105 q.put("last")
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000106 self.assert_(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000107 try:
108 q.put("full", block=0)
109 self.fail("Didn't appear to block with a full queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000110 except Queue.Full:
Georg Brandld22b4662008-02-02 11:39:29 +0000111 pass
112 try:
113 q.put("full", timeout=0.01)
114 self.fail("Didn't appear to time-out with a full queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000115 except Queue.Full:
Georg Brandld22b4662008-02-02 11:39:29 +0000116 pass
117 # Test a blocking put
118 self.do_blocking_test(q.put, ("full",), q.get, ())
119 self.do_blocking_test(q.put, ("full", True, 10), q.get, ())
120 # Empty it
121 for i in range(QUEUE_SIZE):
122 q.get()
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000123 self.assert_(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000124 try:
125 q.get(block=0)
126 self.fail("Didn't appear to block with an empty queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000127 except Queue.Empty:
Georg Brandld22b4662008-02-02 11:39:29 +0000128 pass
129 try:
130 q.get(timeout=0.01)
131 self.fail("Didn't appear to time-out with an empty queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000132 except Queue.Empty:
Georg Brandld22b4662008-02-02 11:39:29 +0000133 pass
134 # Test a blocking get
135 self.do_blocking_test(q.get, (), q.put, ('empty',))
136 self.do_blocking_test(q.get, (True, 10), q.put, ('empty',))
137
138
139 def worker(self, q):
140 while True:
Georg Brandlcafb7102008-02-02 23:59:21 +0000141 x = q.get()
142 if x is None:
Georg Brandld22b4662008-02-02 11:39:29 +0000143 q.task_done()
144 return
Brett Cannon4b7deed2008-02-03 02:43:01 +0000145 with self.cumlock:
Georg Brandlcafb7102008-02-02 23:59:21 +0000146 self.cum += x
Georg Brandld22b4662008-02-02 11:39:29 +0000147 q.task_done()
148
149 def queue_join_test(self, q):
150 self.cum = 0
151 for i in (0,1):
152 threading.Thread(target=self.worker, args=(q,)).start()
153 for i in xrange(100):
154 q.put(i)
155 q.join()
156 self.assertEquals(self.cum, sum(range(100)),
Georg Brandlcafb7102008-02-02 23:59:21 +0000157 "q.join() did not block until all tasks were done")
Georg Brandld22b4662008-02-02 11:39:29 +0000158 for i in (0,1):
159 q.put(None) # instruct the threads to close
160 q.join() # verify that you can join twice
161
162 def test_queue_task_done(self):
163 # Test to make sure a queue task completed successfully.
164 q = self.type2test()
165 try:
166 q.task_done()
167 except ValueError:
168 pass
Tim Peters8d7626c2004-08-20 03:27:12 +0000169 else:
Georg Brandld22b4662008-02-02 11:39:29 +0000170 self.fail("Did not detect task count going negative")
171
172 def test_queue_join(self):
173 # Test that a queue join()s successfully, and before anything else
174 # (done twice for insurance).
175 q = self.type2test()
176 self.queue_join_test(q)
177 self.queue_join_test(q)
178 try:
179 q.task_done()
180 except ValueError:
181 pass
182 else:
183 self.fail("Did not detect task count going negative")
184
185 def test_simple_queue(self):
186 # Do it a couple of times on the same queue.
187 # Done twice to make sure works with same instance reused.
188 q = self.type2test(QUEUE_SIZE)
189 self.simple_queue_test(q)
190 self.simple_queue_test(q)
191
192
193class QueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000194 type2test = Queue.Queue
Georg Brandld22b4662008-02-02 11:39:29 +0000195
196class LifoQueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000197 type2test = Queue.LifoQueue
Georg Brandld22b4662008-02-02 11:39:29 +0000198
199class PriorityQueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000200 type2test = Queue.PriorityQueue
Georg Brandld22b4662008-02-02 11:39:29 +0000201
202
Mark Hammond3b959db2002-04-19 00:11:32 +0000203
204# A Queue subclass that can provoke failure at a moment's notice :)
205class FailingQueueException(Exception):
206 pass
207
Georg Brandla6168f92008-05-25 07:20:14 +0000208class FailingQueue(Queue.Queue):
Mark Hammond3b959db2002-04-19 00:11:32 +0000209 def __init__(self, *args):
210 self.fail_next_put = False
211 self.fail_next_get = False
Georg Brandla6168f92008-05-25 07:20:14 +0000212 Queue.Queue.__init__(self, *args)
Mark Hammond3b959db2002-04-19 00:11:32 +0000213 def _put(self, item):
214 if self.fail_next_put:
215 self.fail_next_put = False
216 raise FailingQueueException, "You Lose"
Georg Brandla6168f92008-05-25 07:20:14 +0000217 return Queue.Queue._put(self, item)
Mark Hammond3b959db2002-04-19 00:11:32 +0000218 def _get(self):
219 if self.fail_next_get:
220 self.fail_next_get = False
221 raise FailingQueueException, "You Lose"
Georg Brandla6168f92008-05-25 07:20:14 +0000222 return Queue.Queue._get(self)
Mark Hammond3b959db2002-04-19 00:11:32 +0000223
Georg Brandld22b4662008-02-02 11:39:29 +0000224class FailingQueueTest(unittest.TestCase, BlockingTestMixin):
Mark Hammond3b959db2002-04-19 00:11:32 +0000225
Georg Brandld22b4662008-02-02 11:39:29 +0000226 def failing_queue_test(self, q):
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000227 if not q.empty():
Georg Brandld22b4662008-02-02 11:39:29 +0000228 raise RuntimeError, "Call this function with an empty queue"
229 for i in range(QUEUE_SIZE-1):
230 q.put(i)
231 # Test a failing non-blocking put.
232 q.fail_next_put = True
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000233 try:
Georg Brandld22b4662008-02-02 11:39:29 +0000234 q.put("oops", block=0)
235 self.fail("The queue didn't fail when it should have")
236 except FailingQueueException:
237 pass
238 q.fail_next_put = True
239 try:
240 q.put("oops", timeout=0.1)
241 self.fail("The queue didn't fail when it should have")
242 except FailingQueueException:
243 pass
244 q.put("last")
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000245 self.assert_(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000246 # Test a failing blocking put
247 q.fail_next_put = True
248 try:
249 self.do_blocking_test(q.put, ("full",), q.get, ())
250 self.fail("The queue didn't fail when it should have")
251 except FailingQueueException:
252 pass
253 # Check the Queue isn't damaged.
254 # put failed, but get succeeded - re-add
255 q.put("last")
256 # Test a failing timeout put
257 q.fail_next_put = True
258 try:
259 self.do_exceptional_blocking_test(q.put, ("full", True, 10), q.get, (),
260 FailingQueueException)
261 self.fail("The queue didn't fail when it should have")
262 except FailingQueueException:
263 pass
264 # Check the Queue isn't damaged.
265 # put failed, but get succeeded - re-add
266 q.put("last")
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000267 self.assert_(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000268 q.get()
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000269 self.assert_(not q.full(), "Queue should not be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000270 q.put("last")
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000271 self.assert_(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000272 # Test a blocking put
273 self.do_blocking_test(q.put, ("full",), q.get, ())
274 # Empty it
275 for i in range(QUEUE_SIZE):
276 q.get()
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000277 self.assert_(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000278 q.put("first")
279 q.fail_next_get = True
280 try:
281 q.get()
282 self.fail("The queue didn't fail when it should have")
283 except FailingQueueException:
284 pass
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000285 self.assert_(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000286 q.fail_next_get = True
287 try:
288 q.get(timeout=0.1)
289 self.fail("The queue didn't fail when it should have")
290 except FailingQueueException:
291 pass
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000292 self.assert_(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000293 q.get()
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000294 self.assert_(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000295 q.fail_next_get = True
296 try:
297 self.do_exceptional_blocking_test(q.get, (), q.put, ('empty',),
298 FailingQueueException)
299 self.fail("The queue didn't fail when it should have")
300 except FailingQueueException:
301 pass
302 # put succeeded, but get failed.
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000303 self.assert_(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000304 q.get()
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000305 self.assert_(q.empty(), "Queue should be empty")
Tim Peterse33901e2006-03-25 01:50:43 +0000306
Georg Brandld22b4662008-02-02 11:39:29 +0000307 def test_failing_queue(self):
308 # Test to make sure a queue is functioning correctly.
309 # Done twice to the same instance.
310 q = FailingQueue(QUEUE_SIZE)
311 self.failing_queue_test(q)
312 self.failing_queue_test(q)
Raymond Hettingerc4e94b92006-03-25 12:15:04 +0000313
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000314
Georg Brandld22b4662008-02-02 11:39:29 +0000315def test_main():
316 test_support.run_unittest(QueueTest, LifoQueueTest, PriorityQueueTest,
317 FailingQueueTest)
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000318
Raymond Hettinger9e1bc982008-01-16 23:40:45 +0000319
Georg Brandld22b4662008-02-02 11:39:29 +0000320if __name__ == "__main__":
321 test_main()