summaryrefslogtreecommitdiff
path: root/tests/integration/test_shutdown.py
blob: 063d779759246625ce7c90185752e3666de52fb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""That every thread the tracker starts also ends when it is asked to stop.

The rest of the suite stops a tracker too, so a thread left behind already
shows up in teardown as the complaint that the tracker had to be killed.
These ask for the arrangements that park a thread in a blocking read in the
first place, and they time the exit, because a reader woken only by its own
receive timeout passes a correctness check while taking a second over it.
"""

import time
import unittest

from harness import Tracker, TrackerTestCase

# Generous enough for a platform where shutting the socket down does not wake
# the reader and the receive timeout has to expire, tight enough that a reader
# which is never woken at all fails instead of hanging the suite
PATIENCE = 3.0


def time_the_exit(extra_config):
    tracker = Tracker(extra_config)
    try:
        # Serve one request of each kind first, so the threads that are about
        # to be joined have actually run
        tracker.announce(b"shutdown-probe\0\0\0\0\0\0")
        sock, connection_id = tracker.udp_connect()
        tracker.udp_announce(sock, connection_id, b"shutdown-probe\0\0\0\0\0\0")
        sock.close()
        started = time.time()
        complaints = tracker.stop()
        return time.time() - started, complaints
    finally:
        tracker._cleanup()


class UdpWorkers(TrackerTestCase):
    config = "listen.udp.workers 8\n"

    def test_announce_reaches_one_of_eight_workers(self):
        """Keeps the workers from being merely started and never used."""
        sock, connection_id = self.tracker.udp_connect()
        answer = self.tracker.udp_announce(sock, connection_id, self.hash_for("eight-workers"))
        sock.close()
        self.assertEqual(answer["seeders"], 1)


class ShutdownEndsEveryThread(unittest.TestCase):
    def test_eight_udp_workers_are_joined(self):
        took, complaints = time_the_exit("listen.udp.workers 8\n")
        self.assertEqual(complaints, [], "the tracker reported:\n" + "\n".join(complaints))
        self.assertLess(took, PATIENCE, "stopping eight udp workers took %.1f s" % took)

    def test_udp_in_the_event_loop_still_stops(self):
        """Worker count zero leaves no thread to join, which must also work."""
        took, complaints = time_the_exit("listen.udp.workers 0\n")
        self.assertEqual(complaints, [], "the tracker reported:\n" + "\n".join(complaints))
        self.assertLess(took, PATIENCE, "stopping without workers took %.1f s" % took)