Skip to content
Snippets Groups Projects
Commit 58e09b5f authored by Oz Linden's avatar Oz Linden
Browse files

merge changes for chop-661

parents b5f00fd1 02cfd172
No related branches found
No related tags found
No related merge requests found
...@@ -124,14 +124,19 @@ def log_error(self, format, *args): ...@@ -124,14 +124,19 @@ def log_error(self, format, *args):
# Suppress error output as well # Suppress error output as well
pass pass
class Server(HTTPServer):
# This pernicious flag is on by default in HTTPServer. But proper
# operation of freeport() absolutely depends on it being off.
allow_reuse_address = False
if __name__ == "__main__": if __name__ == "__main__":
# Instantiate an HTTPServer(TestHTTPRequestHandler) on the first free port # Instantiate a Server(TestHTTPRequestHandler) on the first free port
# in the specified port range. Doing this inline is better than in a # in the specified port range. Doing this inline is better than in a
# daemon thread: if it blows up here, we'll get a traceback. If it blew up # daemon thread: if it blows up here, we'll get a traceback. If it blew up
# in some other thread, the traceback would get eaten and we'd run the # in some other thread, the traceback would get eaten and we'd run the
# subject test program anyway. # subject test program anyway.
httpd, port = freeport(xrange(8000, 8020), httpd, port = freeport(xrange(8000, 8020),
lambda port: HTTPServer(('127.0.0.1', port), TestHTTPRequestHandler)) lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler))
# Pass the selected port number to the subject test program via the # Pass the selected port number to the subject test program via the
# environment. We don't want to impose requirements on the test program's # environment. We don't want to impose requirements on the test program's
# command-line parsing -- and anyway, for C++ integration tests, that's # command-line parsing -- and anyway, for C++ integration tests, that's
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
$/LicenseInfo$ $/LicenseInfo$
""" """
from __future__ import with_statement
import os import os
import sys import sys
import re import re
...@@ -79,9 +81,14 @@ def freeport(portlist, expr): ...@@ -79,9 +81,14 @@ def freeport(portlist, expr):
Example: Example:
class Server(HTTPServer):
# If you use BaseHTTPServer.HTTPServer, turning off this flag is
# essential for proper operation of freeport()!
allow_reuse_address = False
# ...
server, port = freeport(xrange(8000, 8010), server, port = freeport(xrange(8000, 8010),
lambda port: HTTPServer(("localhost", port), lambda port: Server(("localhost", port),
MyRequestHandler)) MyRequestHandler))
# pass 'port' to client code # pass 'port' to client code
# call server.serve_forever() # call server.serve_forever()
""" """
...@@ -164,3 +171,92 @@ def run(*args, **kwds): ...@@ -164,3 +171,92 @@ def run(*args, **kwds):
rc = os.spawnv(os.P_WAIT, args[0], args) rc = os.spawnv(os.P_WAIT, args[0], args)
debug("%s returned %s", args[0], rc) debug("%s returned %s", args[0], rc)
return rc return rc
# ****************************************************************************
# test code -- manual at this point, see SWAT-564
# ****************************************************************************
def test_freeport():
# ------------------------------- Helpers --------------------------------
from contextlib import contextmanager
# helper Context Manager for expecting an exception
# with exc(SomeError):
# raise SomeError()
# raises AssertionError otherwise.
@contextmanager
def exc(exception_class, *args):
try:
yield
except exception_class, err:
for i, expected_arg in enumerate(args):
assert expected_arg == err.args[i], \
"Raised %s, but args[%s] is %r instead of %r" % \
(err.__class__.__name__, i, err.args[i], expected_arg)
print "Caught expected exception %s(%s)" % \
(err.__class__.__name__, ', '.join(repr(arg) for arg in err.args))
else:
assert False, "Failed to raise " + exception_class.__class__.__name__
# helper to raise specified exception
def raiser(exception):
raise exception
# the usual
def assert_equals(a, b):
assert a == b, "%r != %r" % (a, b)
# ------------------------ Sanity check the above ------------------------
class SomeError(Exception): pass
# Without extra args, accept any err.args value
with exc(SomeError):
raiser(SomeError("abc"))
# With extra args, accept only the specified value
with exc(SomeError, "abc"):
raiser(SomeError("abc"))
with exc(AssertionError):
with exc(SomeError, "abc"):
raiser(SomeError("def"))
with exc(AssertionError):
with exc(socket.error, errno.EADDRINUSE):
raiser(socket.error(errno.ECONNREFUSED, 'Connection refused'))
# ----------- freeport() without engaging socket functionality -----------
# If portlist is empty, freeport() raises StopIteration.
with exc(StopIteration):
freeport([], None)
assert_equals(freeport([17], str), ("17", 17))
# This is the magic exception that should prompt us to retry
inuse = socket.error(errno.EADDRINUSE, 'Address already in use')
# Get the iterator to our ports list so we can check later if we've used all
ports = iter(xrange(5))
with exc(socket.error, errno.EADDRINUSE):
freeport(ports, lambda port: raiser(inuse))
# did we entirely exhaust 'ports'?
with exc(StopIteration):
ports.next()
ports = iter(xrange(2))
# Any exception but EADDRINUSE should quit immediately
with exc(SomeError):
freeport(ports, lambda port: raiser(SomeError()))
assert_equals(ports.next(), 1)
# ----------- freeport() with platform-dependent socket stuff ------------
# This is what we should've had unit tests to begin with (see CHOP-661).
def newbind(port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', port))
return sock
bound0, port0 = freeport(xrange(7777, 7780), newbind)
assert_equals(port0, 7777)
bound1, port1 = freeport(xrange(7777, 7780), newbind)
assert_equals(port1, 7778)
bound2, port2 = freeport(xrange(7777, 7780), newbind)
assert_equals(port2, 7779)
with exc(socket.error, errno.EADDRINUSE):
bound3, port3 = freeport(xrange(7777, 7780), newbind)
if __name__ == "__main__":
test_freeport()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment