Fixed #162: Test suite now uses port 0 binding

This commit is contained in:
Andrew Godwin 2018-02-07 12:02:04 -08:00
parent 678a97ec7f
commit 13511d2ca6
3 changed files with 25 additions and 47 deletions

View File

@ -176,13 +176,13 @@ class CommandLineInterface(object):
sys.path.insert(0, ".") sys.path.insert(0, ".")
application = import_by_path(args.application) application = import_by_path(args.application)
# Set up port/host bindings # Set up port/host bindings
if not any([args.host, args.port, args.unix_socket, args.file_descriptor, args.socket_strings]): if not any([args.host, args.port is not None, args.unix_socket, args.file_descriptor, args.socket_strings]):
# no advanced binding options passed, patch in defaults # no advanced binding options passed, patch in defaults
args.host = DEFAULT_HOST args.host = DEFAULT_HOST
args.port = DEFAULT_PORT args.port = DEFAULT_PORT
elif args.host and not args.port: elif args.host and args.port is None:
args.port = DEFAULT_PORT args.port = DEFAULT_PORT
elif args.port and not args.host: elif args.port is not None and not args.host:
args.host = DEFAULT_HOST args.host = DEFAULT_HOST
# Build endpoint description strings from (optional) cli arguments # Build endpoint description strings from (optional) cli arguments
endpoints = build_endpoint_description_strings( endpoints = build_endpoint_description_strings(

View File

@ -108,7 +108,7 @@ class Server(object):
reactor.callLater(2, self.timeout_checker) reactor.callLater(2, self.timeout_checker)
for socket_description in self.endpoints: for socket_description in self.endpoints:
logger.info("Listening on endpoint %s", socket_description) logger.info("Configuring endpoint %s", socket_description)
ep = serverFromString(reactor, str(socket_description)) ep = serverFromString(reactor, str(socket_description))
listener = ep.listen(self.http_factory) listener = ep.listen(self.http_factory)
listener.addCallback(self.listen_success) listener.addCallback(self.listen_success)
@ -139,6 +139,7 @@ class Server(object):
host = port.getHost() host = port.getHost()
if hasattr(host, "host") and hasattr(host, "port"): if hasattr(host, "host") and hasattr(host, "port"):
self.listening_addresses.append((host.host, host.port)) self.listening_addresses.append((host.host, host.port))
logger.info("Listening on TCP address %s:%s", port.getHost().host, port.getHost().port)
def listen_error(self, failure): def listen_error(self, failure):
logger.critical("Listen failure: %s", failure.getErrorMessage()) logger.critical("Listen failure: %s", failure.getErrorMessage())

View File

@ -1,5 +1,4 @@
import errno
import random
import socket import socket
import struct import struct
import subprocess import subprocess
@ -19,60 +18,38 @@ class DaphneTestingInstance:
Works as a context manager. Works as a context manager.
""" """
def __init__(self, xff=False, http_timeout=60): def __init__(self, xff=False, http_timeout=None):
self.xff = xff self.xff = xff
self.http_timeout = http_timeout self.http_timeout = http_timeout
self.host = "127.0.0.1" self.host = "127.0.0.1"
def port_in_use(self, port):
"""
Tests if a port is in use on the local machine.
"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind(("127.0.0.1", port))
except socket.error as e:
if e.errno in [errno.EACCES, errno.EADDRINUSE]:
return True
else:
raise
else:
return False
finally:
s.close()
def find_free_port(self):
"""
Finds an unused port to test stuff on
"""
for _ in range(100):
port = random.randint(11200, 11300)
if not self.port_in_use(port):
return port
raise RuntimeError("Cannot find a free port to test on")
def __enter__(self): def __enter__(self):
# Clear result storage # Clear result storage
TestApplication.delete_setup() TestApplication.delete_setup()
TestApplication.delete_result() TestApplication.delete_result()
# Find a port to listen on # Tell Daphne to use port 0 so the OS gives it a free port
self.port = self.find_free_port() daphne_args = ["daphne", "-p", "0", "-v", "1"]
daphne_args = ["daphne", "-p", str(self.port), "-v", "0"]
# Optionally enable X-Forwarded-For support. # Optionally enable X-Forwarded-For support.
if self.xff: if self.xff:
daphne_args += ["--proxy-headers"] daphne_args += ["--proxy-headers"]
if self.http_timeout: if self.http_timeout:
daphne_args += ["--http-timeout=%i" % self.http_timeout] daphne_args += ["--http-timeout=%i" % self.http_timeout]
# Start up process and make sure it begins listening. Try this 3 times. # Start up process
for _ in range(3): self.process = subprocess.Popen(
self.process = subprocess.Popen(daphne_args + ["daphne.test_application:TestApplication"]) daphne_args + ["daphne.test_application:TestApplication"],
for _ in range(30): stdout=subprocess.PIPE,
time.sleep(0.1) stderr=subprocess.STDOUT,
if self.port_in_use(self.port): )
return self # Read the port from its stdout
# Daphne didn't start up. Sadface. stdout = b""
self.process.terminate() for line in self.process.stdout:
raise RuntimeError("Daphne never came up.") stdout += line
if b"Listening on TCP address " in line:
self.port = int(line.split(b"TCP address ")[1].split(b":")[1].strip())
return self
else:
# Daphne didn't start up right :(
raise RuntimeError("Daphne never listened on a port. Output: \n%s" % stdout)
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
# Shut down the process # Shut down the process