blob: 718ed671b92db64d97c8f41a5cb223d5a3d95af7 [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
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004import threading
Mark Hammond3b959db2002-04-19 00:11:32 +00005import time
Georg Brandl0e3b0ec2008-02-05 18:48:51 +00006import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00007from test import support
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02008
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
Raymond Hettingerda3caed2008-01-14 21:39:24 +000012def qfull(q):
13 return q.maxsize > 0 and q.qsize() == q.maxsize
14
Tim Petersafe52972004-08-20 02:37:25 +000015# A thread to run a function that unclogs a blocked Queue.
Mark Hammond3b959db2002-04-19 00:11:32 +000016class _TriggerThread(threading.Thread):
17 def __init__(self, fn, args):
18 self.fn = fn
19 self.args = args
20 self.startedEvent = threading.Event()
21 threading.Thread.__init__(self)
Tim Petersafe52972004-08-20 02:37:25 +000022
Mark Hammond3b959db2002-04-19 00:11:32 +000023 def run(self):
Tim Peters8d7626c2004-08-20 03:27:12 +000024 # The sleep isn't necessary, but is intended to give the blocking
25 # function in the main thread a chance at actually blocking before
26 # we unclog it. But if the sleep is longer than the timeout-based
27 # tests wait in their blocking functions, those tests will fail.
28 # So we give them much longer timeout values compared to the
29 # sleep here (I aimed at 10 seconds for blocking functions --
30 # they should never actually wait that long - they should make
31 # progress as soon as we call self.fn()).
32 time.sleep(0.1)
Mark Hammond3b959db2002-04-19 00:11:32 +000033 self.startedEvent.set()
34 self.fn(*self.args)
35
Tim Peters8d7626c2004-08-20 03:27:12 +000036
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000037# Execute a function that blocks, and in a separate thread, a function that
38# triggers the release. Returns the result of the blocking function. Caution:
39# block_func must guarantee to block until trigger_func is called, and
40# trigger_func must guarantee to change queue state so that block_func can make
41# enough progress to return. In particular, a block_func that just raises an
42# exception regardless of whether trigger_func is called will lead to
43# timing-dependent sporadic failures, and one of those went rarely seen but
44# undiagnosed for years. Now block_func must be unexceptional. If block_func
45# is supposed to raise an exception, call do_exceptional_blocking_test()
46# instead.
47
48class BlockingTestMixin:
49
Ezio Melotti656c8082013-03-23 23:35:06 +020050 def tearDown(self):
51 self.t = None
52
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000053 def do_blocking_test(self, block_func, block_args, trigger_func, trigger_args):
54 self.t = _TriggerThread(trigger_func, trigger_args)
55 self.t.start()
56 self.result = block_func(*block_args)
57 # If block_func returned before our thread made the call, we failed!
Benjamin Peterson672b8032008-06-11 19:14:14 +000058 if not self.t.startedEvent.is_set():
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000059 self.fail("blocking function '%r' appeared not to block" %
60 block_func)
61 self.t.join(10) # make sure the thread terminates
Benjamin Peterson672b8032008-06-11 19:14:14 +000062 if self.t.is_alive():
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000063 self.fail("trigger function '%r' appeared to not return" %
64 trigger_func)
65 return self.result
66
67 # Call this instead if block_func is supposed to raise an exception.
68 def do_exceptional_blocking_test(self,block_func, block_args, trigger_func,
69 trigger_args, expected_exception_class):
70 self.t = _TriggerThread(trigger_func, trigger_args)
71 self.t.start()
Tim Peters8d7626c2004-08-20 03:27:12 +000072 try:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000073 try:
74 block_func(*block_args)
75 except expected_exception_class:
76 raise
77 else:
78 self.fail("expected exception of kind %r" %
79 expected_exception_class)
80 finally:
81 self.t.join(10) # make sure the thread terminates
Benjamin Peterson672b8032008-06-11 19:14:14 +000082 if self.t.is_alive():
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000083 self.fail("trigger function '%r' appeared to not return" %
84 trigger_func)
Benjamin Peterson672b8032008-06-11 19:14:14 +000085 if not self.t.startedEvent.is_set():
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000086 self.fail("trigger thread ended but event never set")
87
88
R David Murrayc6bfce92012-03-17 16:38:39 -040089class BaseQueueTestMixin(BlockingTestMixin):
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000090 def setUp(self):
91 self.cum = 0
92 self.cumlock = threading.Lock()
93
94 def simple_queue_test(self, q):
95 if q.qsize():
96 raise RuntimeError("Call this function with an empty queue")
Brett Cannon671153d2010-07-23 16:56:21 +000097 self.assertTrue(q.empty())
98 self.assertFalse(q.full())
Georg Brandl0e3b0ec2008-02-05 18:48:51 +000099 # I guess we better check things actually queue correctly a little :)
100 q.put(111)
101 q.put(333)
102 q.put(222)
103 target_order = dict(Queue = [111, 333, 222],
104 LifoQueue = [222, 333, 111],
105 PriorityQueue = [111, 222, 333])
106 actual_order = [q.get(), q.get(), q.get()]
Ezio Melottib3aedd42010-11-20 19:04:17 +0000107 self.assertEqual(actual_order, target_order[q.__class__.__name__],
108 "Didn't seem to queue the correct data!")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000109 for i in range(QUEUE_SIZE-1):
110 q.put(i)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000111 self.assertTrue(q.qsize(), "Queue should not be empty")
112 self.assertTrue(not qfull(q), "Queue should not be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000113 last = 2 * QUEUE_SIZE
114 full = 3 * 2 * QUEUE_SIZE
115 q.put(last)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000116 self.assertTrue(qfull(q), "Queue should be full")
Brett Cannon671153d2010-07-23 16:56:21 +0000117 self.assertFalse(q.empty())
118 self.assertTrue(q.full())
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000119 try:
120 q.put(full, block=0)
121 self.fail("Didn't appear to block with a full queue")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000122 except queue.Full:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000123 pass
124 try:
125 q.put(full, timeout=0.01)
126 self.fail("Didn't appear to time-out with a full queue")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000127 except queue.Full:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000128 pass
129 # Test a blocking put
130 self.do_blocking_test(q.put, (full,), q.get, ())
131 self.do_blocking_test(q.put, (full, True, 10), q.get, ())
132 # Empty it
133 for i in range(QUEUE_SIZE):
134 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000135 self.assertTrue(not q.qsize(), "Queue should be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000136 try:
137 q.get(block=0)
138 self.fail("Didn't appear to block with an empty queue")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000139 except queue.Empty:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000140 pass
141 try:
142 q.get(timeout=0.01)
143 self.fail("Didn't appear to time-out with an empty queue")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000144 except queue.Empty:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000145 pass
146 # Test a blocking get
147 self.do_blocking_test(q.get, (), q.put, ('empty',))
148 self.do_blocking_test(q.get, (True, 10), q.put, ('empty',))
149
150
151 def worker(self, q):
152 while True:
153 x = q.get()
Amaury Forgeot d'Arcb4febc72008-04-01 21:23:34 +0000154 if x < 0:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000155 q.task_done()
156 return
157 with self.cumlock:
158 self.cum += x
159 q.task_done()
160
161 def queue_join_test(self, q):
162 self.cum = 0
163 for i in (0,1):
164 threading.Thread(target=self.worker, args=(q,)).start()
165 for i in range(100):
166 q.put(i)
167 q.join()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000168 self.assertEqual(self.cum, sum(range(100)),
169 "q.join() did not block until all tasks were done")
Amaury Forgeot d'Arcb4febc72008-04-01 21:23:34 +0000170 for i in (0,1):
171 q.put(-1) # instruct the threads to close
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000172 q.join() # verify that you can join twice
173
174 def test_queue_task_done(self):
175 # Test to make sure a queue task completed successfully.
176 q = self.type2test()
177 try:
178 q.task_done()
179 except ValueError:
180 pass
Tim Peters8d7626c2004-08-20 03:27:12 +0000181 else:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000182 self.fail("Did not detect task count going negative")
183
184 def test_queue_join(self):
185 # Test that a queue join()s successfully, and before anything else
186 # (done twice for insurance).
187 q = self.type2test()
188 self.queue_join_test(q)
189 self.queue_join_test(q)
190 try:
191 q.task_done()
192 except ValueError:
193 pass
194 else:
195 self.fail("Did not detect task count going negative")
196
197 def test_simple_queue(self):
198 # Do it a couple of times on the same queue.
199 # Done twice to make sure works with same instance reused.
200 q = self.type2test(QUEUE_SIZE)
201 self.simple_queue_test(q)
202 self.simple_queue_test(q)
203
Brett Cannon671153d2010-07-23 16:56:21 +0000204 def test_negative_timeout_raises_exception(self):
205 q = self.type2test(QUEUE_SIZE)
206 with self.assertRaises(ValueError):
207 q.put(1, timeout=-1)
208 with self.assertRaises(ValueError):
209 q.get(1, timeout=-1)
210
211 def test_nowait(self):
212 q = self.type2test(QUEUE_SIZE)
213 for i in range(QUEUE_SIZE):
214 q.put_nowait(1)
215 with self.assertRaises(queue.Full):
216 q.put_nowait(1)
217
218 for i in range(QUEUE_SIZE):
219 q.get_nowait()
220 with self.assertRaises(queue.Empty):
221 q.get_nowait()
222
Raymond Hettinger189316a2010-10-31 17:57:52 +0000223 def test_shrinking_queue(self):
224 # issue 10110
225 q = self.type2test(3)
226 q.put(1)
227 q.put(2)
228 q.put(3)
229 with self.assertRaises(queue.Full):
230 q.put_nowait(4)
231 self.assertEqual(q.qsize(), 3)
232 q.maxsize = 2 # shrink the queue
233 with self.assertRaises(queue.Full):
234 q.put_nowait(4)
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000235
R David Murrayc6bfce92012-03-17 16:38:39 -0400236class QueueTest(BaseQueueTestMixin, unittest.TestCase):
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000237 type2test = queue.Queue
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000238
R David Murrayc6bfce92012-03-17 16:38:39 -0400239class LifoQueueTest(BaseQueueTestMixin, unittest.TestCase):
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000240 type2test = queue.LifoQueue
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000241
R David Murrayc6bfce92012-03-17 16:38:39 -0400242class PriorityQueueTest(BaseQueueTestMixin, unittest.TestCase):
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000243 type2test = queue.PriorityQueue
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000244
245
Mark Hammond3b959db2002-04-19 00:11:32 +0000246
247# A Queue subclass that can provoke failure at a moment's notice :)
248class FailingQueueException(Exception):
249 pass
250
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000251class FailingQueue(queue.Queue):
Mark Hammond3b959db2002-04-19 00:11:32 +0000252 def __init__(self, *args):
253 self.fail_next_put = False
254 self.fail_next_get = False
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000255 queue.Queue.__init__(self, *args)
Mark Hammond3b959db2002-04-19 00:11:32 +0000256 def _put(self, item):
257 if self.fail_next_put:
258 self.fail_next_put = False
Collin Winter3add4d72007-08-29 23:37:32 +0000259 raise FailingQueueException("You Lose")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000260 return queue.Queue._put(self, item)
Mark Hammond3b959db2002-04-19 00:11:32 +0000261 def _get(self):
262 if self.fail_next_get:
263 self.fail_next_get = False
Collin Winter3add4d72007-08-29 23:37:32 +0000264 raise FailingQueueException("You Lose")
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000265 return queue.Queue._get(self)
Mark Hammond3b959db2002-04-19 00:11:32 +0000266
Ezio Melotti656c8082013-03-23 23:35:06 +0200267class FailingQueueTest(BlockingTestMixin, unittest.TestCase):
Mark Hammond3b959db2002-04-19 00:11:32 +0000268
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000269 def failing_queue_test(self, q):
270 if q.qsize():
271 raise RuntimeError("Call this function with an empty queue")
272 for i in range(QUEUE_SIZE-1):
273 q.put(i)
274 # Test a failing non-blocking put.
275 q.fail_next_put = True
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000276 try:
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000277 q.put("oops", block=0)
278 self.fail("The queue didn't fail when it should have")
279 except FailingQueueException:
280 pass
281 q.fail_next_put = True
282 try:
283 q.put("oops", timeout=0.1)
284 self.fail("The queue didn't fail when it should have")
285 except FailingQueueException:
286 pass
287 q.put("last")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000288 self.assertTrue(qfull(q), "Queue should be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000289 # Test a failing blocking put
290 q.fail_next_put = True
291 try:
292 self.do_blocking_test(q.put, ("full",), q.get, ())
293 self.fail("The queue didn't fail when it should have")
294 except FailingQueueException:
295 pass
296 # Check the Queue isn't damaged.
297 # put failed, but get succeeded - re-add
298 q.put("last")
299 # Test a failing timeout put
300 q.fail_next_put = True
301 try:
302 self.do_exceptional_blocking_test(q.put, ("full", True, 10), q.get, (),
303 FailingQueueException)
304 self.fail("The queue didn't fail when it should have")
305 except FailingQueueException:
306 pass
307 # Check the Queue isn't damaged.
308 # put failed, but get succeeded - re-add
309 q.put("last")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000310 self.assertTrue(qfull(q), "Queue should be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000311 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000312 self.assertTrue(not qfull(q), "Queue should not be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000313 q.put("last")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000314 self.assertTrue(qfull(q), "Queue should be full")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000315 # Test a blocking put
316 self.do_blocking_test(q.put, ("full",), q.get, ())
317 # Empty it
318 for i in range(QUEUE_SIZE):
319 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000320 self.assertTrue(not q.qsize(), "Queue should be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000321 q.put("first")
322 q.fail_next_get = True
323 try:
324 q.get()
325 self.fail("The queue didn't fail when it should have")
326 except FailingQueueException:
327 pass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000328 self.assertTrue(q.qsize(), "Queue should not be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000329 q.fail_next_get = True
330 try:
331 q.get(timeout=0.1)
332 self.fail("The queue didn't fail when it should have")
333 except FailingQueueException:
334 pass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000335 self.assertTrue(q.qsize(), "Queue should not be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000336 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000337 self.assertTrue(not q.qsize(), "Queue should be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000338 q.fail_next_get = True
339 try:
340 self.do_exceptional_blocking_test(q.get, (), q.put, ('empty',),
341 FailingQueueException)
342 self.fail("The queue didn't fail when it should have")
343 except FailingQueueException:
344 pass
345 # put succeeded, but get failed.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000346 self.assertTrue(q.qsize(), "Queue should not be empty")
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000347 q.get()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000348 self.assertTrue(not q.qsize(), "Queue should be empty")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000349
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000350 def test_failing_queue(self):
351 # Test to make sure a queue is functioning correctly.
352 # Done twice to the same instance.
353 q = FailingQueue(QUEUE_SIZE)
354 self.failing_queue_test(q)
355 self.failing_queue_test(q)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000356
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000357
Georg Brandl0e3b0ec2008-02-05 18:48:51 +0000358if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500359 unittest.main()