summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/integration/test_shutdown.py58
-rw-r--r--tests/t_socket.c40
2 files changed, 98 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)
diff --git a/tests/t_socket.c b/tests/t_socket.c
index 8eef7fd..ede8ae0 100644
--- a/tests/t_socket.c
+++ b/tests/t_socket.c
@@ -10,6 +10,7 @@
10#include <poll.h> 10#include <poll.h>
11#include <string.h> 11#include <string.h>
12#include <sys/socket.h> 12#include <sys/socket.h>
13#include <time.h>
13#include <unistd.h> 14#include <unistd.h>
14 15
15/* Opentracker */ 16/* Opentracker */
@@ -144,6 +145,44 @@ static void case_udp_roundtrip(void) {
144 close(s); 145 close(s);
145} 146}
146 147
148/* udp_workers_deinit relies on a worker parked in a read coming back. The
149 receive timeout is what guarantees that; shutting the socket down is what
150 makes it prompt where the platform allows it. Neither is uniform across the
151 systems opentracker runs on, so both are checked rather than assumed. */
152static void case_blocking_read_comes_back(void) {
153 char buf[32], ip[16], own_ip[16];
154 uint16_t port = 0, own_port = 0;
155 time_t started;
156 int s;
157
158 test_case("a receive timeout does not delay a datagram that is already there");
159 s = socket_udp_bind(ip_loopback, 0, SOCKET_EXCLUSIVE);
160 CHECK(s != -1);
161 CHECK_INT(socket_set_blocking(s, 1), 0);
162 CHECK_INT(socket_set_timeout(s, 1), 0);
163 bound_address(s, own_ip, &own_port);
164 CHECK_INT(socket_send(s, "opentracker", 11, own_ip, own_port), 11);
165 CHECK_INT(socket_recv(s, buf, sizeof(buf), ip, &port), 11);
166
167 test_case("and ends a read that would otherwise never return");
168 started = time(NULL);
169 CHECK_INT(socket_recv(s, buf, sizeof(buf), ip, &port), -1);
170 CHECK(errno == EAGAIN || errno == EWOULDBLOCK);
171 CHECK(time(NULL) - started <= 3);
172 close(s);
173
174 test_case("shutting the socket down ends one too");
175 s = socket_udp_bind(ip_loopback, 0, SOCKET_EXCLUSIVE);
176 CHECK(s != -1);
177 CHECK_INT(socket_set_blocking(s, 1), 0);
178 CHECK_INT(socket_set_timeout(s, 1), 0);
179 shutdown(s, SHUT_RD);
180 started = time(NULL);
181 CHECK(socket_recv(s, buf, sizeof(buf), ip, &port) <= 0);
182 CHECK(time(NULL) - started <= 3);
183 close(s);
184}
185
147/* An IPv4 peer must arrive in the v4 mapped form opentracker stores, however 186/* An IPv4 peer must arrive in the v4 mapped form opentracker stores, however
148 the kernel chose to report it. Linux fills a v4 mapped sockaddr_in6 here 187 the kernel chose to report it. Linux fills a v4 mapped sockaddr_in6 here
149 and the BSDs fill a plain sockaddr_in, so this is the case that tells the 188 and the BSDs fill a plain sockaddr_in, so this is the case that tells the
@@ -293,6 +332,7 @@ void test_socket(void) {
293 case_sharing_is_opt_in(); 332 case_sharing_is_opt_in();
294#endif 333#endif
295 case_udp_roundtrip(); 334 case_udp_roundtrip();
335 case_blocking_read_comes_back();
296 case_udp_v4mapped(); 336 case_udp_v4mapped();
297 case_tcp_roundtrip(); 337 case_tcp_roundtrip();
298 case_connect_refused(); 338 case_connect_refused();