summaryrefslogtreecommitdiff
path: root/tests/integration
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integration')
-rw-r--r--tests/integration/harness.py4
-rw-r--r--tests/integration/test_config.py26
-rw-r--r--tests/integration/test_livesync.py108
3 files changed, 133 insertions, 5 deletions
diff --git a/tests/integration/harness.py b/tests/integration/harness.py
index a9d38ca..10aa6de 100644
--- a/tests/integration/harness.py
+++ b/tests/integration/harness.py
@@ -192,7 +192,7 @@ class Tracker:
192 def torrent_count(self): 192 def torrent_count(self):
193 return int(self.stats("torr").split()[0]) 193 return int(self.stats("torr").split()[0])
194 194
195 def livesync_send(self, payload): 195 def livesync_send(self, payload, group=LIVESYNC_GROUP):
196 """Put a packet on the live sync group. 196 """Put a packet on the live sync group.
197 197
198 It has to go to the group rather than straight at the port: the 198 It has to go to the group rather than straight at the port: the
@@ -204,7 +204,7 @@ class Tracker:
204 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton("127.0.0.1")) 204 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton("127.0.0.1"))
205 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1) 205 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
206 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1) 206 sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
207 sock.sendto(payload, (LIVESYNC_GROUP, self.livesync_port)) 207 sock.sendto(payload, (group, self.livesync_port))
208 finally: 208 finally:
209 sock.close() 209 sock.close()
210 210
diff --git a/tests/integration/test_config.py b/tests/integration/test_config.py
index 1a84fa7..2f6bdc4 100644
--- a/tests/integration/test_config.py
+++ b/tests/integration/test_config.py
@@ -1,7 +1,9 @@
1"""Settings the tracker has to accept, and still work with. 1"""Settings the tracker has to accept, and still work with.
2 2
3An unrecognised key makes opentracker refuse to start, so a test that it 3An unrecognised key only warns, so coming up proves nothing on its own and a
4comes up at all is a test that the key was understood. 4test that cares has to look at what the tracker said. A key that is
5recognised but cannot be parsed is another matter, and for the listen
6statements it stops the tracker.
5""" 7"""
6 8
7import unittest 9import unittest
@@ -14,6 +16,7 @@ class TcpWorkers(TrackerTestCase):
14 16
15 def test_the_tracker_serves_with_tcp_workers_asked_for(self): 17 def test_the_tracker_serves_with_tcp_workers_asked_for(self):
16 self.assertEqual(self.tracker.announce(self.hash_for("tcpworkers"))[b"complete"], 1) 18 self.assertEqual(self.tracker.announce(self.hash_for("tcpworkers"))[b"complete"], 1)
19 self.assertNotIn("Unhandled line", self.tracker.output())
17 20
18 21
19class WorkersOnAListenStatement(unittest.TestCase): 22class WorkersOnAListenStatement(unittest.TestCase):
@@ -27,3 +30,22 @@ class WorkersOnAListenStatement(unittest.TestCase):
27 self.assertEqual(complaints, [], "the tracker reported:\n" + "\n".join(complaints)) 30 self.assertEqual(complaints, [], "the tracker reported:\n" + "\n".join(complaints))
28 finally: 31 finally:
29 tracker._cleanup() 32 tracker._cleanup()
33
34
35class AListenLineThatDoesNotParse(unittest.TestCase):
36 """Warning and carrying on left a tracker that had been told where to
37 listen coming up on its default instead, which is every interface."""
38
39 def refuses(self, line):
40 with self.assertRaises(AssertionError) as caught:
41 Tracker(line)
42 return str(caught.exception)
43
44 def test_an_address_that_is_not_one_stops_the_tracker(self):
45 self.assertIn("want an address", self.refuses("listen.tcp 10.0.0.300:7999\n"))
46
47 def test_so_does_a_port_out_of_range(self):
48 self.assertIn("want an address", self.refuses("listen.udp 127.0.0.1:99999\n"))
49
50 def test_so_does_an_option_it_does_not_know(self):
51 self.assertIn("want an address", self.refuses("listen.tcp 127.0.0.1:7999 wokers=2\n"))
diff --git a/tests/integration/test_livesync.py b/tests/integration/test_livesync.py
index 8dd754b..f4cb2db 100644
--- a/tests/integration/test_livesync.py
+++ b/tests/integration/test_livesync.py
@@ -10,7 +10,7 @@ import struct
10import subprocess 10import subprocess
11import unittest 11import unittest
12 12
13from harness import TRACKER, TrackerTestCase 13from harness import TRACKER, Tracker, TrackerTestCase
14 14
15# enum { OT_SYNC_PEER4, OT_SYNC_PEER6 } in src/ot_livesync.c 15# enum { OT_SYNC_PEER4, OT_SYNC_PEER6 } in src/ot_livesync.c
16SYNC_PEER4 = 0 16SYNC_PEER4 = 0
@@ -102,6 +102,17 @@ class LiveSyncIn(TrackerTestCase):
102 self.tracker.livesync_send(packet(99, [(info_hash, peer4("10.1.2.11", 6881, FLAG_SEEDING))])) 102 self.tracker.livesync_send(packet(99, [(info_hash, peer4("10.1.2.11", 6881, FLAG_SEEDING))]))
103 self.assertFalse(self.tracker.wait_until(lambda: self.counts(info_hash) is not None, timeout=1)) 103 self.assertFalse(self.tracker.wait_until(lambda: self.counts(info_hash) is not None, timeout=1))
104 104
105 def test_synced_peers_are_counted(self):
106 """The live sync worker keeps its own tally like every other worker,
107 and ?mode=syncs is the sum of them."""
108 before = int(self.tracker.stats("syncs").split()[0])
109 hashes = [self.hash_for("sync-count-%d" % i) for i in range(3)]
110 self.tracker.livesync_send(packet(SYNC_PEER4, [(h, peer4("10.1.2.%d" % (20 + i), 6881, FLAG_SEEDING))
111 for i, h in enumerate(hashes)]))
112 self.assertTrue(self.tracker.wait_until(
113 lambda: int(self.tracker.stats("syncs").split()[0]) == before + 3),
114 "syncs says %s" % self.tracker.stats("syncs").split()[0])
115
105 def test_a_packet_with_nothing_but_a_header_is_ignored(self): 116 def test_a_packet_with_nothing_but_a_header_is_ignored(self):
106 before = self.tracker.torrent_count() 117 before = self.tracker.torrent_count()
107 self.tracker.livesync_send(struct.pack("!I", 0xDEADBEEF) + struct.pack("!I", SYNC_PEER4)) 118 self.tracker.livesync_send(struct.pack("!I", 0xDEADBEEF) + struct.pack("!I", SYNC_PEER4))
@@ -119,3 +130,98 @@ class LiveSyncAccess(TrackerTestCase):
119 info_hash = self.hash_for("sync-unblessed") 130 info_hash = self.hash_for("sync-unblessed")
120 self.tracker.livesync_send(packet(SYNC_PEER4, [(info_hash, peer4("10.1.2.12", 6881, FLAG_SEEDING))])) 131 self.tracker.livesync_send(packet(SYNC_PEER4, [(info_hash, peer4("10.1.2.12", 6881, FLAG_SEEDING))]))
121 self.assertFalse(self.tracker.wait_until(lambda: self.tracker.torrent_count() != 0, timeout=1)) 132 self.assertFalse(self.tracker.wait_until(lambda: self.tracker.torrent_count() != 0, timeout=1))
133
134
135@unittest.skipUnless(has_livesync(), "this build has no WANT_SYNC_LIVE")
136class LiveSyncUnconfigured(TrackerTestCase):
137 """A binary that has the feature but no cluster to join. Somebody whose
138 packager turned WANT_SYNC_LIVE on did not ask for live sync and should
139 not be able to tell that it is there."""
140
141 config = ""
142
143 def test_the_tracker_runs_anyway(self):
144 info_hash = self.hash_for("no-cluster")
145 self.tracker.announce(info_hash, port=6881, left=0)
146 self.assertEqual(self.tracker.torrent_count(), 1)
147
148 def test_nothing_is_said_about_the_missing_cluster(self):
149 self.assertNotIn("sync", self.tracker.output().lower())
150
151 def test_no_socket_is_bound_for_it(self):
152 """A configured tracker holds the wildcard address on that port, so
153 taking it here proves nothing was started."""
154 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
155 try:
156 sock.bind(("0.0.0.0", self.tracker.livesync_port))
157 finally:
158 sock.close()
159
160 def test_announcing_stays_clean(self):
161 """Enough announces to pass the watermark that would flush an
162 outgoing buffer, if there were one to flush. The sanitizers in this
163 build turn anything the attempt touched into a failure."""
164 for i in range(200):
165 self.tracker.announce(self.hash_for("no-cluster-%d" % i), port=6881 + i, left=0)
166 self.assertEqual(self.tracker.torrent_count(), 200)
167
168
169# RFC 2365 sets 239.192.0.0/14 aside for exactly this kind of use
170OWN_GROUP = "239.192.0.7"
171# A group nothing in this suite ever joins. Deliberately not the default one:
172# other trackers here do join that, on ports the harness picks at random, and
173# a socket bound to the wildcard address shares a port's multicast traffic
174# with anything else on the host that has joined the group it is addressed
175# to. Two clusters sharing a host and a port is pathological in the field but
176# routine in a test suite.
177UNJOINED_GROUP = "239.192.0.99"
178
179
180@unittest.skipUnless(has_livesync(), "this build has no WANT_SYNC_LIVE")
181class LiveSyncGroup(TrackerTestCase):
182 config = ("livesync.cluster.group %s\n"
183 "livesync.cluster.listen 127.0.0.1:{livesync_port}\n"
184 "livesync.cluster.node_ip 127.0.0.1\n" % OWN_GROUP)
185
186 def test_the_configured_group_is_joined(self):
187 info_hash = self.hash_for("own-group")
188 self.tracker.livesync_send(packet(SYNC_PEER4, [(info_hash, peer4("10.1.3.1", 6881, FLAG_SEEDING))]),
189 group=OWN_GROUP)
190 self.assertTrue(self.tracker.wait_until(lambda: self.tracker.torrent_count() == 1))
191
192 def test_another_group_is_left_alone(self):
193 """A cluster that picked its own group hears nothing from the others."""
194 info_hash = self.hash_for("not-our-group")
195 self.tracker.livesync_send(packet(SYNC_PEER4, [(info_hash, peer4("10.1.3.2", 6881, FLAG_SEEDING))]),
196 group=UNJOINED_GROUP)
197 self.assertFalse(self.tracker.wait_until(lambda: self.tracker.torrent_count() != 0, timeout=1))
198
199
200@unittest.skipUnless(has_livesync(), "this build has no WANT_SYNC_LIVE")
201class LiveSyncGroupRefused(unittest.TestCase):
202 """A cluster that asked for a group of its own must not quietly end up on
203 the one every other opentracker is on, so a line that does not name a
204 group stops the tracker rather than falling back."""
205
206 def start_with(self, group):
207 return Tracker("livesync.cluster.group %s\n"
208 "livesync.cluster.listen 127.0.0.1:{livesync_port}\n"
209 "livesync.cluster.node_ip 127.0.0.1\n" % group)
210
211 def test_a_unicast_address_stops_the_tracker(self):
212 with self.assertRaises(AssertionError) as caught:
213 self.start_with("10.0.0.1")
214 self.assertIn("multicast address", str(caught.exception))
215
216 def test_so_does_something_that_is_not_an_address(self):
217 with self.assertRaises(AssertionError) as caught:
218 self.start_with("not-an-address")
219 self.assertIn("multicast address", str(caught.exception))
220
221 def test_a_group_at_either_end_of_the_range_is_taken(self):
222 for group in ("224.0.0.1", "239.255.255.255"):
223 tracker = self.start_with(group)
224 try:
225 self.assertEqual(tracker.torrent_count(), 0)
226 finally:
227 tracker.stop()