diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/integration/harness.py | 38 |
1 files changed, 35 insertions, 3 deletions
diff --git a/tests/integration/harness.py b/tests/integration/harness.py index 868075d..7e75574 100644 --- a/tests/integration/harness.py +++ b/tests/integration/harness.py | |||
| @@ -60,17 +60,20 @@ def free_port(): | |||
| 60 | class Tracker: | 60 | class Tracker: |
| 61 | """One tracker process, listening on loopback.""" | 61 | """One tracker process, listening on loopback.""" |
| 62 | 62 | ||
| 63 | def __init__(self, extra_config=""): | 63 | def __init__(self, extra_config="", files=None): |
| 64 | self.port = free_port() | 64 | self.port = free_port() |
| 65 | self.dir = tempfile.mkdtemp(prefix="ot-integration-") | 65 | self.dir = tempfile.mkdtemp(prefix="ot-integration-") |
| 66 | self.log = None | 66 | self.log = None |
| 67 | self.process = None | 67 | self.process = None |
| 68 | try: | 68 | try: |
| 69 | for name, content in (files or {}).items(): | ||
| 70 | with open(os.path.join(self.dir, name), "wb") as handle: | ||
| 71 | handle.write(content) | ||
| 69 | self.config = os.path.join(self.dir, "opentracker.conf") | 72 | self.config = os.path.join(self.dir, "opentracker.conf") |
| 70 | with open(self.config, "w") as handle: | 73 | with open(self.config, "w") as handle: |
| 71 | handle.write("listen.tcp 127.0.0.1:%d\n" % self.port) | 74 | handle.write("listen.tcp 127.0.0.1:%d\n" % self.port) |
| 72 | handle.write("listen.udp 127.0.0.1:%d\n" % self.port) | 75 | handle.write("listen.udp 127.0.0.1:%d\n" % self.port) |
| 73 | handle.write(extra_config) | 76 | handle.write(extra_config.replace("{dir}", self.dir)) |
| 74 | self.log = open(os.path.join(self.dir, "tracker.log"), "w+b") | 77 | self.log = open(os.path.join(self.dir, "tracker.log"), "w+b") |
| 75 | # Leak detection is left at the sanitizer's own default, which is on | 78 | # Leak detection is left at the sanitizer's own default, which is on |
| 76 | # where LeakSanitizer exists and off where it does not. Asking for it | 79 | # where LeakSanitizer exists and off where it does not. Asking for it |
| @@ -154,6 +157,12 @@ class Tracker: | |||
| 154 | def stats(self, mode): | 157 | def stats(self, mode): |
| 155 | return self.get("/stats?mode=" + mode) | 158 | return self.get("/stats?mode=" + mode) |
| 156 | 159 | ||
| 160 | def get_with_headers(self, path, headers=None, timeout=10): | ||
| 161 | """The body and the response headers, for the compressed answers.""" | ||
| 162 | request = urllib.request.Request("http://127.0.0.1:%d%s" % (self.port, path), headers=headers or {}) | ||
| 163 | with urllib.request.urlopen(request, timeout=timeout) as response: | ||
| 164 | return response.read(), dict(response.headers) | ||
| 165 | |||
| 157 | def torrent_count(self): | 166 | def torrent_count(self): |
| 158 | return int(self.stats("torr").split()[0]) | 167 | return int(self.stats("torr").split()[0]) |
| 159 | 168 | ||
| @@ -180,14 +189,37 @@ class Tracker: | |||
| 180 | assert action == 1, "udp announce answered action %d" % action | 189 | assert action == 1, "udp announce answered action %d" % action |
| 181 | return {"interval": interval, "leechers": leechers, "seeders": seeders, "peers": answer[20:]} | 190 | return {"interval": interval, "leechers": leechers, "seeders": seeders, "peers": answer[20:]} |
| 182 | 191 | ||
| 192 | def udp_scrape(self, sock, connection_id, hashes): | ||
| 193 | """One (seeders, completed, leechers) triple per hash, in order.""" | ||
| 194 | sock.sendto(struct.pack(">QII", connection_id, 2, 3) + b"".join(hashes), ("127.0.0.1", self.port)) | ||
| 195 | answer = sock.recv(2048) | ||
| 196 | action, _ = struct.unpack(">II", answer[:8]) | ||
| 197 | assert action == 2, "udp scrape answered action %d" % action | ||
| 198 | return [struct.unpack(">III", answer[8 + 12 * i : 20 + 12 * i]) for i in range(len(hashes))] | ||
| 199 | |||
| 200 | def udp_raw(self, payload, timeout=1.0): | ||
| 201 | """Sends a packet exactly as given. Returns the answer, or None if the | ||
| 202 | tracker chose to say nothing, which is how it treats a broken one.""" | ||
| 203 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||
| 204 | sock.settimeout(timeout) | ||
| 205 | try: | ||
| 206 | sock.sendto(payload, ("127.0.0.1", self.port)) | ||
| 207 | try: | ||
| 208 | return sock.recv(2048) | ||
| 209 | except socket.timeout: | ||
| 210 | return None | ||
| 211 | finally: | ||
| 212 | sock.close() | ||
| 213 | |||
| 183 | 214 | ||
| 184 | class TrackerTestCase(unittest.TestCase): | 215 | class TrackerTestCase(unittest.TestCase): |
| 185 | """Gives every test its own tracker and fails it on any sanitizer output.""" | 216 | """Gives every test its own tracker and fails it on any sanitizer output.""" |
| 186 | 217 | ||
| 187 | config = "" | 218 | config = "" |
| 219 | files = None | ||
| 188 | 220 | ||
| 189 | def setUp(self): | 221 | def setUp(self): |
| 190 | self.tracker = Tracker(self.config) | 222 | self.tracker = Tracker(self.config, self.files) |
| 191 | 223 | ||
| 192 | def tearDown(self): | 224 | def tearDown(self): |
| 193 | complaints = self.tracker.stop() | 225 | complaints = self.tracker.stop() |
