blob: 715466f7259437dc55a22b23e9419ffa983a16de [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 Kumarance8e33a2010-01-08 19:04:16 +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!
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000052 if not self.t.startedEvent.is_set():
Georg Brandld22b4662008-02-02 11:39:29 +000053 self.fail("blocking function '%r' appeared not to block" %
54 block_func)
55 self.t.join(10) # make sure the thread terminates
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000056 if self.t.is_alive():
Georg Brandld22b4662008-02-02 11:39:29 +000057 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
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000076 if self.t.is_alive():
Georg Brandld22b4662008-02-02 11:39:29 +000077 self.fail("trigger function '%r' appeared to not return" %
78 trigger_func)
Benjamin Peterson0fbcf692008-06-11 17:27:50 +000079 if not self.t.startedEvent.is_set():
Georg Brandld22b4662008-02-02 11:39:29 +000080 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)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000103 self.assertTrue(not q.empty(), "Queue should not be empty")
104 self.assertTrue(not q.full(), "Queue should not be full")
Ezio Melottidde5b942010-02-03 05:37:26 +0000105 last = 2 * QUEUE_SIZE
106 full = 3 * 2 * QUEUE_SIZE
107 q.put(last)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000108 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000109 try:
Ezio Melottidde5b942010-02-03 05:37:26 +0000110 q.put(full, block=0)
Georg Brandld22b4662008-02-02 11:39:29 +0000111 self.fail("Didn't appear to block with a full queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000112 except Queue.Full:
Georg Brandld22b4662008-02-02 11:39:29 +0000113 pass
114 try:
Ezio Melottidde5b942010-02-03 05:37:26 +0000115 q.put(full, timeout=0.01)
Georg Brandld22b4662008-02-02 11:39:29 +0000116 self.fail("Didn't appear to time-out with a full queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000117 except Queue.Full:
Georg Brandld22b4662008-02-02 11:39:29 +0000118 pass
119 # Test a blocking put
Ezio Melottidde5b942010-02-03 05:37:26 +0000120 self.do_blocking_test(q.put, (full,), q.get, ())
121 self.do_blocking_test(q.put, (full, True, 10), q.get, ())
Georg Brandld22b4662008-02-02 11:39:29 +0000122 # Empty it
123 for i in range(QUEUE_SIZE):
124 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000125 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000126 try:
127 q.get(block=0)
128 self.fail("Didn't appear to block with an empty queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000129 except Queue.Empty:
Georg Brandld22b4662008-02-02 11:39:29 +0000130 pass
131 try:
132 q.get(timeout=0.01)
133 self.fail("Didn't appear to time-out with an empty queue")
Georg Brandla6168f92008-05-25 07:20:14 +0000134 except Queue.Empty:
Georg Brandld22b4662008-02-02 11:39:29 +0000135 pass
136 # Test a blocking get
137 self.do_blocking_test(q.get, (), q.put, ('empty',))
138 self.do_blocking_test(q.get, (True, 10), q.put, ('empty',))
139
140
141 def worker(self, q):
142 while True:
Georg Brandlcafb7102008-02-02 23:59:21 +0000143 x = q.get()
144 if x is None:
Georg Brandld22b4662008-02-02 11:39:29 +0000145 q.task_done()
146 return
Brett Cannon4b7deed2008-02-03 02:43:01 +0000147 with self.cumlock:
Georg Brandlcafb7102008-02-02 23:59:21 +0000148 self.cum += x
Georg Brandld22b4662008-02-02 11:39:29 +0000149 q.task_done()
150
151 def queue_join_test(self, q):
152 self.cum = 0
153 for i in (0,1):
154 threading.Thread(target=self.worker, args=(q,)).start()
155 for i in xrange(100):
156 q.put(i)
157 q.join()
158 self.assertEquals(self.cum, sum(range(100)),
Georg Brandlcafb7102008-02-02 23:59:21 +0000159 "q.join() did not block until all tasks were done")
Georg Brandld22b4662008-02-02 11:39:29 +0000160 for i in (0,1):
161 q.put(None) # instruct the threads to close
162 q.join() # verify that you can join twice
163
164 def test_queue_task_done(self):
165 # Test to make sure a queue task completed successfully.
166 q = self.type2test()
167 try:
168 q.task_done()
169 except ValueError:
170 pass
Tim Peters8d7626c2004-08-20 03:27:12 +0000171 else:
Georg Brandld22b4662008-02-02 11:39:29 +0000172 self.fail("Did not detect task count going negative")
173
174 def test_queue_join(self):
175 # Test that a queue join()s successfully, and before anything else
176 # (done twice for insurance).
177 q = self.type2test()
178 self.queue_join_test(q)
179 self.queue_join_test(q)
180 try:
181 q.task_done()
182 except ValueError:
183 pass
184 else:
185 self.fail("Did not detect task count going negative")
186
187 def test_simple_queue(self):
188 # Do it a couple of times on the same queue.
189 # Done twice to make sure works with same instance reused.
190 q = self.type2test(QUEUE_SIZE)
191 self.simple_queue_test(q)
192 self.simple_queue_test(q)
193
194
195class QueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000196 type2test = Queue.Queue
Georg Brandld22b4662008-02-02 11:39:29 +0000197
198class LifoQueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000199 type2test = Queue.LifoQueue
Georg Brandld22b4662008-02-02 11:39:29 +0000200
201class PriorityQueueTest(BaseQueueTest):
Georg Brandla6168f92008-05-25 07:20:14 +0000202 type2test = Queue.PriorityQueue
Georg Brandld22b4662008-02-02 11:39:29 +0000203
204
Mark Hammond3b959db2002-04-19 00:11:32 +0000205
206# A Queue subclass that can provoke failure at a moment's notice :)
207class FailingQueueException(Exception):
208 pass
209
Georg Brandla6168f92008-05-25 07:20:14 +0000210class FailingQueue(Queue.Queue):
Mark Hammond3b959db2002-04-19 00:11:32 +0000211 def __init__(self, *args):
212 self.fail_next_put = False
213 self.fail_next_get = False
Georg Brandla6168f92008-05-25 07:20:14 +0000214 Queue.Queue.__init__(self, *args)
Mark Hammond3b959db2002-04-19 00:11:32 +0000215 def _put(self, item):
216 if self.fail_next_put:
217 self.fail_next_put = False
218 raise FailingQueueException, "You Lose"
Georg Brandla6168f92008-05-25 07:20:14 +0000219 return Queue.Queue._put(self, item)
Mark Hammond3b959db2002-04-19 00:11:32 +0000220 def _get(self):
221 if self.fail_next_get:
222 self.fail_next_get = False
223 raise FailingQueueException, "You Lose"
Georg Brandla6168f92008-05-25 07:20:14 +0000224 return Queue.Queue._get(self)
Mark Hammond3b959db2002-04-19 00:11:32 +0000225
Georg Brandld22b4662008-02-02 11:39:29 +0000226class FailingQueueTest(unittest.TestCase, BlockingTestMixin):
Mark Hammond3b959db2002-04-19 00:11:32 +0000227
Georg Brandld22b4662008-02-02 11:39:29 +0000228 def failing_queue_test(self, q):
Georg Brandl3c3d9ac2008-02-06 23:45:51 +0000229 if not q.empty():
Georg Brandld22b4662008-02-02 11:39:29 +0000230 raise RuntimeError, "Call this function with an empty queue"
231 for i in range(QUEUE_SIZE-1):
232 q.put(i)
233 # Test a failing non-blocking put.
234 q.fail_next_put = True
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000235 try:
Georg Brandld22b4662008-02-02 11:39:29 +0000236 q.put("oops", block=0)
237 self.fail("The queue didn't fail when it should have")
238 except FailingQueueException:
239 pass
240 q.fail_next_put = True
241 try:
242 q.put("oops", timeout=0.1)
243 self.fail("The queue didn't fail when it should have")
244 except FailingQueueException:
245 pass
246 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000247 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000248 # Test a failing blocking put
249 q.fail_next_put = True
250 try:
251 self.do_blocking_test(q.put, ("full",), q.get, ())
252 self.fail("The queue didn't fail when it should have")
253 except FailingQueueException:
254 pass
255 # Check the Queue isn't damaged.
256 # put failed, but get succeeded - re-add
257 q.put("last")
258 # Test a failing timeout put
259 q.fail_next_put = True
260 try:
261 self.do_exceptional_blocking_test(q.put, ("full", True, 10), q.get, (),
262 FailingQueueException)
263 self.fail("The queue didn't fail when it should have")
264 except FailingQueueException:
265 pass
266 # Check the Queue isn't damaged.
267 # put failed, but get succeeded - re-add
268 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000269 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000270 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000271 self.assertTrue(not q.full(), "Queue should not be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000272 q.put("last")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000273 self.assertTrue(q.full(), "Queue should be full")
Georg Brandld22b4662008-02-02 11:39:29 +0000274 # Test a blocking put
275 self.do_blocking_test(q.put, ("full",), q.get, ())
276 # Empty it
277 for i in range(QUEUE_SIZE):
278 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000279 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000280 q.put("first")
281 q.fail_next_get = True
282 try:
283 q.get()
284 self.fail("The queue didn't fail when it should have")
285 except FailingQueueException:
286 pass
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000287 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000288 q.fail_next_get = True
289 try:
290 q.get(timeout=0.1)
291 self.fail("The queue didn't fail when it should have")
292 except FailingQueueException:
293 pass
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000294 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000295 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000296 self.assertTrue(q.empty(), "Queue should be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000297 q.fail_next_get = True
298 try:
299 self.do_exceptional_blocking_test(q.get, (), q.put, ('empty',),
300 FailingQueueException)
301 self.fail("The queue didn't fail when it should have")
302 except FailingQueueException:
303 pass
304 # put succeeded, but get failed.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000305 self.assertTrue(not q.empty(), "Queue should not be empty")
Georg Brandld22b4662008-02-02 11:39:29 +0000306 q.get()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000307 self.assertTrue(q.empty(), "Queue should be empty")
Tim Peterse33901e2006-03-25 01:50:43 +0000308
Georg Brandld22b4662008-02-02 11:39:29 +0000309 def test_failing_queue(self):
310 # Test to make sure a queue is functioning correctly.
311 # Done twice to the same instance.
312 q = FailingQueue(QUEUE_SIZE)
313 self.failing_queue_test(q)
314 self.failing_queue_test(q)
Raymond Hettingerc4e94b92006-03-25 12:15:04 +0000315
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000316
Georg Brandld22b4662008-02-02 11:39:29 +0000317def test_main():
318 test_support.run_unittest(QueueTest, LifoQueueTest, PriorityQueueTest,
319 FailingQueueTest)
Raymond Hettingerfd3fcf02006-03-24 20:43:29 +0000320
Raymond Hettinger9e1bc982008-01-16 23:40:45 +0000321
Georg Brandld22b4662008-02-02 11:39:29 +0000322if __name__ == "__main__":
323 test_main()