blob: 34a4aef1980dea30321a8bd3b91e4bab33d35023 [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 time
Georg Brandld22b4662008-02-02 11:39:29 +00005import unittest
6from test import test_support
Victor Stinner6a102812010-04-27 23:55:59 +00007threading = test_support.import_module('threading')
Mark Hammond3b959db2002-04-19 00:11:32 +00008
Senthil Kumarance8e33a2010-01-08 19:04:16 +00009QUEUE_SIZE = 5
Mark Hammond3b959db2002-04-19 00:11:32 +000010
Tim Petersafe52972004-08-20 02:37:25 +000011# A thread to run a function that unclogs a blocked Queue.
Mark Hammond3b959db2002-04-19 00:11:32 +000012class _TriggerThread(threading.Thread):
13 def __init__(self, fn, args):
14 self.fn = fn
15 self.args = args
16 self.startedEvent = threading.Event()
17 threading.Thread.__init__(self)
Tim Petersafe52972004-08-20 02:37:25 +000018
Mark Hammond3b959db2002-04-19 00:11:32 +000019 def run(self):
Tim Peters8d7626c2004-08-20 03:27:12 +000020 # The sleep isn't necessary, but is intended to give the blocking
21 # function in the main thread a chance at actually blocking before
22 # we unclog it. But if the sleep is longer than the timeout-based
23 # tests wait in their blocking functions, those tests will fail.
24 # So we give them much longer timeout values compared to the
25 # sleep here (I aimed at 10 seconds for blocking functions --
26 # they should never actually wait that long - they should make
27 # progress as soon as we call self.fn()).
28 time.sleep(0.1)
Mark Hammond3b959db2002-04-19 00:11:32 +000029 self.startedEvent.set()
30 self.fn(*self.args)
31
Tim Peters8d7626c2004-08-20 03:27:12 +000032
Georg Brandld22b4662008-02-02 11:39:29 +000033# Execute a function that blocks, and in a separate thread, a function that
34# triggers the release. Returns the result of the blocking function. Caution:
35# block_func must guarantee to block until trigger_func is called, and
36# trigger_func must guarantee to change queue state so that block_func can make
37# enough progress to return. In particular, a block_func that just raises an
38# exception regardless of whether trigger_func is called will lead to
39# timing-dependent sporadic failures, and one of those went rarely seen but
40# undiagnosed for years. Now block_func must be unexceptional. If block_func
41# is supposed to raise an exception, call do_exceptional_blocking_test()
42# instead.
43
44class BlockingTestMixin:
45
Ezio Melottibe1632b2013-03-23 23:35:06 +020046 def tearDown(self):
47 self.t = None
48
Georg Brandld22b4662008-02-02 11:39:29 +000049 def do_blocking_test(self, block_func, block_args, trigger_func, trigger_args):
50 self.t = _TriggerThread(trigger_func, trigger_args)
51 self.t.start()
52 self.result = block_func(*block_args)
53 # If block_func returned before our thread made the call, we failed!
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000054 if not self.t.startedEvent.is_set():
Georg Brandld22b4662008-02-02 11:39:29 +000055 self.fail("blocking function '%r' appeared not to block" %
56 block_func)
57 self.t.join(10) # make sure the thread terminates
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000058 if self.t.is_alive():
Georg Brandld22b4662008-02-02 11:39:29 +000059 self.fail("trigger function '%r' appeared to not return" %
60 trigger_func)
61 return self.result
62
63 # Call this instead if block_func is supposed to raise an exception.
64 def do_exceptional_blocking_test(self,block_func, block_args, trigger_func,
65 trigger_args, expected_exception_class):
66 self.t = _TriggerThread(trigger_func, trigger_args)
67 self.t.start()
Tim Peters8d7626c2004-08-20 03:27:12 +000068 try:
Georg Brandld22b4662008-02-02 11:39:29 +000069 try:
70 block_func(*block_args)
71 except expected_exception_class:
72 raise
73 else:
74 self.fail("expected exception of kind %r" %
75 expected_exception_class)
76 finally:
77 self.t.join(10) # make sure the thread terminates
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000078 if self.t.is_alive():
Georg Brandld22b4662008-02-02 11:39:29 +000079 self.fail("trigger function '%r' appeared to not return" %
80 trigger_func)
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000081 if not self.t.startedEvent.is_set():
Georg Brandld22b4662008-02-02 11:39:29 +000082 self.fail("trigger thread ended but event never set")
83
84
R David Murray5c2689a2012-03-17 16:44:16 -040085class BaseQueueTest(BlockingTestMixin):
Georg Brandld22b4662008-02-02 11:39:29 +000086 def setUp(self):
87 self.cum = 0
88 self.cumlock = threading.Lock()
89
90 def simple_queue_test(self, q):
Georg Brandl3c3d9ac2008-02-06 23:45:51 +000091 if not q.empty():
Georg Brandld22b4662008-02-02 11:39:29 +000092 raise RuntimeError, "Call this function with an empty queue"
93 # I guess we better check things actually queue correctly a little :)
94 q.put(111)
95 q.put(333)
96 q.put(222)
97 target_order = dict(Queue = [111, 333, 222],
98 LifoQueue = [222, 333, 111],
99 PriorityQueue = [111, 222, 333])
100 actual_order = [q.get(), q.get(), q.get()]
Ezio Melotti2623a372010-11-21 13:34:58 +0000101 self.assertEqual(actual_order, target_order[q.__class__.__name__],
102 "Didn't seem to queue the correct data!")
Georg Brandld22b4662008-02-02 11:39:29 +0000103 for i in range(QUEUE_SIZE-1):
104 q.put(i)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000105 self.assertTrue(not q.empty(), "Queue should not be empty")
106 self.assertTrue(not q.full(), "Queue should not be full")
Ezio Melottidde5b942010-02-03 05:37:26 +0000107 last = 2 * QUEUE_SIZE
108 full = 3 * 2 * QUEUE_SIZE
109 q.put(last)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000110 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000111 try:
Ezio Melottidde5b942010-02-03 05:37:26 +0000112 q.put(full, block=0)
Georg Brandld22b4662008-02-02 11:39:29 +0000113 self.fail("Didn't appear to block with a full queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000114 except Queue.Full:
Georg Brandld22b4662008-02-02 11:39:29 +0000115 pass
116 try:
Ezio Melottidde5b942010-02-03 05:37:26 +0000117 q.put(full, timeout=0.01)
Georg Brandld22b4662008-02-02 11:39:29 +0000118 self.fail("Didn't appear to time-out with a full queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000119 except Queue.Full:
Georg Brandld22b4662008-02-02 11:39:29 +0000120 pass
121 # Test a blocking put
Ezio Melottidde5b942010-02-03 05:37:26 +0000122 self.do_blocking_test(q.put, (full,), q.get, ())
123 self.do_blocking_test(q.put, (full, True, 10), q.get, ())
Georg Brandld22b4662008-02-02 11:39:29 +0000124 # Empty it
125 for i in range(QUEUE_SIZE):
126 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000127 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000128 try:
129 q.get(block=0)
130 self.fail("Didn't appear to block with an empty queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000131 except Queue.Empty:
Georg Brandld22b4662008-02-02 11:39:29 +0000132 pass
133 try:
134 q.get(timeout=0.01)
135 self.fail("Didn't appear to time-out with an empty queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000136 except Queue.Empty:
Georg Brandld22b4662008-02-02 11:39:29 +0000137 pass
138 # Test a blocking get
139 self.do_blocking_test(q.get, (), q.put, ('empty',))
140 self.do_blocking_test(q.get, (True, 10), q.put, ('empty',))
141
142
143 def worker(self, q):
144 while True:
Georg Brandlcafb7102008-02-02 23:59:21 +0000145 x = q.get()
146 if x is None:
Georg Brandld22b4662008-02-02 11:39:29 +0000147 q.task_done()
148 return
Brett Cannon4b7deed2008-02-03 02:43:01 +0000149 with self.cumlock:
Georg Brandlcafb7102008-02-02 23:59:21 +0000150 self.cum += x
Georg Brandld22b4662008-02-02 11:39:29 +0000151 q.task_done()
152
153 def queue_join_test(self, q):
154 self.cum = 0
155 for i in (0,1):
156 threading.Thread(target=self.worker, args=(q,)).start()
157 for i in xrange(100):
158 q.put(i)
159 q.join()
Ezio Melotti2623a372010-11-21 13:34:58 +0000160 self.assertEqual(self.cum, sum(range(100)),
161 "q.join() did not block until all tasks were done")
Georg Brandld22b4662008-02-02 11:39:29 +0000162 for i in (0,1):
163 q.put(None) # instruct the threads to close
164 q.join() # verify that you can join twice
165
166 def test_queue_task_done(self):
167 # Test to make sure a queue task completed successfully.
168 q = self.type2test()
169 try:
170 q.task_done()
171 except ValueError:
172 pass
Tim Peters8d7626c2004-08-20 03:27:12 +0000173 else:
Georg Brandld22b4662008-02-02 11:39:29 +0000174 self.fail("Did not detect task count going negative")
175
176 def test_queue_join(self):
177 # Test that a queue join()s successfully, and before anything else
178 # (done twice for insurance).
179 q = self.type2test()
180 self.queue_join_test(q)
181 self.queue_join_test(q)
182 try:
183 q.task_done()
184 except ValueError:
185 pass
186 else:
187 self.fail("Did not detect task count going negative")
188
189 def test_simple_queue(self):
190 # Do it a couple of times on the same queue.
191 # Done twice to make sure works with same instance reused.
192 q = self.type2test(QUEUE_SIZE)
193 self.simple_queue_test(q)
194 self.simple_queue_test(q)
195
196
R David Murray5c2689a2012-03-17 16:44:16 -0400197class QueueTest(BaseQueueTest, unittest.TestCase):
Georg Brandla6168f92008-05-25 07:20:14 +0000198 type2test = Queue.Queue
Georg Brandld22b4662008-02-02 11:39:29 +0000199
R David Murray5c2689a2012-03-17 16:44:16 -0400200class LifoQueueTest(BaseQueueTest, unittest.TestCase):
Georg Brandla6168f92008-05-25 07:20:14 +0000201 type2test = Queue.LifoQueue
Georg Brandld22b4662008-02-02 11:39:29 +0000202
R David Murray5c2689a2012-03-17 16:44:16 -0400203class PriorityQueueTest(BaseQueueTest, unittest.TestCase):
Georg Brandla6168f92008-05-25 07:20:14 +0000204 type2test = Queue.PriorityQueue
Georg Brandld22b4662008-02-02 11:39:29 +0000205
206
Mark Hammond3b959db2002-04-19 00:11:32 +0000207
208# A Queue subclass that can provoke failure at a moment's notice :)
209class FailingQueueException(Exception):
210 pass
211
Georg Brandla6168f92008-05-25 07:20:14 +0000212class FailingQueue(Queue.Queue):
Mark Hammond3b959db2002-04-19 00:11:32 +0000213 def __init__(self, *args):
214 self.fail_next_put = False
215 self.fail_next_get = False
Georg Brandla6168f92008-05-25 07:20:14 +0000216 Queue.Queue.__init__(self, *args)
Mark Hammond3b959db2002-04-19 00:11:32 +0000217 def _put(self, item):
218 if self.fail_next_put:
219 self.fail_next_put = False
220 raise FailingQueueException, "You Lose"
Georg Brandla6168f92008-05-25 07:20:14 +0000221 return Queue.Queue._put(self, item)
Mark Hammond3b959db2002-04-19 00:11:32 +0000222 def _get(self):
223 if self.fail_next_get:
224 self.fail_next_get = False
225 raise FailingQueueException, "You Lose"
Georg Brandla6168f92008-05-25 07:20:14 +0000226 return Queue.Queue._get(self)
Mark Hammond3b959db2002-04-19 00:11:32 +0000227
Ezio Melottibe1632b2013-03-23 23:35:06 +0200228class FailingQueueTest(BlockingTestMixin, unittest.TestCase):
Mark Hammond3b959db2002-04-19 00:11:32 +0000229
Georg Brandld22b4662008-02-02 11:39:29 +0000230 def failing_queue_test(self, q):
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000231 if not q.empty():
Georg Brandld22b4662008-02-02 11:39:29 +0000232 raise RuntimeError, "Call this function with an empty queue"
233 for i in range(QUEUE_SIZE-1):
234 q.put(i)
235 # Test a failing non-blocking put.
236 q.fail_next_put = True
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000237 try:
Georg Brandld22b4662008-02-02 11:39:29 +0000238 q.put("oops", block=0)
239 self.fail("The queue didn't fail when it should have")
240 except FailingQueueException:
241 pass
242 q.fail_next_put = True
243 try:
244 q.put("oops", timeout=0.1)
245 self.fail("The queue didn't fail when it should have")
246 except FailingQueueException:
247 pass
248 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000249 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000250 # Test a failing blocking put
251 q.fail_next_put = True
252 try:
253 self.do_blocking_test(q.put, ("full",), q.get, ())
254 self.fail("The queue didn't fail when it should have")
255 except FailingQueueException:
256 pass
257 # Check the Queue isn't damaged.
258 # put failed, but get succeeded - re-add
259 q.put("last")
260 # Test a failing timeout put
261 q.fail_next_put = True
262 try:
263 self.do_exceptional_blocking_test(q.put, ("full", True, 10), q.get, (),
264 FailingQueueException)
265 self.fail("The queue didn't fail when it should have")
266 except FailingQueueException:
267 pass
268 # Check the Queue isn't damaged.
269 # put failed, but get succeeded - re-add
270 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000271 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000272 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000273 self.assertTrue(not q.full(), "Queue should not be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000274 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000275 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000276 # Test a blocking put
277 self.do_blocking_test(q.put, ("full",), q.get, ())
278 # Empty it
279 for i in range(QUEUE_SIZE):
280 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000281 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000282 q.put("first")
283 q.fail_next_get = True
284 try:
285 q.get()
286 self.fail("The queue didn't fail when it should have")
287 except FailingQueueException:
288 pass
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000289 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000290 q.fail_next_get = True
291 try:
292 q.get(timeout=0.1)
293 self.fail("The queue didn't fail when it should have")
294 except FailingQueueException:
295 pass
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000296 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000297 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000298 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000299 q.fail_next_get = True
300 try:
301 self.do_exceptional_blocking_test(q.get, (), q.put, ('empty',),
302 FailingQueueException)
303 self.fail("The queue didn't fail when it should have")
304 except FailingQueueException:
305 pass
306 # put succeeded, but get failed.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000307 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000308 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000309 self.assertTrue(q.empty(), "Queue should be empty")
Tim Peterse33901e2006-03-25 01:50:43 +0000310
Georg Brandld22b4662008-02-02 11:39:29 +0000311 def test_failing_queue(self):
312 # Test to make sure a queue is functioning correctly.
313 # Done twice to the same instance.
314 q = FailingQueue(QUEUE_SIZE)
315 self.failing_queue_test(q)
316 self.failing_queue_test(q)
Raymond Hettingerc4e94b92006-03-25 12:15:04 +0000317
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000318
Georg Brandld22b4662008-02-02 11:39:29 +0000319def test_main():
320 test_support.run_unittest(QueueTest, LifoQueueTest, PriorityQueueTest,
321 FailingQueueTest)
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000322
Raymond Hettinger9e1bc982008-01-16 23:40:45 +0000323
Georg Brandld22b4662008-02-02 11:39:29 +0000324if __name__ == "__main__":
325 test_main()