blob: 4ccaa39adff69fdf5fcf189486a6117e555ca13d [file] [log] [blame]
Alexandre Vassalottif260e442008-05-11 19:59:59 +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.
Alexandre Vassalottif260e442008-05-11 19:59:59 +00003import queue
Mark Hammond3b959db2002-04-19 00:11:32 +00004import time
Georg Brandl0e3b0ec2008-02-05 18:48:51 +00005import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00006from test import support
Victor Stinner45df8202010-04-28 22:31:17 +00007threading = support.import_module('threading')
Mark Hammond3b959db2002-04-19 00:11:32 +00008
Tim Petersafe52972004-08-20 02:37:25 +00009QUEUE_SIZE = 5
Mark Hammond3b959db2002-04-19 00:11:32 +000010
Raymond Hettingerda3caed2008-01-14 21:39:24 +000011def qfull(q):
12 return q.maxsize > 0 and q.qsize() == q.maxsize
13
Tim Petersafe52972004-08-20 02:37:25 +000014# A thread to run a function that unclogs a blocked Queue.
Mark Hammond3b959db2002-04-19 00:11:32 +000015class _TriggerThread(threading.Thread):
16 def __init__(self, fn, args):
17 self.fn = fn
18 self.args = args
19 self.startedEvent = threading.Event()
20 threading.Thread.__init__(self)
Tim Petersafe52972004-08-20 02:37:25 +000021
Mark Hammond3b959db2002-04-19 00:11:32 +000022 def run(self):
Tim Peters8d7626c2004-08-20 03:27:12 +000023 # The sleep isn't necessary, but is intended to give the blocking
24 # function in the main thread a chance at actually blocking before
25 # we unclog it. But if the sleep is longer than the timeout-based
26 # tests wait in their blocking functions, those tests will fail.
27 # So we give them much longer timeout values compared to the
28 # sleep here (I aimed at 10 seconds for blocking functions --
29 # they should never actually wait that long - they should make
30 # progress as soon as we call self.fn()).
31 time.sleep(0.1)
Mark Hammond3b959db2002-04-19 00:11:32 +000032 self.startedEvent.set()
33 self.fn(*self.args)
34
Tim Peters8d7626c2004-08-20 03:27:12 +000035
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000036# Execute a function that blocks, and in a separate thread, a function that
37# triggers the release. Returns the result of the blocking function. Caution:
38# block_func must guarantee to block until trigger_func is called, and
39# trigger_func must guarantee to change queue state so that block_func can make
40# enough progress to return. In particular, a block_func that just raises an
41# exception regardless of whether trigger_func is called will lead to
42# timing-dependent sporadic failures, and one of those went rarely seen but
43# undiagnosed for years. Now block_func must be unexceptional. If block_func
44# is supposed to raise an exception, call do_exceptional_blocking_test()
45# instead.
46
47class BlockingTestMixin:
48
Ezio Melotti656c8082013-03-23 23:35:06 +020049 def tearDown(self):
50 self.t = None
51
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000052 def do_blocking_test(self, block_func, block_args, trigger_func, trigger_args):
53 self.t = _TriggerThread(trigger_func, trigger_args)
54 self.t.start()
55 self.result = block_func(*block_args)
56 # If block_func returned before our thread made the call, we failed!
Benjamin Peterson672b8032008-06-11 19:14:14 +000057 if not self.t.startedEvent.is_set():
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000058 self.fail("blocking function '%r' appeared not to block" %
59 block_func)
60 self.t.join(10) # make sure the thread terminates
Benjamin Peterson672b8032008-06-11 19:14:14 +000061 if self.t.is_alive():
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000062 self.fail("trigger function '%r' appeared to not return" %
63 trigger_func)
64 return self.result
65
66 # Call this instead if block_func is supposed to raise an exception.
67 def do_exceptional_blocking_test(self,block_func, block_args, trigger_func,
68 trigger_args, expected_exception_class):
69 self.t = _TriggerThread(trigger_func, trigger_args)
70 self.t.start()
Tim Peters8d7626c2004-08-20 03:27:12 +000071 try:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000072 try:
73 block_func(*block_args)
74 except expected_exception_class:
75 raise
76 else:
77 self.fail("expected exception of kind %r" %
78 expected_exception_class)
79 finally:
80 self.t.join(10) # make sure the thread terminates
Benjamin Peterson672b8032008-06-11 19:14:14 +000081 if self.t.is_alive():
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000082 self.fail("trigger function '%r' appeared to not return" %
83 trigger_func)
Benjamin Peterson672b8032008-06-11 19:14:14 +000084 if not self.t.startedEvent.is_set():
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000085 self.fail("trigger thread ended but event never set")
86
87
R David Murrayc6bfce92012-03-17 16:38:39 -040088class BaseQueueTestMixin(BlockingTestMixin):
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000089 def setUp(self):
90 self.cum = 0
91 self.cumlock = threading.Lock()
92
93 def simple_queue_test(self, q):
94 if q.qsize():
95 raise RuntimeError("Call this function with an empty queue")
Brett Cannon671153d2010-07-23 16:56:21 +000096 self.assertTrue(q.empty())
97 self.assertFalse(q.full())
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000098 # I guess we better check things actually queue correctly a little :)
99 q.put(111)
100 q.put(333)
101 q.put(222)
102 target_order = dict(Queue = [111, 333, 222],
103 LifoQueue = [222, 333, 111],
104 PriorityQueue = [111, 222, 333])
105 actual_order = [q.get(), q.get(), q.get()]
Ezio Melottib3aedd42010-11-20 19:04:17 +0000106 self.assertEqual(actual_order, target_order[q.__class__.__name__],
107 "Didn't seem to queue the correct data!")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000108 for i in range(QUEUE_SIZE-1):
109 q.put(i)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000110 self.assertTrue(q.qsize(), "Queue should not be empty")
111 self.assertTrue(not qfull(q), "Queue should not be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000112 last = 2 * QUEUE_SIZE
113 full = 3 * 2 * QUEUE_SIZE
114 q.put(last)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000115 self.assertTrue(qfull(q), "Queue should be full")
Brett Cannon671153d2010-07-23 16:56:21 +0000116 self.assertFalse(q.empty())
117 self.assertTrue(q.full())
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000118 try:
119 q.put(full, block=0)
120 self.fail("Didn't appear to block with a full queue")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000121 except queue.Full:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000122 pass
123 try:
124 q.put(full, timeout=0.01)
125 self.fail("Didn't appear to time-out with a full queue")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000126 except queue.Full:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000127 pass
128 # Test a blocking put
129 self.do_blocking_test(q.put, (full,), q.get, ())
130 self.do_blocking_test(q.put, (full, True, 10), q.get, ())
131 # Empty it
132 for i in range(QUEUE_SIZE):
133 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000134 self.assertTrue(not q.qsize(), "Queue should be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000135 try:
136 q.get(block=0)
137 self.fail("Didn't appear to block with an empty queue")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000138 except queue.Empty:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000139 pass
140 try:
141 q.get(timeout=0.01)
142 self.fail("Didn't appear to time-out with an empty queue")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000143 except queue.Empty:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000144 pass
145 # Test a blocking get
146 self.do_blocking_test(q.get, (), q.put, ('empty',))
147 self.do_blocking_test(q.get, (True, 10), q.put, ('empty',))
148
149
150 def worker(self, q):
151 while True:
152 x = q.get()
Amaury Forgeot d'Arcb4febc72008-04-01 21:23:34 +0000153 if x < 0:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000154 q.task_done()
155 return
156 with self.cumlock:
157 self.cum += x
158 q.task_done()
159
160 def queue_join_test(self, q):
161 self.cum = 0
162 for i in (0,1):
163 threading.Thread(target=self.worker, args=(q,)).start()
164 for i in range(100):
165 q.put(i)
166 q.join()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000167 self.assertEqual(self.cum, sum(range(100)),
168 "q.join() did not block until all tasks were done")
Amaury Forgeot d'Arcb4febc72008-04-01 21:23:34 +0000169 for i in (0,1):
170 q.put(-1) # instruct the threads to close
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000171 q.join() # verify that you can join twice
172
173 def test_queue_task_done(self):
174 # Test to make sure a queue task completed successfully.
175 q = self.type2test()
176 try:
177 q.task_done()
178 except ValueError:
179 pass
Tim Peters8d7626c2004-08-20 03:27:12 +0000180 else:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000181 self.fail("Did not detect task count going negative")
182
183 def test_queue_join(self):
184 # Test that a queue join()s successfully, and before anything else
185 # (done twice for insurance).
186 q = self.type2test()
187 self.queue_join_test(q)
188 self.queue_join_test(q)
189 try:
190 q.task_done()
191 except ValueError:
192 pass
193 else:
194 self.fail("Did not detect task count going negative")
195
196 def test_simple_queue(self):
197 # Do it a couple of times on the same queue.
198 # Done twice to make sure works with same instance reused.
199 q = self.type2test(QUEUE_SIZE)
200 self.simple_queue_test(q)
201 self.simple_queue_test(q)
202
Brett Cannon671153d2010-07-23 16:56:21 +0000203 def test_negative_timeout_raises_exception(self):
204 q = self.type2test(QUEUE_SIZE)
205 with self.assertRaises(ValueError):
206 q.put(1, timeout=-1)
207 with self.assertRaises(ValueError):
208 q.get(1, timeout=-1)
209
210 def test_nowait(self):
211 q = self.type2test(QUEUE_SIZE)
212 for i in range(QUEUE_SIZE):
213 q.put_nowait(1)
214 with self.assertRaises(queue.Full):
215 q.put_nowait(1)
216
217 for i in range(QUEUE_SIZE):
218 q.get_nowait()
219 with self.assertRaises(queue.Empty):
220 q.get_nowait()
221
Raymond Hettinger189316a2010-10-31 17:57:52 +0000222 def test_shrinking_queue(self):
223 # issue 10110
224 q = self.type2test(3)
225 q.put(1)
226 q.put(2)
227 q.put(3)
228 with self.assertRaises(queue.Full):
229 q.put_nowait(4)
230 self.assertEqual(q.qsize(), 3)
231 q.maxsize = 2 # shrink the queue
232 with self.assertRaises(queue.Full):
233 q.put_nowait(4)
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000234
R David Murrayc6bfce92012-03-17 16:38:39 -0400235class QueueTest(BaseQueueTestMixin, unittest.TestCase):
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000236 type2test = queue.Queue
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000237
R David Murrayc6bfce92012-03-17 16:38:39 -0400238class LifoQueueTest(BaseQueueTestMixin, unittest.TestCase):
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000239 type2test = queue.LifoQueue
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000240
R David Murrayc6bfce92012-03-17 16:38:39 -0400241class PriorityQueueTest(BaseQueueTestMixin, unittest.TestCase):
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000242 type2test = queue.PriorityQueue
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000243
244
Mark Hammond3b959db2002-04-19 00:11:32 +0000245
246# A Queue subclass that can provoke failure at a moment's notice :)
247class FailingQueueException(Exception):
248 pass
249
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000250class FailingQueue(queue.Queue):
Mark Hammond3b959db2002-04-19 00:11:32 +0000251 def __init__(self, *args):
252 self.fail_next_put = False
253 self.fail_next_get = False
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000254 queue.Queue.__init__(self, *args)
Mark Hammond3b959db2002-04-19 00:11:32 +0000255 def _put(self, item):
256 if self.fail_next_put:
257 self.fail_next_put = False
Collin Winter3add4d72007-08-29 23:37:32 +0000258 raise FailingQueueException("You Lose")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000259 return queue.Queue._put(self, item)
Mark Hammond3b959db2002-04-19 00:11:32 +0000260 def _get(self):
261 if self.fail_next_get:
262 self.fail_next_get = False
Collin Winter3add4d72007-08-29 23:37:32 +0000263 raise FailingQueueException("You Lose")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000264 return queue.Queue._get(self)
Mark Hammond3b959db2002-04-19 00:11:32 +0000265
Ezio Melotti656c8082013-03-23 23:35:06 +0200266class FailingQueueTest(BlockingTestMixin, unittest.TestCase):
Mark Hammond3b959db2002-04-19 00:11:32 +0000267
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000268 def failing_queue_test(self, q):
269 if q.qsize():
270 raise RuntimeError("Call this function with an empty queue")
271 for i in range(QUEUE_SIZE-1):
272 q.put(i)
273 # Test a failing non-blocking put.
274 q.fail_next_put = True
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000275 try:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000276 q.put("oops", block=0)
277 self.fail("The queue didn't fail when it should have")
278 except FailingQueueException:
279 pass
280 q.fail_next_put = True
281 try:
282 q.put("oops", timeout=0.1)
283 self.fail("The queue didn't fail when it should have")
284 except FailingQueueException:
285 pass
286 q.put("last")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000287 self.assertTrue(qfull(q), "Queue should be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000288 # Test a failing blocking put
289 q.fail_next_put = True
290 try:
291 self.do_blocking_test(q.put, ("full",), q.get, ())
292 self.fail("The queue didn't fail when it should have")
293 except FailingQueueException:
294 pass
295 # Check the Queue isn't damaged.
296 # put failed, but get succeeded - re-add
297 q.put("last")
298 # Test a failing timeout put
299 q.fail_next_put = True
300 try:
301 self.do_exceptional_blocking_test(q.put, ("full", True, 10), q.get, (),
302 FailingQueueException)
303 self.fail("The queue didn't fail when it should have")
304 except FailingQueueException:
305 pass
306 # Check the Queue isn't damaged.
307 # put failed, but get succeeded - re-add
308 q.put("last")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000309 self.assertTrue(qfull(q), "Queue should be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000310 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000311 self.assertTrue(not qfull(q), "Queue should not be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000312 q.put("last")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000313 self.assertTrue(qfull(q), "Queue should be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000314 # Test a blocking put
315 self.do_blocking_test(q.put, ("full",), q.get, ())
316 # Empty it
317 for i in range(QUEUE_SIZE):
318 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000319 self.assertTrue(not q.qsize(), "Queue should be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000320 q.put("first")
321 q.fail_next_get = True
322 try:
323 q.get()
324 self.fail("The queue didn't fail when it should have")
325 except FailingQueueException:
326 pass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000327 self.assertTrue(q.qsize(), "Queue should not be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000328 q.fail_next_get = True
329 try:
330 q.get(timeout=0.1)
331 self.fail("The queue didn't fail when it should have")
332 except FailingQueueException:
333 pass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000334 self.assertTrue(q.qsize(), "Queue should not be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000335 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000336 self.assertTrue(not q.qsize(), "Queue should be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000337 q.fail_next_get = True
338 try:
339 self.do_exceptional_blocking_test(q.get, (), q.put, ('empty',),
340 FailingQueueException)
341 self.fail("The queue didn't fail when it should have")
342 except FailingQueueException:
343 pass
344 # put succeeded, but get failed.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000345 self.assertTrue(q.qsize(), "Queue should not be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000346 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000347 self.assertTrue(not q.qsize(), "Queue should be empty")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000348
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000349 def test_failing_queue(self):
350 # Test to make sure a queue is functioning correctly.
351 # Done twice to the same instance.
352 q = FailingQueue(QUEUE_SIZE)
353 self.failing_queue_test(q)
354 self.failing_queue_test(q)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000355
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000356
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000357if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500358 unittest.main()