|
| 1 | +import threading |
| 2 | +import unittest |
| 3 | +from concurrent import futures |
| 4 | +from unittest import mock |
| 5 | + |
| 6 | +from somacore.query import _eager_iter |
| 7 | + |
| 8 | + |
| 9 | +class EagerIterTest(unittest.TestCase): |
| 10 | + def setUp(self): |
| 11 | + super().setUp() |
| 12 | + self.kiddie_pool = futures.ThreadPoolExecutor(1) |
| 13 | + """Tiny thread pool for testing.""" |
| 14 | + self.verify_pool = futures.ThreadPoolExecutor(1) |
| 15 | + """Separate thread pool so verification is not blocked.""" |
| 16 | + |
| 17 | + def tearDown(self): |
| 18 | + self.verify_pool.shutdown(wait=False) |
| 19 | + self.kiddie_pool.shutdown(wait=False) |
| 20 | + super().tearDown() |
| 21 | + |
| 22 | + def test_thread_starvation(self): |
| 23 | + sem = threading.Semaphore() |
| 24 | + try: |
| 25 | + # Monopolize the threadpool. |
| 26 | + sem.acquire() |
| 27 | + self.kiddie_pool.submit(sem.acquire) |
| 28 | + eager = _eager_iter.EagerIterator(iter("abc"), pool=self.kiddie_pool) |
| 29 | + got_a = self.verify_pool.submit(lambda: next(eager)) |
| 30 | + self.assertEqual("a", got_a.result(0.1)) |
| 31 | + got_b = self.verify_pool.submit(lambda: next(eager)) |
| 32 | + self.assertEqual("b", got_b.result(0.1)) |
| 33 | + got_c = self.verify_pool.submit(lambda: next(eager)) |
| 34 | + self.assertEqual("c", got_c.result(0.1)) |
| 35 | + with self.assertRaises(StopIteration): |
| 36 | + self.verify_pool.submit(lambda: next(eager)).result(0.1) |
| 37 | + finally: |
| 38 | + sem.release() |
| 39 | + |
| 40 | + def test_nesting(self): |
| 41 | + inner = _eager_iter.EagerIterator(iter("abc"), pool=self.kiddie_pool) |
| 42 | + outer = _eager_iter.EagerIterator(inner, pool=self.kiddie_pool) |
| 43 | + self.assertEqual( |
| 44 | + "a, b, c", self.verify_pool.submit(", ".join, outer).result(0.1) |
| 45 | + ) |
| 46 | + |
| 47 | + def test_exceptions(self): |
| 48 | + flaky = mock.MagicMock() |
| 49 | + flaky.__next__.side_effect = [1, 2, ValueError(), 3, 4] |
| 50 | + |
| 51 | + eager_flaky = _eager_iter.EagerIterator(flaky, pool=self.kiddie_pool) |
| 52 | + got_1 = self.verify_pool.submit(lambda: next(eager_flaky)) |
| 53 | + self.assertEqual(1, got_1.result(0.1)) |
| 54 | + got_2 = self.verify_pool.submit(lambda: next(eager_flaky)) |
| 55 | + self.assertEqual(2, got_2.result(0.1)) |
| 56 | + with self.assertRaises(ValueError): |
| 57 | + self.verify_pool.submit(lambda: next(eager_flaky)).result(0.1) |
| 58 | + got_3 = self.verify_pool.submit(lambda: next(eager_flaky)) |
| 59 | + self.assertEqual(3, got_3.result(0.1)) |
| 60 | + got_4 = self.verify_pool.submit(lambda: next(eager_flaky)) |
| 61 | + self.assertEqual(4, got_4.result(0.1)) |
| 62 | + for _ in range(5): |
| 63 | + with self.assertRaises(StopIteration): |
| 64 | + self.verify_pool.submit(lambda: next(eager_flaky)).result(0.1) |
0 commit comments