From 8a2986932d8fc23b1d1c21ae05b35a4c47b373e5 Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Sun, 11 Jan 2015 11:21:42 -0500 Subject: [PATCH] Refactor connection-closed tests. On CI servers, port numbers get reused frequently, so a closed port cannot be relied upon to refuse connections for long. We now use an open client-side socket's ephemeral port, which will always refuse connections and cannot be reused for as log as the socket is open. --- tornado/test/iostream_test.py | 6 +++--- tornado/test/simple_httpclient_test.py | 8 ++++---- tornado/test/tcpclient_test.py | 8 ++++---- tornado/test/util.py | 20 ++++++++++++++++++++ 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/tornado/test/iostream_test.py b/tornado/test/iostream_test.py index f54eed6bb..b0e94fb20 100644 --- a/tornado/test/iostream_test.py +++ b/tornado/test/iostream_test.py @@ -8,7 +8,7 @@ from tornado.log import gen_log, app_log from tornado.netutil import ssl_wrap_socket from tornado.stack_context import NullContext from tornado.testing import AsyncHTTPTestCase, AsyncHTTPSTestCase, AsyncTestCase, bind_unused_port, ExpectLog, gen_test -from tornado.test.util import unittest, skipIfNonUnix +from tornado.test.util import unittest, skipIfNonUnix, refusing_port from tornado.web import RequestHandler, Application import certifi import errno @@ -217,8 +217,8 @@ class TestIOStreamMixin(object): # When a connection is refused, the connect callback should not # be run. (The kqueue IOLoop used to behave differently from the # epoll IOLoop in this respect) - server_socket, port = bind_unused_port() - server_socket.close() + cleanup_func, port = refusing_port() + self.addCleanup(cleanup_func) stream = IOStream(socket.socket(), self.io_loop) self.connect_called = False diff --git a/tornado/test/simple_httpclient_test.py b/tornado/test/simple_httpclient_test.py index 11cf05991..a059bc933 100644 --- a/tornado/test/simple_httpclient_test.py +++ b/tornado/test/simple_httpclient_test.py @@ -19,8 +19,8 @@ from tornado.netutil import Resolver, bind_sockets from tornado.simple_httpclient import SimpleAsyncHTTPClient, _default_ca_certs from tornado.test.httpclient_test import ChunkHandler, CountdownHandler, HelloWorldHandler from tornado.test import httpclient_test -from tornado.testing import AsyncHTTPTestCase, AsyncHTTPSTestCase, AsyncTestCase, bind_unused_port, ExpectLog -from tornado.test.util import skipOnTravis, skipIfNoIPv6 +from tornado.testing import AsyncHTTPTestCase, AsyncHTTPSTestCase, AsyncTestCase, ExpectLog +from tornado.test.util import skipOnTravis, skipIfNoIPv6, refusing_port from tornado.web import RequestHandler, Application, asynchronous, url, stream_request_body @@ -322,8 +322,8 @@ class SimpleHTTPClientTestMixin(object): self.assertTrue(host_re.match(response.body), response.body) def test_connection_refused(self): - server_socket, port = bind_unused_port() - server_socket.close() + cleanup_func, port = refusing_port() + self.addCleanup(cleanup_func) with ExpectLog(gen_log, ".*", required=False): self.http_client.fetch("http://localhost:%d/" % port, self.stop) response = self.wait() diff --git a/tornado/test/tcpclient_test.py b/tornado/test/tcpclient_test.py index 5df4a7abb..1a4201e6b 100644 --- a/tornado/test/tcpclient_test.py +++ b/tornado/test/tcpclient_test.py @@ -24,8 +24,8 @@ from tornado.concurrent import Future from tornado.netutil import bind_sockets, Resolver from tornado.tcpclient import TCPClient, _Connector from tornado.tcpserver import TCPServer -from tornado.testing import AsyncTestCase, bind_unused_port, gen_test -from tornado.test.util import skipIfNoIPv6, unittest +from tornado.testing import AsyncTestCase, gen_test +from tornado.test.util import skipIfNoIPv6, unittest, refusing_port # Fake address families for testing. Used in place of AF_INET # and AF_INET6 because some installations do not have AF_INET6. @@ -120,8 +120,8 @@ class TCPClientTest(AsyncTestCase): @gen_test def test_refused_ipv4(self): - sock, port = bind_unused_port() - sock.close() + cleanup_func, port = refusing_port() + self.addCleanup(cleanup_func) with self.assertRaises(IOError): yield self.client.connect('127.0.0.1', port) diff --git a/tornado/test/util.py b/tornado/test/util.py index d31bbba33..4f0c07e5e 100644 --- a/tornado/test/util.py +++ b/tornado/test/util.py @@ -4,6 +4,8 @@ import os import socket import sys +from tornado.testing import bind_unused_port + # Encapsulate the choice of unittest or unittest2 here. # To be used as 'from tornado.test.util import unittest'. if sys.version_info < (2, 7): @@ -28,3 +30,21 @@ skipIfNoNetwork = unittest.skipIf('NO_NETWORK' in os.environ, 'network access disabled') skipIfNoIPv6 = unittest.skipIf(not socket.has_ipv6, 'ipv6 support not present') + +def refusing_port(): + """Returns a local port number that will refuse all connections. + + Return value is (cleanup_func, port); the cleanup function + must be called to free the port to be reused. + """ + # On travis-ci, port numbers are reassigned frequently. To avoid + # collisions with other tests, we use an open client-side socket's + # ephemeral port number to ensure that nothing can listen on that + # port. + server_socket, port = bind_unused_port() + client_socket = socket.socket() + client_socket.connect(("127.0.0.1", port)) + conn, client_addr = server_socket.accept() + conn.close() + server_socket.close() + return (client_socket.close, client_addr[1]) -- 2.47.2