1
# Copyright (C) 2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25
from bzrlib.tests import test_server
28
def load_tests(basic_tests, module, loader):
29
suite = loader.suiteClass()
30
server_tests, remaining_tests = tests.split_suite_by_condition(
31
basic_tests, tests.condition_isinstance(TestTCPServerInAThread))
32
server_scenarios = [ (name, {'server_class': getattr(test_server, name)})
34
('TestingTCPServer', 'TestingThreadingTCPServer')]
35
tests.multiply_tests(server_tests, server_scenarios, suite)
36
suite.addTest(remaining_tests)
40
class TCPClient(object):
45
def connect(self, addr):
46
if self.sock is not None:
47
raise AssertionError('Already connected to %r'
48
% (self.sock.getsockname(),))
49
self.sock = osutils.connect_socket(addr)
52
if self.sock is not None:
54
self.sock.shutdown(socket.SHUT_RDWR)
56
except socket.error, e:
57
if e[0] in (errno.EBADF, errno.ENOTCONN):
58
# Right, the socket is already down
65
return self.sock.sendall(s)
67
def read(self, bufsize=4096):
68
return self.sock.recv(bufsize)
71
class TCPConnectionHandler(SocketServer.StreamRequestHandler):
75
self.handle_connection()
77
self.handle_connection()
79
def handle_connection(self):
80
req = self.rfile.readline()
84
self.wfile.write('pong\n')
86
raise ValueError('[%s] not understood' % req)
89
class TestTCPServerInAThread(tests.TestCase):
94
def get_server(self, server_class=None, connection_handler_class=None):
95
if server_class is not None:
96
self.server_class = server_class
97
if connection_handler_class is None:
98
connection_handler_class = TCPConnectionHandler
99
server = test_server.TestingTCPServerInAThread(
100
('localhost', 0), self.server_class, connection_handler_class)
101
server.start_server()
102
self.addCleanup(server.stop_server)
105
def get_client(self):
107
self.addCleanup(client.disconnect)
110
def get_server_connection(self, server, conn_rank):
111
return server.server.clients[conn_rank]
113
def assertClientAddr(self, client, server, conn_rank):
114
conn = self.get_server_connection(server, conn_rank)
115
self.assertEquals(client.sock.getsockname(), conn[1])
117
def test_start_stop(self):
118
server = self.get_server()
119
client = self.get_client()
120
client.connect((server.host, server.port))
122
# since the server doesn't accept connections anymore attempting to
123
# connect should fail
124
client = self.get_client()
125
self.assertRaises(socket.error,
126
client.connect, (server.host, server.port))
128
def test_client_talks_server_respond(self):
129
server = self.get_server()
130
client = self.get_client()
131
client.connect((server.host, server.port))
132
self.assertIs(None, client.write('ping\n'))
134
self.assertClientAddr(client, server, 0)
135
self.assertEquals('pong\n', resp)
137
def test_server_fails_to_start(self):
138
class CantStart(Exception):
141
class CantStartServer(test_server.TestingTCPServer):
143
def server_bind(self):
146
# The exception is raised in the main thread
147
self.assertRaises(CantStart,
148
self.get_server, server_class=CantStartServer)
150
def test_server_fails_while_serving_or_stoping(self):
151
class ServerFailure(Exception):
154
class FailingConnectionHandler(TCPConnectionHandler):
157
raise ServerFailure()
159
server = self.get_server(
160
connection_handler_class=FailingConnectionHandler)
161
# The server won't fail until a client connect
162
client = self.get_client()
163
client.connect((server.host, server.port))
165
# Now we must force the server to answer by sending the request and
166
# waiting for some answer. But since we don't control when the
167
# server thread will be given cycles, we don't control either
168
# whether our reads or writes may hang.
169
client.sock.settimeout(0.1)
170
client.write('ping\n')
174
# Now the server has raise the exception in its own thread
175
self.assertRaises(ServerFailure, server.stop_server)