Speed test_socketserver up from 28.739s to 0.226s, simplify the logic, and make
sure all tests run even if some fail.
This commit is contained in:
parent
960b9b7a2f
commit
180997b2bb
|
@ -2,13 +2,15 @@
|
|||
Test suite for SocketServer.py.
|
||||
"""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import errno
|
||||
import imp
|
||||
import os
|
||||
import select
|
||||
import time
|
||||
import signal
|
||||
import socket
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
import SocketServer
|
||||
|
||||
|
@ -19,7 +21,6 @@ from test.test_support import TESTFN as TEST_FILE
|
|||
test.test_support.requires("network")
|
||||
|
||||
NREQ = 3
|
||||
DELAY = 0.5
|
||||
TEST_STR = "hello world\n"
|
||||
HOST = "localhost"
|
||||
|
||||
|
@ -27,14 +28,6 @@ HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX")
|
|||
HAVE_FORKING = hasattr(os, "fork") and os.name != "os2"
|
||||
|
||||
|
||||
class MyMixinHandler:
|
||||
def handle(self):
|
||||
time.sleep(DELAY)
|
||||
line = self.rfile.readline()
|
||||
time.sleep(DELAY)
|
||||
self.wfile.write(line)
|
||||
|
||||
|
||||
def receive(sock, n, timeout=20):
|
||||
r, w, x = select.select([sock], [], [], timeout)
|
||||
if sock in r:
|
||||
|
@ -42,14 +35,6 @@ def receive(sock, n, timeout=20):
|
|||
else:
|
||||
raise RuntimeError, "timed out on %r" % (sock,)
|
||||
|
||||
|
||||
class MyStreamHandler(MyMixinHandler, SocketServer.StreamRequestHandler):
|
||||
pass
|
||||
|
||||
class MyDatagramHandler(MyMixinHandler,
|
||||
SocketServer.DatagramRequestHandler):
|
||||
pass
|
||||
|
||||
if HAVE_UNIX_SOCKETS:
|
||||
class ForkingUnixStreamServer(SocketServer.ForkingMixIn,
|
||||
SocketServer.UnixStreamServer):
|
||||
|
@ -84,48 +69,28 @@ class ServerThread(threading.Thread):
|
|||
pass
|
||||
if verbose: print "thread: creating server"
|
||||
svr = svrcls(self.__addr, self.__hdlrcls)
|
||||
# pull the address out of the server in case it changed
|
||||
# this can happen if another process is using the port
|
||||
addr = svr.server_address
|
||||
if addr:
|
||||
self.__addr = addr
|
||||
if self.__addr != svr.socket.getsockname():
|
||||
raise RuntimeError('server_address was %s, expected %s' %
|
||||
(self.__addr, svr.socket.getsockname()))
|
||||
# We had the OS pick a port, so pull the real address out of
|
||||
# the server.
|
||||
self.addr = svr.server_address
|
||||
self.port = self.addr[1]
|
||||
if self.addr != svr.socket.getsockname():
|
||||
raise RuntimeError('server_address was %s, expected %s' %
|
||||
(self.addr, svr.socket.getsockname()))
|
||||
self.ready.set()
|
||||
if verbose: print "thread: serving three times"
|
||||
svr.serve_a_few()
|
||||
if verbose: print "thread: done"
|
||||
|
||||
|
||||
class ForgivingTCPServer(SocketServer.TCPServer):
|
||||
# prevent errors if another process is using the port we want
|
||||
def server_bind(self):
|
||||
host, default_port = self.server_address
|
||||
# this code shamelessly stolen from test.test_support
|
||||
# the ports were changed to protect the innocent
|
||||
import sys
|
||||
for port in [default_port, 3434, 8798, 23833]:
|
||||
try:
|
||||
self.server_address = host, port
|
||||
SocketServer.TCPServer.server_bind(self)
|
||||
break
|
||||
except socket.error, (err, msg):
|
||||
if err != errno.EADDRINUSE:
|
||||
raise
|
||||
print >> sys.__stderr__, \
|
||||
"WARNING: failed to listen on port %d, trying another: " % port
|
||||
|
||||
|
||||
class SocketServerTest(unittest.TestCase):
|
||||
"""Test all socket servers."""
|
||||
|
||||
def setUp(self):
|
||||
signal.alarm(20) # Kill deadlocks after 20 seconds.
|
||||
self.port_seed = 0
|
||||
self.test_files = []
|
||||
|
||||
def tearDown(self):
|
||||
time.sleep(DELAY)
|
||||
reap_children()
|
||||
|
||||
for fn in self.test_files:
|
||||
|
@ -134,16 +99,18 @@ class SocketServerTest(unittest.TestCase):
|
|||
except os.error:
|
||||
pass
|
||||
self.test_files[:] = []
|
||||
|
||||
def pickport(self):
|
||||
self.port_seed += 1
|
||||
return 10000 + (os.getpid() % 1000)*10 + self.port_seed
|
||||
signal.alarm(0) # Didn't deadlock.
|
||||
|
||||
def pickaddr(self, proto):
|
||||
if proto == socket.AF_INET:
|
||||
return (HOST, self.pickport())
|
||||
return (HOST, 0)
|
||||
else:
|
||||
fn = TEST_FILE + str(self.pickport())
|
||||
# XXX: We need a way to tell AF_UNIX to pick its own name
|
||||
# like AF_INET provides port==0.
|
||||
dir = None
|
||||
if os.name == 'os2':
|
||||
dir = '\socket'
|
||||
fn = tempfile.mktemp(prefix='unix_socket.', dir=dir)
|
||||
if os.name == 'os2':
|
||||
# AF_UNIX socket names on OS/2 require a specific prefix
|
||||
# which can't include a drive letter and must also use
|
||||
|
@ -152,7 +119,6 @@ class SocketServerTest(unittest.TestCase):
|
|||
fn = fn[2:]
|
||||
if fn[0] in (os.sep, os.altsep):
|
||||
fn = fn[1:]
|
||||
fn = os.path.join('\socket', fn)
|
||||
if os.sep == '/':
|
||||
fn = fn.replace(os.sep, os.altsep)
|
||||
else:
|
||||
|
@ -160,25 +126,30 @@ class SocketServerTest(unittest.TestCase):
|
|||
self.test_files.append(fn)
|
||||
return fn
|
||||
|
||||
def run_servers(self, proto, servers, hdlrcls, testfunc):
|
||||
for svrcls in servers:
|
||||
addr = self.pickaddr(proto)
|
||||
if verbose:
|
||||
print "ADDR =", addr
|
||||
print "CLASS =", svrcls
|
||||
t = ServerThread(addr, svrcls, hdlrcls)
|
||||
if verbose: print "server created"
|
||||
t.start()
|
||||
if verbose: print "server running"
|
||||
for i in range(NREQ):
|
||||
t.ready.wait(10*DELAY)
|
||||
self.assert_(t.ready.isSet(),
|
||||
"Server not ready within a reasonable time")
|
||||
if verbose: print "test client", i
|
||||
testfunc(proto, addr)
|
||||
if verbose: print "waiting for server"
|
||||
t.join()
|
||||
if verbose: print "done"
|
||||
def run_server(self, svrcls, hdlrbase, testfunc):
|
||||
class MyHandler(hdlrbase):
|
||||
def handle(self):
|
||||
line = self.rfile.readline()
|
||||
self.wfile.write(line)
|
||||
|
||||
addr = self.pickaddr(svrcls.address_family)
|
||||
if verbose:
|
||||
print "ADDR =", addr
|
||||
print "CLASS =", svrcls
|
||||
t = ServerThread(addr, svrcls, MyHandler)
|
||||
if verbose: print "server created"
|
||||
t.start()
|
||||
if verbose: print "server running"
|
||||
t.ready.wait(10)
|
||||
self.assert_(t.ready.isSet(),
|
||||
"%s not ready within a reasonable time" % svrcls)
|
||||
addr = t.addr
|
||||
for i in range(NREQ):
|
||||
if verbose: print "test client", i
|
||||
testfunc(svrcls.address_family, addr)
|
||||
if verbose: print "waiting for server"
|
||||
t.join()
|
||||
if verbose: print "done"
|
||||
|
||||
def stream_examine(self, proto, addr):
|
||||
s = socket.socket(proto, socket.SOCK_STREAM)
|
||||
|
@ -201,47 +172,74 @@ class SocketServerTest(unittest.TestCase):
|
|||
self.assertEquals(buf, TEST_STR)
|
||||
s.close()
|
||||
|
||||
def test_TCPServers(self):
|
||||
# Test SocketServer.TCPServer
|
||||
servers = [ForgivingTCPServer, SocketServer.ThreadingTCPServer]
|
||||
if HAVE_FORKING:
|
||||
servers.append(SocketServer.ForkingTCPServer)
|
||||
self.run_servers(socket.AF_INET, servers,
|
||||
MyStreamHandler, self.stream_examine)
|
||||
def test_TCPServer(self):
|
||||
self.run_server(SocketServer.TCPServer,
|
||||
SocketServer.StreamRequestHandler,
|
||||
self.stream_examine)
|
||||
|
||||
def test_UDPServers(self):
|
||||
# Test SocketServer.UDPServer
|
||||
servers = [SocketServer.UDPServer,
|
||||
SocketServer.ThreadingUDPServer]
|
||||
if HAVE_FORKING:
|
||||
servers.append(SocketServer.ForkingUDPServer)
|
||||
self.run_servers(socket.AF_INET, servers, MyDatagramHandler,
|
||||
self.dgram_examine)
|
||||
def test_ThreadingTCPServer(self):
|
||||
self.run_server(SocketServer.ThreadingTCPServer,
|
||||
SocketServer.StreamRequestHandler,
|
||||
self.stream_examine)
|
||||
|
||||
if HAVE_FORKING:
|
||||
def test_ThreadingTCPServer(self):
|
||||
self.run_server(SocketServer.ForkingTCPServer,
|
||||
SocketServer.StreamRequestHandler,
|
||||
self.stream_examine)
|
||||
|
||||
if HAVE_UNIX_SOCKETS:
|
||||
def test_UnixStreamServer(self):
|
||||
self.run_server(SocketServer.UnixStreamServer,
|
||||
SocketServer.StreamRequestHandler,
|
||||
self.stream_examine)
|
||||
|
||||
def test_ThreadingUnixStreamServer(self):
|
||||
self.run_server(SocketServer.ThreadingUnixStreamServer,
|
||||
SocketServer.StreamRequestHandler,
|
||||
self.stream_examine)
|
||||
|
||||
def test_stream_servers(self):
|
||||
# Test SocketServer's stream servers
|
||||
if not HAVE_UNIX_SOCKETS:
|
||||
return
|
||||
servers = [SocketServer.UnixStreamServer,
|
||||
SocketServer.ThreadingUnixStreamServer]
|
||||
if HAVE_FORKING:
|
||||
servers.append(ForkingUnixStreamServer)
|
||||
self.run_servers(socket.AF_UNIX, servers, MyStreamHandler,
|
||||
self.stream_examine)
|
||||
def test_ForkingUnixStreamServer(self):
|
||||
self.run_server(ForkingUnixStreamServer,
|
||||
SocketServer.StreamRequestHandler,
|
||||
self.stream_examine)
|
||||
|
||||
def test_UDPServer(self):
|
||||
self.run_server(SocketServer.UDPServer,
|
||||
SocketServer.DatagramRequestHandler,
|
||||
self.dgram_examine)
|
||||
|
||||
def test_ThreadingUDPServer(self):
|
||||
self.run_server(SocketServer.ThreadingUDPServer,
|
||||
SocketServer.DatagramRequestHandler,
|
||||
self.dgram_examine)
|
||||
|
||||
if HAVE_FORKING:
|
||||
def test_ForkingUDPServer(self):
|
||||
self.run_server(SocketServer.ForkingUDPServer,
|
||||
SocketServer.DatagramRequestHandler,
|
||||
self.dgram_examine)
|
||||
|
||||
# Alas, on Linux (at least) recvfrom() doesn't return a meaningful
|
||||
# client address so this cannot work:
|
||||
|
||||
# def test_dgram_servers(self):
|
||||
# # Test SocketServer.UnixDatagramServer
|
||||
# if not HAVE_UNIX_SOCKETS:
|
||||
# return
|
||||
# servers = [SocketServer.UnixDatagramServer,
|
||||
# SocketServer.ThreadingUnixDatagramServer]
|
||||
# if HAVE_UNIX_SOCKETS:
|
||||
# def test_UnixDatagramServer(self):
|
||||
# self.run_server(SocketServer.UnixDatagramServer,
|
||||
# SocketServer.DatagramRequestHandler,
|
||||
# self.dgram_examine)
|
||||
#
|
||||
# def test_ThreadingUnixDatagramServer(self):
|
||||
# self.run_server(SocketServer.ThreadingUnixDatagramServer,
|
||||
# SocketServer.DatagramRequestHandler,
|
||||
# self.dgram_examine)
|
||||
#
|
||||
# if HAVE_FORKING:
|
||||
# servers.append(ForkingUnixDatagramServer)
|
||||
# self.run_servers(socket.AF_UNIX, servers, MyDatagramHandler,
|
||||
# self.dgram_examine)
|
||||
# def test_ForkingUnixDatagramServer(self):
|
||||
# self.run_server(SocketServer.ForkingUnixDatagramServer,
|
||||
# SocketServer.DatagramRequestHandler,
|
||||
# self.dgram_examine)
|
||||
|
||||
|
||||
def test_main():
|
||||
|
@ -253,3 +251,4 @@ def test_main():
|
|||
|
||||
if __name__ == "__main__":
|
||||
test_main()
|
||||
signal.alarm(3) # Shutdown shouldn't take more than 3 seconds.
|
||||
|
|
Loading…
Reference in New Issue