summaryrefslogtreecommitdiff
path: root/tests/integration/harness.py
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-10-27 00:28:35 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-10-27 00:28:35 +0100
commit8c482cb2f7019c2491f3bf4f25c4fb5d395ad5da (patch)
tree78b43b10eb798aeaa46428a3391c8fde5bd565a5 /tests/integration/harness.py
parentb1cfed46d5ac6d73246540ebb915cd1314e27f6e (diff)
add more checkers to the sanitizer and integration check runs
Diffstat (limited to 'tests/integration/harness.py')
-rw-r--r--tests/integration/harness.py60
1 files changed, 46 insertions, 14 deletions
diff --git a/tests/integration/harness.py b/tests/integration/harness.py
index c8da1e2..868075d 100644
--- a/tests/integration/harness.py
+++ b/tests/integration/harness.py
@@ -7,6 +7,8 @@ reports its own memory errors, which the teardown turns into test failures.
7""" 7"""
8 8
9import os 9import os
10import shutil
11import signal
10import socket 12import socket
11import struct 13import struct
12import subprocess 14import subprocess
@@ -61,15 +63,34 @@ class Tracker:
61 def __init__(self, extra_config=""): 63 def __init__(self, extra_config=""):
62 self.port = free_port() 64 self.port = free_port()
63 self.dir = tempfile.mkdtemp(prefix="ot-integration-") 65 self.dir = tempfile.mkdtemp(prefix="ot-integration-")
64 self.config = os.path.join(self.dir, "opentracker.conf") 66 self.log = None
65 with open(self.config, "w") as handle: 67 self.process = None
66 handle.write("listen.tcp 127.0.0.1:%d\n" % self.port) 68 try:
67 handle.write("listen.udp 127.0.0.1:%d\n" % self.port) 69 self.config = os.path.join(self.dir, "opentracker.conf")
68 handle.write(extra_config) 70 with open(self.config, "w") as handle:
69 self.log = open(os.path.join(self.dir, "tracker.log"), "w+b") 71 handle.write("listen.tcp 127.0.0.1:%d\n" % self.port)
70 environment = dict(os.environ, ASAN_OPTIONS="detect_leaks=0:abort_on_error=0") 72 handle.write("listen.udp 127.0.0.1:%d\n" % self.port)
71 self.process = subprocess.Popen([TRACKER, "-f", self.config], stdout=self.log, stderr=self.log, env=environment) 73 handle.write(extra_config)
72 self._wait_for_port() 74 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
76 # where LeakSanitizer exists and off where it does not. Asking for it
77 # by name makes the tracker refuse to start on macOS and FreeBSD.
78 environment = dict(os.environ, ASAN_OPTIONS="abort_on_error=0")
79 self.process = subprocess.Popen([TRACKER, "-f", self.config], stdout=self.log, stderr=self.log, env=environment)
80 self._wait_for_port()
81 except BaseException:
82 self._cleanup()
83 raise
84
85 def _cleanup(self):
86 """Close what we opened and take the scratch directory with it."""
87 if self.process is not None and self.process.poll() is None:
88 self.process.kill()
89 self.process.wait()
90 if self.log is not None:
91 self.log.close()
92 self.log = None
93 shutil.rmtree(self.dir, ignore_errors=True)
73 94
74 def _wait_for_port(self): 95 def _wait_for_port(self):
75 for _ in range(200): 96 for _ in range(200):
@@ -84,21 +105,32 @@ class Tracker:
84 raise AssertionError("tracker never listened on %d:\n%s" % (self.port, self.output())) 105 raise AssertionError("tracker never listened on %d:\n%s" % (self.port, self.output()))
85 106
86 def output(self): 107 def output(self):
108 if self.log is None:
109 return ""
87 self.log.flush() 110 self.log.flush()
88 self.log.seek(0) 111 self.log.seek(0)
89 return self.log.read().decode("utf-8", "replace") 112 return self.log.read().decode("utf-8", "replace")
90 113
91 def stop(self): 114 def stop(self):
92 """Returns anything the tracker complained about on the way out.""" 115 """Returns anything the tracker complained about on the way out.
93 if self.process.poll() is None: 116
94 self.process.kill() 117 The interrupt rather than a kill is what makes the leak check work at
95 self.process.wait() 118 all: LeakSanitizer reports from an exit handler, so a killed tracker
119 reports nothing and the suite passes over leaks it never looked for.
120 """
121 if self.process is not None and self.process.poll() is None:
122 self.process.send_signal(signal.SIGINT)
123 try:
124 self.process.wait(timeout=5)
125 except subprocess.TimeoutExpired:
126 self.process.kill()
127 self.process.wait()
96 complaints = [ 128 complaints = [
97 line 129 line
98 for line in self.output().splitlines() 130 for line in self.output().splitlines()
99 if "Sanitizer" in line or "runtime error" in line or line.startswith("ERROR") 131 if "Sanitizer" in line or "runtime error" in line or line.startswith("ERROR")
100 ] 132 ]
101 self.log.close() 133 self._cleanup()
102 return complaints 134 return complaints
103 135
104 # ----------------------------------------------------------- over http 136 # ----------------------------------------------------------- over http