← Commit list

c39193a589

Dan Fandrich
Dan Fandrich
2026-07-28
SUCCESS Duration: 191s

Subject

tests: improve exception handling in Python test code

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

tests/dictserver.py+2 -2
@@ -101,8 +101,8 @@ class DictHandler(socketserver.BaseRequestHandler):
log.debug("[DICT] Responding with %r", response)
self.request.sendall(response.encode("utf-8"))
- except IOError:
- log.exception("[DICT] IOError hit during request")
+ except OSError:
+ log.exception("[DICT] OSError hit during request")
def get_options():
tests/http/scorecard.py+3 -3
@@ -148,7 +148,7 @@ class Card:
def parse_size(cls, s):
m = re.match(r'(\d+)(mb|kb|gb)?', s, re.IGNORECASE)
if m is None:
- raise Exception(f'unrecognized size: {s}')
+ raise ScoreCardError(f'unrecognized size: {s}')
size = int(m.group(1))
if not m.group(2):
pass
@@ -263,7 +263,7 @@ class ScoreRunner:
if self._limit_rate:
m = re.match(r'(\d+(\.\d+)?)([gmkb])?', self._limit_rate.lower())
if not m:
- raise Exception(f'unrecognised limit-rate: {self._limit_rate}')
+ raise ScoreCardError(f'unrecognised limit-rate: {self._limit_rate}')
self._limit_rate_num = float(m.group(1))
if m.group(3) == 'g':
self._limit_rate_num *= 1024 * 1024 * 1024
@@ -274,7 +274,7 @@ class ScoreRunner:
elif m.group(3) == 'b':
pass
else:
- raise Exception(f'unrecognised limit-rate: {self._limit_rate}')
+ raise ScoreCardError(f'unrecognised limit-rate: {self._limit_rate}')
self.suppress_cl = suppress_cl
def info(self, msg):
tests/http/test_05_errors.py+3 -4
@@ -24,6 +24,7 @@
#
###########################################################################
#
+import contextlib
import logging
import os
import socket
@@ -192,13 +193,11 @@ class TestErrors:
# accept one connection and immediately close it
def accept_and_close():
- try:
+ # ignore expected socket error
+ with contextlib.suppress(OSError):
conn, _ = server.accept()
conn.recv(1) # wait for ClientHello
conn.close()
- except Exception:
- # ignore expected socket error
- pass
t = threading.Thread(target=accept_and_close)
t.start()
tests/http/test_11_unix.py+1 -3
@@ -82,9 +82,7 @@ Content-Length: 19
finally:
c.close()
- except ConnectionAbortedError:
- self._done = True
- except OSError:
+ except (ConnectionAbortedError, OSError):
self._done = True
tests/http/testenv/caddy.py+1 -1
@@ -125,7 +125,7 @@ class Caddy:
self._process.terminate()
try:
self._process.wait(timeout=1)
- except Exception:
+ except subprocess.TimeoutExpired:
self._process.kill()
self._process = None
self.close_log()
tests/http/testenv/certs.py+7 -5
@@ -57,6 +57,10 @@ EC_SUPPORTED.update([(curve.name.upper(), curve) for curve in [
]])
+class CertError(Exception):
+ """Error in certificate handling."""
+
+
def _private_key(key_type):
if isinstance(key_type, str):
key_type = key_type.upper()
@@ -150,7 +154,7 @@ class Credentials:
return f"rsa{self._pkey.key_size}"
if isinstance(self._pkey, EllipticCurvePrivateKey):
return f"{self._pkey.curve.name}"
- raise Exception(f"unknown key type: {self._pkey}")
+ raise CertError(f"unknown key type: {self._pkey}")
@property
def private_key(self) -> Any:
@@ -249,9 +253,7 @@ class Credentials:
os.makedirs(self.hashdir, exist_ok=True)
p = subprocess.run(args=[
openssl, 'x509', '-hash', '-noout', '-in', self.cert_file
- ], capture_output=True, text=True)
- if p.returncode != 0:
- raise Exception(f'openssl failed to compute cert hash: {p}')
+ ], capture_output=True, text=True, check=True)
cert_hname = f'{p.stdout.strip()}.0'
shutil.copy(self.cert_file, os.path.join(self.hashdir, cert_hname))
@@ -401,7 +403,7 @@ class TestCA:
valid_from=valid_from, valid_to=valid_to,
key_type=key_type)
else:
- raise Exception(f"unrecognized certificate specification: {spec}")
+ raise CertError(f"unrecognized certificate specification: {spec}")
return creds
@staticmethod
tests/http/testenv/client.py+1 -1
@@ -98,7 +98,7 @@ class LocalClient:
p = subprocess.run(myargs, stderr=cerr, stdout=cout,
cwd=self._run_dir, shell=False,
input=None, env=run_env,
- timeout=self._timeout)
+ timeout=self._timeout, check=False)
exitcode = p.returncode
except subprocess.TimeoutExpired:
log.warning(f'Timeout after {self._timeout}s: {args}')
tests/http/testenv/curl.py+27 -40
@@ -24,6 +24,7 @@
#
###########################################################################
#
+import contextlib
import json
import logging
import os
@@ -41,7 +42,7 @@ from urllib.parse import urlparse
import psutil
-from .env import Env
+from .env import Env, EnvError
log = logging.getLogger(__name__)
@@ -133,12 +134,9 @@ class PerfProfile:
self._proc.terminate()
self._rc = self._proc.returncode
with open(self._file, 'w') as cout:
- p = subprocess.run([
+ subprocess.run([
'sudo', 'perf', 'script'
- ], stdout=cout, cwd=self._run_dir, shell=False)
- rc = p.returncode
- if rc != 0:
- raise Exception(f'perf returned error {rc}')
+ ], stdout=cout, cwd=self._run_dir, shell=False, check=True)
@property
def file(self):
@@ -192,7 +190,7 @@ class RunTcpDump:
port_pairs: Optional[List[Tuple[int, int]]] = None
) -> Optional[List[str]]:
if self._proc:
- raise Exception('tcpdump still running')
+ raise EnvError('tcpdump still running')
# a pair matches only a RST between exactly these two ports, while
# a port in `ports` matches any RST it is involved in
pairs = None
@@ -222,7 +220,7 @@ class RunTcpDump:
@property
def stderr(self) -> List[str]:
if self._proc:
- raise Exception('tcpdump still running')
+ raise EnvError('tcpdump still running')
with open(self._stderrfile) as fd:
return fd.readlines()
@@ -232,7 +230,7 @@ class RunTcpDump:
try:
tcpdump = self._env.tcpdump()
if tcpdump is None:
- raise Exception('tcpdump not available')
+ raise EnvError('tcpdump not available')
# look with tcpdump for TCP RST packets which indicate
# we did not shut down connections cleanly
args = []
@@ -249,11 +247,9 @@ class RunTcpDump:
assert self._proc
assert self._proc.returncode is None
while self._proc:
- try:
+ # timeout means tcpdump is still running
+ with contextlib.suppress(subprocess.TimeoutExpired):
self._proc.wait(timeout=1)
- except subprocess.TimeoutExpired:
- # timeout means tcpdump is still running
- pass
except Exception:
log.exception('Tcpdump')
@@ -649,13 +645,13 @@ class CurlClient:
if 'FLAMEGRAPH' in os.environ:
self._fg_dir = os.environ['FLAMEGRAPH']
if not os.path.exists(self._fg_dir):
- raise Exception(f'FlameGraph checkout not found in {self._fg_dir}, set env variable FLAMEGRAPH')
+ raise EnvError(f'FlameGraph checkout not found in {self._fg_dir}, set env variable FLAMEGRAPH')
if sys.platform.startswith('linux'):
self._with_perf = True
elif sys.platform.startswith('darwin'):
self._with_dtrace = True
else:
- raise Exception(f'flame graphs unsupported on {sys.platform}')
+ raise EnvError(f'flame graphs unsupported on {sys.platform}')
self._socks_args = socks_args
self._silent = silent
self._run_env = run_env
@@ -919,7 +915,7 @@ class CurlClient:
'--upload-file', '-'
])
else:
- raise Exception('need either file or data to upload')
+ raise EnvError('need either file or data to upload')
if with_stats:
extra_args.extend([
'-w', '%{json}\\n'
@@ -995,7 +991,7 @@ class CurlClient:
'--upload-file', '-'
])
else:
- raise Exception('need either file or data to upload')
+ raise EnvError('need either file or data to upload')
if with_stats:
extra_args.extend([
'-w', '%{json}\\n'
@@ -1071,7 +1067,7 @@ class CurlClient:
cwd=self._run_dir, shell=False,
input=intext.encode() if intext else None,
timeout=self._timeout,
- env=self._run_env)
+ env=self._run_env, check=False)
exitcode = p.returncode
except subprocess.TimeoutExpired:
now = datetime.now()
@@ -1160,7 +1156,7 @@ class CurlClient:
args.extend(options)
if alpn_proto is not None:
if alpn_proto not in self.ALPN_ARG:
- raise Exception(f'unknown ALPN protocol: "{alpn_proto}"')
+ raise EnvError(f'unknown ALPN protocol: "{alpn_proto}"')
args.append(self.ALPN_ARG[alpn_proto])
if u.scheme == 'http':
@@ -1243,36 +1239,30 @@ class CurlClient:
def _perf_collapse(self, perf: PerfProfile, file_err):
if not os.path.exists(perf.file):
- raise Exception(f'perf output file does not exist: {perf.file}')
+ raise EnvError(f'perf output file does not exist: {perf.file}')
fg_collapse = os.path.join(self._fg_dir, 'stackcollapse-perf.pl')
if not os.path.exists(fg_collapse):
- raise Exception(f'FlameGraph script not found: {fg_collapse}')
+ raise EnvError(f'FlameGraph script not found: {fg_collapse}')
stacks_collapsed = f'{perf.file}.collapsed'
log.info(f'collapsing stacks into {stacks_collapsed}')
with open(stacks_collapsed, 'w') as cout, open(file_err, 'w') as cerr:
- p = subprocess.run([
+ subprocess.run([
fg_collapse, perf.file
- ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False)
- rc = p.returncode
- if rc != 0:
- raise Exception(f'{fg_collapse} returned error {rc}')
+ ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True)
return stacks_collapsed
def _dtrace_collapse(self, dtrace: DTraceProfile, file_err):
if not os.path.exists(dtrace.file):
- raise Exception(f'dtrace output file does not exist: {dtrace.file}')
+ raise EnvError(f'dtrace output file does not exist: {dtrace.file}')
fg_collapse = os.path.join(self._fg_dir, 'stackcollapse.pl')
if not os.path.exists(fg_collapse):
- raise Exception(f'FlameGraph script not found: {fg_collapse}')
+ raise EnvError(f'FlameGraph script not found: {fg_collapse}')
stacks_collapsed = f'{dtrace.file}.collapsed'
log.info(f'collapsing stacks into {stacks_collapsed}')
with open(stacks_collapsed, 'w') as cout, open(file_err, 'a') as cerr:
- p = subprocess.run([
+ subprocess.run([
fg_collapse, dtrace.file
- ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False)
- rc = p.returncode
- if rc != 0:
- raise Exception(f'{fg_collapse} returned error {rc}')
+ ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True)
return stacks_collapsed
def _generate_flame(self, curl_args: List[str],
@@ -1281,7 +1271,7 @@ class CurlClient:
fg_gen_flame = os.path.join(self._fg_dir, 'flamegraph.pl')
file_svg = os.path.join(self._run_dir, 'curl.flamegraph.svg')
if not os.path.exists(fg_gen_flame):
- raise Exception(f'FlameGraph script not found: {fg_gen_flame}')
+ raise EnvError(f'FlameGraph script not found: {fg_gen_flame}')
log.info('waiting a sec for perf/dtrace to finish flushing')
time.sleep(2)
@@ -1292,7 +1282,7 @@ class CurlClient:
elif dtrace:
stacks_collapsed = self._dtrace_collapse(dtrace, file_err)
else:
- raise Exception('no stacks measure given')
+ raise EnvError('no stacks measure given')
log.info(f'generating graph into {file_svg}')
cmdline = ' '.join(curl_args)
@@ -1303,14 +1293,11 @@ class CurlClient:
title = cmdline
subtitle = ''
with open(file_svg, 'w') as cout, open(file_err, 'a') as cerr:
- p = subprocess.run([
+ subprocess.run([
fg_gen_flame, '--colors', 'green',
'--title', title, '--subtitle', subtitle,
stacks_collapsed
- ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False)
- rc = p.returncode
- if rc != 0:
- raise Exception(f'{fg_gen_flame} returned error {rc}')
+ ], stdout=cout, stderr=cerr, cwd=self._run_dir, shell=False, check=True)
def mk_altsvc_file(self, name, src_alpn, src_host, src_port,
dest_alpn, dest_host, dest_port):
tests/http/testenv/env.py+18 -21
@@ -43,6 +43,10 @@ from .certs import CertificateSpec, Credentials, TestCA
log = logging.getLogger(__name__)
+class EnvError(Exception):
+ """Exception from the test environment."""
+
+
def init_config_from(conf_path):
if os.path.isfile(conf_path):
config = ConfigParser(interpolation=ExtendedInterpolation())
@@ -58,7 +62,7 @@ CONFIG_PATH = os.path.join(TOP_PATH, "tests", "http", "config.ini")
if not os.path.exists(CONFIG_PATH):
ALT_CONFIG_PATH = os.path.join(PROJ_PATH, "tests", "http", "config.ini")
if not os.path.exists(ALT_CONFIG_PATH):
- raise Exception(
+ raise EnvError(
f"unable to find config.ini in {CONFIG_PATH} nor {ALT_CONFIG_PATH}"
)
TOP_PATH = PROJ_PATH
@@ -77,11 +81,7 @@ class NghttpxUtil:
if cmd is None:
return None
if cls.VERSION_FULL is None or cmd != cls.CMD:
- p = subprocess.run(args=[cmd, "--version"], capture_output=True, text=True)
- if p.returncode != 0:
- raise RuntimeError(
- f"{cmd} --version failed with exit code: {p.returncode}"
- )
+ p = subprocess.run(args=[cmd, "--version"], capture_output=True, text=True, check=True)
cls.CMD = cmd
for line in p.stdout.splitlines(keepends=False):
if line.startswith("nghttpx "):
@@ -131,9 +131,7 @@ class EnvConfig:
}
self.curl_is_debug = False
self.curl_protos = []
- p = subprocess.run(args=[self.curl, "-V"], capture_output=True, text=True)
- if p.returncode != 0:
- raise RuntimeError(f"{self.curl} -V failed with exit code: {p.returncode}")
+ p = subprocess.run(args=[self.curl, "-V"], capture_output=True, text=True, check=True)
if p.stderr.startswith("WARNING:"):
self.curl_is_debug = True
for line in p.stdout.splitlines(keepends=False):
@@ -162,9 +160,7 @@ class EnvConfig:
prot.lower() for prot in line[11:].split(" ")
}
- p = subprocess.run(args=[self.curlinfo], capture_output=True, text=True)
- if p.returncode != 0:
- raise RuntimeError(f"{self.curlinfo} failed with exit code: {p.returncode}")
+ p = subprocess.run(args=[self.curlinfo], capture_output=True, text=True, check=True)
self.curl_is_verbose = 'verbose-strings: ON' in p.stdout
self.curl_can_cert_status = 'cert-status: ON' in p.stdout
self.curl_override_dns = 'override-dns: ON' in p.stdout
@@ -226,7 +222,7 @@ class EnvConfig:
self.openssl = "openssl"
p = subprocess.run(
- args=[self.openssl, "version"], capture_output=True, text=True
+ args=[self.openssl, "version"], capture_output=True, text=True, check=False
)
if p.returncode != 0:
# no openssl in path
@@ -256,7 +252,7 @@ class EnvConfig:
if self.h2o is not None:
try:
p = subprocess.run(
- args=[self.h2o, "--version"], capture_output=True, text=True
+ args=[self.h2o, "--version"], capture_output=True, text=True, check=False
)
if p.returncode != 0:
# not a working h2o
@@ -274,7 +270,7 @@ class EnvConfig:
if self.caddy is not None:
p = subprocess.run(
- args=[self.caddy, "version"], capture_output=True, text=True
+ args=[self.caddy, "version"], capture_output=True, text=True, check=False
)
if p.returncode != 0:
# not a working caddy
@@ -294,7 +290,7 @@ class EnvConfig:
if self.vsftpd is not None:
with tempfile.TemporaryFile("w+") as tmp:
p = subprocess.run(
- args=[self.vsftpd, "-v"], capture_output=True, text=True, stdin=tmp
+ args=[self.vsftpd, "-v"], capture_output=True, text=True, stdin=tmp, check=False
)
if p.returncode != 0:
# not a working vsftpd
@@ -315,14 +311,14 @@ class EnvConfig:
# vsftp does not use stdout or stderr for printing its version... -.-
self._vsftpd_version = "unknown"
else:
- raise Exception(f"Unable to determine VsFTPD version from: {p.stderr}")
+ raise EnvError(f"Unable to determine VsFTPD version from: {p.stderr}")
self.danted = self.config["danted"]["danted"]
if self.danted == "":
self.danted = None
self._danted_version = None
if self.danted is not None:
- p = subprocess.run(args=[self.danted, "-v"], capture_output=True, text=True)
+ p = subprocess.run(args=[self.danted, "-v"], capture_output=True, text=True, check=False)
assert p.returncode == 0
if p.returncode != 0:
# not a working vsftpd
@@ -334,14 +330,14 @@ class EnvConfig:
self._danted_version = m.group(1)
else:
self.danted = None
- raise Exception(f"Unable to determine danted version from: {p.stderr}")
+ raise EnvError(f"Unable to determine danted version from: {p.stderr}")
self.sshd = self.config["sshd"]["sshd"]
if self.sshd == "":
self.sshd = None
self._sshd_version = None
if self.sshd is not None:
- p = subprocess.run(args=[self.sshd, "-V"], capture_output=True, text=True)
+ p = subprocess.run(args=[self.sshd, "-V"], capture_output=True, text=True, check=False)
assert p.returncode == 0
if p.returncode != 0:
self.sshd = None
@@ -352,7 +348,7 @@ class EnvConfig:
self._sshd_version = m.group(1)
else:
self.sshd = None
- raise Exception(
+ raise EnvError(
f"Unable to determine sshd version from: {p.stderr}"
)
@@ -373,6 +369,7 @@ class EnvConfig:
args=[self.apxs, "-q", "HTTPD_VERSION"],
capture_output=True,
text=True,
+ check=False,
)
if p.returncode != 0:
log.error(f"{self.apxs} failed to query HTTPD_VERSION: {p}")
tests/http/testenv/httpd.py+7 -9
@@ -37,7 +37,7 @@ from json import JSONEncoder
from typing import Dict, List, Optional, Union
from .curl import CurlClient, ExecResult
-from .env import Env
+from .env import Env, EnvError
from .ports import alloc_ports_and_do
log = logging.getLogger(__name__)
@@ -95,14 +95,12 @@ class Httpd:
self._loaded_domain1_cred_name = None
assert env.apxs
p = subprocess.run(args=[env.apxs, '-q', 'libexecdir'],
- capture_output=True, text=True)
- if p.returncode != 0:
- raise Exception(f'{env.apxs} failed to query libexecdir: {p}')
+ capture_output=True, text=True, check=True)
self._mods_dir = p.stdout.strip()
if self._mods_dir is None:
- raise Exception('apache modules directory cannot be found')
+ raise EnvError('apache modules directory cannot be found')
if not os.path.exists(self._mods_dir):
- raise Exception(f'apache modules directory does not exist: {self._mods_dir}')
+ raise EnvError(f'apache modules directory does not exist: {self._mods_dir}')
self._maybe_running = False
self.ports = {}
self._rmf(self._error_log)
@@ -144,7 +142,7 @@ class Httpd:
p = subprocess.run(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE,
cwd=self.env.gen_dir,
input=intext.encode() if intext else None,
- env=env)
+ env=env, check=True)
start = datetime.now()
return ExecResult(args=args, exit_code=p.returncode,
stdout=p.stdout.decode().splitlines(),
@@ -587,9 +585,9 @@ class Httpd:
shutil.copy(in_source, out_source)
p = subprocess.run([
self.env.apxs, '-c', out_source
- ], capture_output=True, cwd=out_dir)
+ ], capture_output=True, cwd=out_dir, check=False)
rv = p.returncode
if rv != 0:
log.error(f"compiling mod_curltest failed: {p.stderr}")
- raise Exception(f"compiling mod_curltest failed: {p.stderr}")
+ raise EnvError(f"compiling mod_curltest failed: {p.stderr}")
Httpd.MOD_CURLTEST = os.path.join(out_dir, '.libs/mod_curltest.so')
tests/http/testenv/ports.py+4 -7
@@ -39,13 +39,10 @@ def alloc_port_set(port_specs: Dict[str, int]) -> Dict[str, int]:
socks = []
ports = {}
for name, ptype in port_specs.items():
- try:
- s = socket.socket(type=ptype)
- s.bind(('127.0.0.1', 0))
- ports[name] = s.getsockname()[1]
- socks.append(s)
- except Exception as e:
- raise e
+ s = socket.socket(type=ptype)
+ s.bind(('127.0.0.1', 0))
+ ports[name] = s.getsockname()[1]
+ socks.append(s)
for s in socks:
s.close()
return ports
tests/http/testenv/sshd.py+6 -12
@@ -125,11 +125,9 @@ class Sshd:
for alg in self._key_algs:
key_file = os.path.join(self._sshd_dir, f'ssh_host_{alg}_key')
if not os.path.exists(key_file):
- p = subprocess.run(args=[
+ subprocess.run(args=[
self._keygen, '-q', '-N', '', '-t', alg, '-f', key_file
- ], capture_output=True, text=True)
- if p.returncode != 0:
- raise RuntimeError(f'error generating host key {key_file}: {p.returncode}')
+ ], capture_output=True, text=True, check=True)
self._host_key_files.append(key_file)
pub_file = f'{key_file}.pub'
self._host_pub_files.append(pub_file)
@@ -139,11 +137,9 @@ class Sshd:
fd_known.write(f'[{self.env.domain1.lower()}]:{self.port} {pubkey}')
fd_unknown.write(f'dummy.invalid {pubkey}')
# hash the known_hosts file, libssh requires it
- p = subprocess.run(args=[
+ subprocess.run(args=[
self._keygen, '-H', '-f', self._known_hosts
- ], capture_output=True, text=True)
- if p.returncode != 0:
- raise RuntimeError(f'error hashing {self._known_hosts}: {p.returncode}')
+ ], capture_output=True, text=True, check=True)
def mk_user_keys(self):
self._user_key_files = []
@@ -152,11 +148,9 @@ class Sshd:
for user in self._users:
key_file = os.path.join(self._sshd_dir, f'id_{user}_user_{alg}_key')
if not os.path.exists(key_file):
- p = subprocess.run(args=[
+ subprocess.run(args=[
self._keygen, '-q', '-N', '', '-t', alg, '-f', key_file
- ], capture_output=True, text=True)
- if p.returncode != 0:
- raise RuntimeError(f'error generating user key {key_file}: {p.returncode}')
+ ], capture_output=True, text=True, check=True)
self._user_key_files.append(key_file)
self._user_pub_files.append(f'{key_file}.pub')
with open(self._auth_keys, 'w') as fd:
tests/http/testenv/ws_echo_server.py+3 -4
@@ -26,6 +26,7 @@
#
import argparse
import asyncio
+import contextlib
import logging
from websockets import server
@@ -33,12 +34,10 @@ from websockets.exceptions import ConnectionClosedError
async def echo(websocket):
- try:
+ # exception when websocket connection closed by client
+ with contextlib.suppress(ConnectionClosedError):
async for message in websocket:
await websocket.send(message)
- except ConnectionClosedError:
- # websocket connection closed by client
- pass
async def run_server(port):
tests/negtelnetserver.py+2 -2
@@ -109,8 +109,8 @@ class NegotiatingTelnetHandler(socketserver.BaseRequestHandler):
self.request.recv(4 * 1024)
self.request.shutdown(socket.SHUT_RDWR)
- except IOError:
- log.exception("IOError hit during request")
+ except OSError:
+ log.exception("OSError hit during request")
class Negotiator:
tests/util.py+1 -1
@@ -77,7 +77,7 @@ class TestData:
m = REPLY_DATA.search(contents)
if not m:
- raise Exception("Could not find a <reply><data> section")
+ raise RuntimeError("Could not find a <reply><data> section")
# Left-strip the data so we do not get a newline before our data.
return m.group(1).lstrip()