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()