blob: e6d2e6961332460320dc0db8a9f56e9c9103a4df [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
Senthil Kumaran3ddc4352010-01-08 18:41:40 +000010QUEUE_SIZE = LAST = 5
11FULL = LAST+1
Mark Hammond3b959db2002-04-19 00:11:32 +000012
Tim Petersafe52972004-08-20 02:37:25 +000013# A thread to run a function that unclogs a blocked Queue.
Mark Hammond3b959db2002-04-19 00:11:32 +000014class _TriggerThread(threading.Thread):
15 def __init__(self, fn, args):
16 self.fn = fn
17 self.args = args
18 self.startedEvent = threading.Event()
19 threading.Thread.__init__(self)
Tim Petersafe52972004-08-20 02:37:25 +000020
Mark Hammond3b959db2002-04-19 00:11:32 +000021 def run(self):
Tim Peters8d7626c2004-08-20 03:27:12 +000022 # The sleep isn't necessary, but is intended to give the blocking
23 # function in the main thread a chance at actually blocking before
24 # we unclog it. But if the sleep is longer than the timeout-based
25 # tests wait in their blocking functions, those tests will fail.
26 # So we give them much longer timeout values compared to the
27 # sleep here (I aimed at 10 seconds for blocking functions --
28 # they should never actually wait that long - they should make
29 # progress as soon as we call self.fn()).
30 time.sleep(0.1)
Mark Hammond3b959db2002-04-19 00:11:32 +000031 self.startedEvent.set()
32 self.fn(*self.args)
33
Tim Peters8d7626c2004-08-20 03:27:12 +000034
Georg Brandld22b4662008-02-02 11:39:29 +000035# Execute a function that blocks, and in a separate thread, a function that
36# triggers the release. Returns the result of the blocking function. Caution:
37# block_func must guarantee to block until trigger_func is called, and
38# trigger_func must guarantee to change queue state so that block_func can make
39# enough progress to return. In particular, a block_func that just raises an
40# exception regardless of whether trigger_func is called will lead to
41# timing-dependent sporadic failures, and one of those went rarely seen but
42# undiagnosed for years. Now block_func must be unexceptional. If block_func
43# is supposed to raise an exception, call do_exceptional_blocking_test()
44# instead.
45
46class BlockingTestMixin:
47
48 def do_blocking_test(self, block_func, block_args, trigger_func, trigger_args):
49 self.t = _TriggerThread(trigger_func, trigger_args)
50 self.t.start()
51 self.result = block_func(*block_args)
52 # If block_func returned before our thread made the call, we failed!
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000053 if not self.t.startedEvent.is_set():
Georg Brandld22b4662008-02-02 11:39:29 +000054 self.fail("blocking function '%r' appeared not to block" %
55 block_func)
56 self.t.join(10) # make sure the thread terminates
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000057 if self.t.is_alive():
Georg Brandld22b4662008-02-02 11:39:29 +000058 self.fail("trigger function '%r' appeared to not return" %
59 trigger_func)
60 return self.result
61
62 # Call this instead if block_func is supposed to raise an exception.
63 def do_exceptional_blocking_test(self,block_func, block_args, trigger_func,
64 trigger_args, expected_exception_class):
65 self.t = _TriggerThread(trigger_func, trigger_args)
66 self.t.start()
Tim Peters8d7626c2004-08-20 03:27:12 +000067 try:
Georg Brandld22b4662008-02-02 11:39:29 +000068 try:
69 block_func(*block_args)
70 except expected_exception_class:
71 raise
72 else:
73 self.fail("expected exception of kind %r" %
74 expected_exception_class)
75 finally:
76 self.t.join(10) # make sure the thread terminates
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000077 if self.t.is_alive():
Georg Brandld22b4662008-02-02 11:39:29 +000078 self.fail("trigger function '%r' appeared to not return" %
79 trigger_func)
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000080 if not self.t.startedEvent.is_set():
Georg Brandld22b4662008-02-02 11:39:29 +000081 self.fail("trigger thread ended but event never set")
82
83
84class BaseQueueTest(unittest.TestCase, BlockingTestMixin):
85 def setUp(self):
86 self.cum = 0
87 self.cumlock = threading.Lock()
88
89 def simple_queue_test(self, q):
Georg Brandl3c3d9ac2008-02-06 23:45:51 +000090 if not q.empty():
Georg Brandld22b4662008-02-02 11:39:29 +000091 raise RuntimeError, "Call this function with an empty queue"
92 # I guess we better check things actually queue correctly a little :)
93 q.put(111)
94 q.put(333)
95 q.put(222)
96 target_order = dict(Queue = [111, 333, 222],
97 LifoQueue = [222, 333, 111],
98 PriorityQueue = [111, 222, 333])
99 actual_order = [q.get(), q.get(), q.get()]
100 self.assertEquals(actual_order, target_order[q.__class__.__name__],
101 "Didn't seem to queue the correct data!")
102 for i in range(QUEUE_SIZE-1):
103 q.put(i)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000104 self.assertTrue(not q.empty(), "Queue should not be empty")
105 self.assertTrue(not q.full(), "Queue should not be full")
Senthil Kumaran3ddc4352010-01-08 18:41:40 +0000106 q.put(LAST)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000107 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000108 try:
Senthil Kumaran3ddc4352010-01-08 18:41:40 +0000109 q.put(FULL, block=0)
Georg Brandld22b4662008-02-02 11:39:29 +0000110 self.fail("Didn't appear to block with a full queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000111 except Queue.Full:
Georg Brandld22b4662008-02-02 11:39:29 +0000112 pass
113 try:
Senthil Kumaran3ddc4352010-01-08 18:41:40 +0000114 q.put(FULL, timeout=0.01)
Georg Brandld22b4662008-02-02 11:39:29 +0000115 self.fail("Didn't appear to time-out with a full queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000116 except Queue.Full:
Georg Brandld22b4662008-02-02 11:39:29 +0000117 pass
118 # Test a blocking put
Senthil Kumaran3ddc4352010-01-08 18:41:40 +0000119 self.do_blocking_test(q.put, (FULL,), q.get, ())
120 self.do_blocking_test(q.put, (FULL, True, 10), q.get, ())
Georg Brandld22b4662008-02-02 11:39:29 +0000121 # Empty it
122 for i in range(QUEUE_SIZE):
123 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000124 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000125 try:
126 q.get(block=0)
127 self.fail("Didn't appear to block with an empty queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000128 except Queue.Empty:
Georg Brandld22b4662008-02-02 11:39:29 +0000129 pass
130 try:
131 q.get(timeout=0.01)
132 self.fail("Didn't appear to time-out with an empty queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000133 except Queue.Empty:
Georg Brandld22b4662008-02-02 11:39:29 +0000134 pass
135 # Test a blocking get
136 self.do_blocking_test(q.get, (), q.put, ('empty',))
137 self.do_blocking_test(q.get, (True, 10), q.put, ('empty',))
138
139
140 def worker(self, q):
141 while True:
Georg Brandlcafb7102008-02-02 23:59:21 +0000142 x = q.get()
143 if x is None:
Georg Brandld22b4662008-02-02 11:39:29 +0000144 q.task_done()
145 return
Brett Cannon4b7deed2008-02-03 02:43:01 +0000146 with self.cumlock:
Georg Brandlcafb7102008-02-02 23:59:21 +0000147 self.cum += x
Georg Brandld22b4662008-02-02 11:39:29 +0000148 q.task_done()
149
150 def queue_join_test(self, q):
151 self.cum = 0
152 for i in (0,1):
153 threading.Thread(target=self.worker, args=(q,)).start()
154 for i in xrange(100):
155 q.put(i)
156 q.join()
157 self.assertEquals(self.cum, sum(range(100)),
Georg Brandlcafb7102008-02-02 23:59:21 +0000158 "q.join() did not block until all tasks were done")
Georg Brandld22b4662008-02-02 11:39:29 +0000159 for i in (0,1):
160 q.put(None) # instruct the threads to close
161 q.join() # verify that you can join twice
162
163 def test_queue_task_done(self):
164 # Test to make sure a queue task completed successfully.
165 q = self.type2test()
166 try:
167 q.task_done()
168 except ValueError:
169 pass
Tim Peters8d7626c2004-08-20 03:27:12 +0000170 else:
Georg Brandld22b4662008-02-02 11:39:29 +0000171 self.fail("Did not detect task count going negative")
172
173 def test_queue_join(self):
174 # Test that a queue join()s successfully, and before anything else
175 # (done twice for insurance).
176 q = self.type2test()
177 self.queue_join_test(q)
178 self.queue_join_test(q)
179 try:
180 q.task_done()
181 except ValueError:
182 pass
183 else:
184 self.fail("Did not detect task count going negative")
185
186 def test_simple_queue(self):
187 # Do it a couple of times on the same queue.
188 # Done twice to make sure works with same instance reused.
189 q = self.type2test(QUEUE_SIZE)
190 self.simple_queue_test(q)
191 self.simple_queue_test(q)
192
193
194class QueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000195 type2test = Queue.Queue
Georg Brandld22b4662008-02-02 11:39:29 +0000196
197class LifoQueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000198 type2test = Queue.LifoQueue
Georg Brandld22b4662008-02-02 11:39:29 +0000199
200class PriorityQueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000201 type2test = Queue.PriorityQueue
Georg Brandld22b4662008-02-02 11:39:29 +0000202
203
Mark Hammond3b959db2002-04-19 00:11:32 +0000204
205# A Queue subclass that can provoke failure at a moment's notice :)
206class FailingQueueException(Exception):
207 pass
208
Georg Brandla6168f92008-05-25 07:20:14 +0000209class FailingQueue(Queue.Queue):
Mark Hammond3b959db2002-04-19 00:11:32 +0000210 def __init__(self, *args):
211 self.fail_next_put = False
212 self.fail_next_get = False
Georg Brandla6168f92008-05-25 07:20:14 +0000213 Queue.Queue.__init__(self, *args)
Mark Hammond3b959db2002-04-19 00:11:32 +0000214 def _put(self, item):
215 if self.fail_next_put:
216 self.fail_next_put = False
217 raise FailingQueueException, "You Lose"
Georg Brandla6168f92008-05-25 07:20:14 +0000218 return Queue.Queue._put(self, item)
Mark Hammond3b959db2002-04-19 00:11:32 +0000219 def _get(self):
220 if self.fail_next_get:
221 self.fail_next_get = False
222 raise FailingQueueException, "You Lose"
Georg Brandla6168f92008-05-25 07:20:14 +0000223 return Queue.Queue._get(self)
Mark Hammond3b959db2002-04-19 00:11:32 +0000224
Georg Brandld22b4662008-02-02 11:39:29 +0000225class FailingQueueTest(unittest.TestCase, BlockingTestMixin):
Mark Hammond3b959db2002-04-19 00:11:32 +0000226
Georg Brandld22b4662008-02-02 11:39:29 +0000227 def failing_queue_test(self, q):
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000228 if not q.empty():
Georg Brandld22b4662008-02-02 11:39:29 +0000229 raise RuntimeError, "Call this function with an empty queue"
230 for i in range(QUEUE_SIZE-1):
231 q.put(i)
232 # Test a failing non-blocking put.
233 q.fail_next_put = True
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000234 try:
Georg Brandld22b4662008-02-02 11:39:29 +0000235 q.put("oops", block=0)
236 self.fail("The queue didn't fail when it should have")
237 except FailingQueueException:
238 pass
239 q.fail_next_put = True
240 try:
241 q.put("oops", timeout=0.1)
242 self.fail("The queue didn't fail when it should have")
243 except FailingQueueException:
244 pass
245 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000246 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000247 # Test a failing blocking put
248 q.fail_next_put = True
249 try:
250 self.do_blocking_test(q.put, ("full",), q.get, ())
251 self.fail("The queue didn't fail when it should have")
252 except FailingQueueException:
253 pass
254 # Check the Queue isn't damaged.
255 # put failed, but get succeeded - re-add
256 q.put("last")
257 # Test a failing timeout put
258 q.fail_next_put = True
259 try:
260 self.do_exceptional_blocking_test(q.put, ("full", True, 10), q.get, (),
261 FailingQueueException)
262 self.fail("The queue didn't fail when it should have")
263 except FailingQueueException:
264 pass
265 # Check the Queue isn't damaged.
266 # put failed, but get succeeded - re-add
267 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000268 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000269 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000270 self.assertTrue(not q.full(), "Queue should not be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000271 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000272 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000273 # Test a blocking put
274 self.do_blocking_test(q.put, ("full",), q.get, ())
275 # Empty it
276 for i in range(QUEUE_SIZE):
277 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000278 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000279 q.put("first")
280 q.fail_next_get = True
281 try:
282 q.get()
283 self.fail("The queue didn't fail when it should have")
284 except FailingQueueException:
285 pass
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000286 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000287 q.fail_next_get = True
288 try:
289 q.get(timeout=0.1)
290 self.fail("The queue didn't fail when it should have")
291 except FailingQueueException:
292 pass
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000293 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000294 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000295 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000296 q.fail_next_get = True
297 try:
298 self.do_exceptional_blocking_test(q.get, (), q.put, ('empty',),
299 FailingQueueException)
300 self.fail("The queue didn't fail when it should have")
301 except FailingQueueException:
302 pass
303 # put succeeded, but get failed.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000304 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000305 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000306 self.assertTrue(q.empty(), "Queue should be empty")
Tim Peterse33901e2006-03-25 01:50:43 +0000307
Georg Brandld22b4662008-02-02 11:39:29 +0000308 def test_failing_queue(self):
309 # Test to make sure a queue is functioning correctly.
310 # Done twice to the same instance.
311 q = FailingQueue(QUEUE_SIZE)
312 self.failing_queue_test(q)
313 self.failing_queue_test(q)
Raymond Hettingerc4e94b92006-03-25 12:15:04 +0000314
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000315
Georg Brandld22b4662008-02-02 11:39:29 +0000316def test_main():
317 test_support.run_unittest(QueueTest, LifoQueueTest, PriorityQueueTest,
318 FailingQueueTest)
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000319
Raymond Hettinger9e1bc982008-01-16 23:40:45 +0000320
Georg Brandld22b4662008-02-02 11:39:29 +0000321if __name__ == "__main__":
322 test_main()