summaryrefslogtreecommitdiff
path: root/tests/integration
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-10-28 23:31:07 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-10-28 23:31:07 +0100
commit772873104658182a5b76f5c0eed76047c62c6651 (patch)
tree431ba9c063fab10b4356cd8c7e63c9d2e957e2d9 /tests/integration
parentdd0ad26ea9824b75accced2b56328cbb222c4c6d (diff)
With more workers blockingly waiting on udp sockets, we need to keep track of all sockets to shut them down on exit
Diffstat (limited to 'tests/integration')
-rw-r--r--tests/integration/test_shutdown.py58
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
3The rest of the suite stops a tracker too, so a thread left behind already
4shows up in teardown as the complaint that the tracker had to be killed.
5These ask for the arrangements that park a thread in a blocking read in the
6first place, and they time the exit, because a reader woken only by its own
7receive timeout passes a correctness check while taking a second over it.
8"""
9
10import time
11import unittest
12
13from 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
18PATIENCE = 3.0
19
20
21def 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
37class 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
48class 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)