diff options
Diffstat (limited to 'tests/integration/test_shutdown.py')
| -rw-r--r-- | tests/integration/test_shutdown.py | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/tests/integration/test_shutdown.py b/tests/integration/test_shutdown.py new file mode 100644 index 0000000..063d779 --- /dev/null +++ b/tests/integration/test_shutdown.py | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | """That every thread the tracker starts also ends when it is asked to stop. | ||
| 2 | |||
| 3 | The rest of the suite stops a tracker too, so a thread left behind already | ||
| 4 | shows up in teardown as the complaint that the tracker had to be killed. | ||
| 5 | These ask for the arrangements that park a thread in a blocking read in the | ||
| 6 | first place, and they time the exit, because a reader woken only by its own | ||
| 7 | receive timeout passes a correctness check while taking a second over it. | ||
| 8 | """ | ||
| 9 | |||
| 10 | import time | ||
| 11 | import unittest | ||
| 12 | |||
| 13 | from harness import Tracker, TrackerTestCase | ||
| 14 | |||
| 15 | # Generous enough for a platform where shutting the socket down does not wake | ||
| 16 | # the reader and the receive timeout has to expire, tight enough that a reader | ||
| 17 | # which is never woken at all fails instead of hanging the suite | ||
| 18 | PATIENCE = 3.0 | ||
| 19 | |||
| 20 | |||
| 21 | def time_the_exit(extra_config): | ||
| 22 | tracker = Tracker(extra_config) | ||
| 23 | try: | ||
| 24 | # Serve one request of each kind first, so the threads that are about | ||
| 25 | # to be joined have actually run | ||
| 26 | tracker.announce(b"shutdown-probe\0\0\0\0\0\0") | ||
| 27 | sock, connection_id = tracker.udp_connect() | ||
| 28 | tracker.udp_announce(sock, connection_id, b"shutdown-probe\0\0\0\0\0\0") | ||
| 29 | sock.close() | ||
| 30 | started = time.time() | ||
| 31 | complaints = tracker.stop() | ||
| 32 | return time.time() - started, complaints | ||
| 33 | finally: | ||
| 34 | tracker._cleanup() | ||
| 35 | |||
| 36 | |||
| 37 | class UdpWorkers(TrackerTestCase): | ||
| 38 | config = "listen.udp.workers 8\n" | ||
| 39 | |||
| 40 | def test_announce_reaches_one_of_eight_workers(self): | ||
| 41 | """Keeps the workers from being merely started and never used.""" | ||
| 42 | sock, connection_id = self.tracker.udp_connect() | ||
| 43 | answer = self.tracker.udp_announce(sock, connection_id, self.hash_for("eight-workers")) | ||
| 44 | sock.close() | ||
| 45 | self.assertEqual(answer["seeders"], 1) | ||
| 46 | |||
| 47 | |||
| 48 | class ShutdownEndsEveryThread(unittest.TestCase): | ||
| 49 | def test_eight_udp_workers_are_joined(self): | ||
| 50 | took, complaints = time_the_exit("listen.udp.workers 8\n") | ||
| 51 | self.assertEqual(complaints, [], "the tracker reported:\n" + "\n".join(complaints)) | ||
| 52 | self.assertLess(took, PATIENCE, "stopping eight udp workers took %.1f s" % took) | ||
| 53 | |||
| 54 | def test_udp_in_the_event_loop_still_stops(self): | ||
| 55 | """Worker count zero leaves no thread to join, which must also work.""" | ||
| 56 | took, complaints = time_the_exit("listen.udp.workers 0\n") | ||
| 57 | self.assertEqual(complaints, [], "the tracker reported:\n" + "\n".join(complaints)) | ||
| 58 | self.assertLess(took, PATIENCE, "stopping without workers took %.1f s" % took) | ||
