From 8c482cb2f7019c2491f3bf4f25c4fb5d395ad5da Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Tue, 27 Oct 2026 00:28:35 +0100 Subject: add more checkers to the sanitizer and integration check runs --- tests/integration/harness.py | 60 +++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 14 deletions(-) (limited to 'tests/integration/harness.py') 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. """ import os +import shutil +import signal import socket import struct import subprocess @@ -61,15 +63,34 @@ class Tracker: def __init__(self, extra_config=""): self.port = free_port() self.dir = tempfile.mkdtemp(prefix="ot-integration-") - self.config = os.path.join(self.dir, "opentracker.conf") - with open(self.config, "w") as handle: - handle.write("listen.tcp 127.0.0.1:%d\n" % self.port) - handle.write("listen.udp 127.0.0.1:%d\n" % self.port) - handle.write(extra_config) - self.log = open(os.path.join(self.dir, "tracker.log"), "w+b") - environment = dict(os.environ, ASAN_OPTIONS="detect_leaks=0:abort_on_error=0") - self.process = subprocess.Popen([TRACKER, "-f", self.config], stdout=self.log, stderr=self.log, env=environment) - self._wait_for_port() + self.log = None + self.process = None + try: + self.config = os.path.join(self.dir, "opentracker.conf") + with open(self.config, "w") as handle: + handle.write("listen.tcp 127.0.0.1:%d\n" % self.port) + handle.write("listen.udp 127.0.0.1:%d\n" % self.port) + handle.write(extra_config) + self.log = open(os.path.join(self.dir, "tracker.log"), "w+b") + # Leak detection is left at the sanitizer's own default, which is on + # where LeakSanitizer exists and off where it does not. Asking for it + # by name makes the tracker refuse to start on macOS and FreeBSD. + environment = dict(os.environ, ASAN_OPTIONS="abort_on_error=0") + self.process = subprocess.Popen([TRACKER, "-f", self.config], stdout=self.log, stderr=self.log, env=environment) + self._wait_for_port() + except BaseException: + self._cleanup() + raise + + def _cleanup(self): + """Close what we opened and take the scratch directory with it.""" + if self.process is not None and self.process.poll() is None: + self.process.kill() + self.process.wait() + if self.log is not None: + self.log.close() + self.log = None + shutil.rmtree(self.dir, ignore_errors=True) def _wait_for_port(self): for _ in range(200): @@ -84,21 +105,32 @@ class Tracker: raise AssertionError("tracker never listened on %d:\n%s" % (self.port, self.output())) def output(self): + if self.log is None: + return "" self.log.flush() self.log.seek(0) return self.log.read().decode("utf-8", "replace") def stop(self): - """Returns anything the tracker complained about on the way out.""" - if self.process.poll() is None: - self.process.kill() - self.process.wait() + """Returns anything the tracker complained about on the way out. + + The interrupt rather than a kill is what makes the leak check work at + all: LeakSanitizer reports from an exit handler, so a killed tracker + reports nothing and the suite passes over leaks it never looked for. + """ + if self.process is not None and self.process.poll() is None: + self.process.send_signal(signal.SIGINT) + try: + self.process.wait(timeout=5) + except subprocess.TimeoutExpired: + self.process.kill() + self.process.wait() complaints = [ line for line in self.output().splitlines() if "Sanitizer" in line or "runtime error" in line or line.startswith("ERROR") ] - self.log.close() + self._cleanup() return complaints # ----------------------------------------------------------- over http -- cgit v1.3