github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/third_party/stdlib/test/test_queue.py (about) 1 # Some simple queue module tests, plus some failure conditions 2 # to ensure the Queue locks remain stable. 3 import Queue 4 import time 5 import unittest 6 from test import test_support 7 #threading = test_support.import_module('threading') 8 import threading 9 10 QUEUE_SIZE = 5 11 12 # A thread to run a function that unclogs a blocked Queue. 13 class _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) 19 20 def run(self): 21 # 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) 30 self.startedEvent.set() 31 self.fn(*self.args) 32 33 34 # 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 45 class BlockingTestMixin(object): 46 47 def tearDown(self): 48 self.t = None 49 50 def do_blocking_test(self, block_func, block_args, trigger_func, trigger_args): 51 self.t = _TriggerThread(trigger_func, trigger_args) 52 self.t.start() 53 self.result = block_func(*block_args) 54 # If block_func returned before our thread made the call, we failed! 55 if not self.t.startedEvent.is_set(): 56 self.fail("blocking function '%r' appeared not to block" % 57 block_func) 58 self.t.join(10) # make sure the thread terminates 59 if self.t.is_alive(): 60 self.fail("trigger function '%r' appeared to not return" % 61 trigger_func) 62 return self.result 63 64 # Call this instead if block_func is supposed to raise an exception. 65 def do_exceptional_blocking_test(self,block_func, block_args, trigger_func, 66 trigger_args, expected_exception_class): 67 self.t = _TriggerThread(trigger_func, trigger_args) 68 self.t.start() 69 try: 70 try: 71 block_func(*block_args) 72 except expected_exception_class: 73 raise 74 else: 75 self.fail("expected exception of kind %r" % 76 expected_exception_class) 77 finally: 78 self.t.join(10) # make sure the thread terminates 79 if self.t.is_alive(): 80 self.fail("trigger function '%r' appeared to not return" % 81 trigger_func) 82 if not self.t.startedEvent.is_set(): 83 self.fail("trigger thread ended but event never set") 84 85 86 class BaseQueueTest(BlockingTestMixin): 87 def setUp(self): 88 self.cum = 0 89 self.cumlock = threading.Lock() 90 91 def simple_queue_test(self, q): 92 if not q.empty(): 93 raise RuntimeError, "Call this function with an empty queue" 94 # I guess we better check things actually queue correctly a little :) 95 q.put(111) 96 q.put(333) 97 q.put(222) 98 target_order = dict(Queue = [111, 333, 222], 99 LifoQueue = [222, 333, 111], 100 PriorityQueue = [111, 222, 333]) 101 actual_order = [q.get(), q.get(), q.get()] 102 self.assertEqual(actual_order, target_order[q.__class__.__name__], 103 "Didn't seem to queue the correct data!") 104 for i in range(QUEUE_SIZE-1): 105 q.put(i) 106 self.assertTrue(not q.empty(), "Queue should not be empty") 107 self.assertTrue(not q.full(), "Queue should not be full") 108 last = 2 * QUEUE_SIZE 109 full = 3 * 2 * QUEUE_SIZE 110 q.put(last) 111 self.assertTrue(q.full(), "Queue should be full") 112 try: 113 q.put(full, block=0) 114 self.fail("Didn't appear to block with a full queue") 115 except Queue.Full: 116 pass 117 try: 118 q.put(full, timeout=0.01) 119 self.fail("Didn't appear to time-out with a full queue") 120 except Queue.Full: 121 pass 122 # Test a blocking put 123 self.do_blocking_test(q.put, (full,), q.get, ()) 124 self.do_blocking_test(q.put, (full, True, 10), q.get, ()) 125 # Empty it 126 for i in range(QUEUE_SIZE): 127 q.get() 128 self.assertTrue(q.empty(), "Queue should be empty") 129 try: 130 q.get(block=0) 131 self.fail("Didn't appear to block with an empty queue") 132 except Queue.Empty: 133 pass 134 try: 135 q.get(timeout=0.01) 136 self.fail("Didn't appear to time-out with an empty queue") 137 except Queue.Empty: 138 pass 139 # Test a blocking get 140 self.do_blocking_test(q.get, (), q.put, ('empty',)) 141 self.do_blocking_test(q.get, (True, 10), q.put, ('empty',)) 142 143 144 def worker(self, q): 145 while True: 146 x = q.get() 147 if x is None: 148 q.task_done() 149 return 150 with self.cumlock: 151 self.cum += x 152 q.task_done() 153 154 def queue_join_test(self, q): 155 self.cum = 0 156 for i in (0,1): 157 threading.Thread(target=self.worker, args=(q,)).start() 158 for i in xrange(100): 159 q.put(i) 160 q.join() 161 self.assertEqual(self.cum, sum(range(100)), 162 "q.join() did not block until all tasks were done") 163 for i in (0,1): 164 q.put(None) # instruct the threads to close 165 q.join() # verify that you can join twice 166 167 def test_queue_task_done(self): 168 # Test to make sure a queue task completed successfully. 169 q = self.type2test() 170 try: 171 q.task_done() 172 except ValueError: 173 pass 174 else: 175 self.fail("Did not detect task count going negative") 176 177 def test_queue_join(self): 178 # Test that a queue join()s successfully, and before anything else 179 # (done twice for insurance). 180 q = self.type2test() 181 self.queue_join_test(q) 182 self.queue_join_test(q) 183 try: 184 q.task_done() 185 except ValueError: 186 pass 187 else: 188 self.fail("Did not detect task count going negative") 189 190 def test_simple_queue(self): 191 # Do it a couple of times on the same queue. 192 # Done twice to make sure works with same instance reused. 193 q = self.type2test(QUEUE_SIZE) 194 self.simple_queue_test(q) 195 self.simple_queue_test(q) 196 197 198 class QueueTest(BaseQueueTest, unittest.TestCase): 199 type2test = Queue.Queue 200 201 class LifoQueueTest(BaseQueueTest, unittest.TestCase): 202 type2test = Queue.LifoQueue 203 204 class PriorityQueueTest(BaseQueueTest, unittest.TestCase): 205 type2test = Queue.PriorityQueue 206 207 208 209 # A Queue subclass that can provoke failure at a moment's notice :) 210 class FailingQueueException(Exception): 211 pass 212 213 class FailingQueue(Queue.Queue): 214 def __init__(self, *args): 215 self.fail_next_put = False 216 self.fail_next_get = False 217 Queue.Queue.__init__(self, *args) 218 def _put(self, item): 219 if self.fail_next_put: 220 self.fail_next_put = False 221 raise FailingQueueException, "You Lose" 222 return Queue.Queue._put(self, item) 223 def _get(self): 224 if self.fail_next_get: 225 self.fail_next_get = False 226 raise FailingQueueException, "You Lose" 227 return Queue.Queue._get(self) 228 229 class FailingQueueTest(BlockingTestMixin, unittest.TestCase): 230 231 def failing_queue_test(self, q): 232 if not q.empty(): 233 raise RuntimeError, "Call this function with an empty queue" 234 for i in range(QUEUE_SIZE-1): 235 q.put(i) 236 # Test a failing non-blocking put. 237 q.fail_next_put = True 238 try: 239 q.put("oops", block=0) 240 self.fail("The queue didn't fail when it should have") 241 except FailingQueueException: 242 pass 243 q.fail_next_put = True 244 try: 245 q.put("oops", timeout=0.1) 246 self.fail("The queue didn't fail when it should have") 247 except FailingQueueException: 248 pass 249 q.put("last") 250 self.assertTrue(q.full(), "Queue should be full") 251 # Test a failing blocking put 252 q.fail_next_put = True 253 try: 254 self.do_blocking_test(q.put, ("full",), q.get, ()) 255 self.fail("The queue didn't fail when it should have") 256 except FailingQueueException: 257 pass 258 # Check the Queue isn't damaged. 259 # put failed, but get succeeded - re-add 260 q.put("last") 261 # Test a failing timeout put 262 q.fail_next_put = True 263 try: 264 self.do_exceptional_blocking_test(q.put, ("full", True, 10), q.get, (), 265 FailingQueueException) 266 self.fail("The queue didn't fail when it should have") 267 except FailingQueueException: 268 pass 269 # Check the Queue isn't damaged. 270 # put failed, but get succeeded - re-add 271 q.put("last") 272 self.assertTrue(q.full(), "Queue should be full") 273 q.get() 274 self.assertTrue(not q.full(), "Queue should not be full") 275 q.put("last") 276 self.assertTrue(q.full(), "Queue should be full") 277 # Test a blocking put 278 self.do_blocking_test(q.put, ("full",), q.get, ()) 279 # Empty it 280 for i in range(QUEUE_SIZE): 281 q.get() 282 self.assertTrue(q.empty(), "Queue should be empty") 283 q.put("first") 284 q.fail_next_get = True 285 try: 286 q.get() 287 self.fail("The queue didn't fail when it should have") 288 except FailingQueueException: 289 pass 290 self.assertTrue(not q.empty(), "Queue should not be empty") 291 q.fail_next_get = True 292 try: 293 q.get(timeout=0.1) 294 self.fail("The queue didn't fail when it should have") 295 except FailingQueueException: 296 pass 297 self.assertTrue(not q.empty(), "Queue should not be empty") 298 q.get() 299 self.assertTrue(q.empty(), "Queue should be empty") 300 q.fail_next_get = True 301 try: 302 self.do_exceptional_blocking_test(q.get, (), q.put, ('empty',), 303 FailingQueueException) 304 self.fail("The queue didn't fail when it should have") 305 except FailingQueueException: 306 pass 307 # put succeeded, but get failed. 308 self.assertTrue(not q.empty(), "Queue should not be empty") 309 q.get() 310 self.assertTrue(q.empty(), "Queue should be empty") 311 312 def test_failing_queue(self): 313 # Test to make sure a queue is functioning correctly. 314 # Done twice to the same instance. 315 q = FailingQueue(QUEUE_SIZE) 316 self.failing_queue_test(q) 317 self.failing_queue_test(q) 318 319 320 def test_main(): 321 test_support.run_unittest(QueueTest, LifoQueueTest, PriorityQueueTest, 322 FailingQueueTest) 323 324 325 if __name__ == "__main__": 326 test_main()