~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_test_server.py

  • Committer: Patch Queue Manager
  • Date: 2011-09-08 11:01:15 UTC
  • mfrom: (6123.1.16 gpg-typo)
  • Revision ID: pqm@cupuasso-20110908110115-gbb9benwkdksvzk5
(jelmer) Fix a typo (invalid format identifier) in an error message in
 bzrlib.gpg. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
import SocketServer
20
20
import threading
21
21
 
22
 
 
23
22
from bzrlib import (
24
23
    osutils,
25
24
    tests,
31
30
load_tests = load_tests_apply_scenarios
32
31
 
33
32
 
34
 
def portable_socket_pair():
35
 
    """Return a pair of TCP sockets connected to each other.
36
 
 
37
 
    Unlike socket.socketpair, this should work on Windows.
38
 
    """
39
 
    listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
40
 
    listen_sock.bind(('127.0.0.1', 0))
41
 
    listen_sock.listen(1)
42
 
    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
43
 
    client_sock.connect(listen_sock.getsockname())
44
 
    server_sock, addr = listen_sock.accept()
45
 
    listen_sock.close()
46
 
    return server_sock, client_sock
47
 
 
48
 
 
49
33
class TCPClient(object):
50
34
 
51
35
    def __init__(self):
77
61
        return self.sock.recv(bufsize)
78
62
 
79
63
 
80
 
class TCPConnectionHandler(SocketServer.BaseRequestHandler):
 
64
class TCPConnectionHandler(SocketServer.StreamRequestHandler):
81
65
 
82
66
    def handle(self):
83
67
        self.done = False
85
69
        while not self.done:
86
70
            self.handle_connection()
87
71
 
88
 
    def readline(self):
89
 
        # TODO: We should be buffering any extra data sent, etc. However, in
90
 
        #       practice, we don't send extra content, so we haven't bothered
91
 
        #       to implement it yet.
92
 
        req = self.request.recv(4096)
93
 
        # An empty string is allowed, to indicate the end of the connection
94
 
        if not req or (req.endswith('\n') and req.count('\n') == 1):
95
 
            return req
96
 
        raise ValueError('[%r] not a simple line' % (req,))
97
 
 
98
72
    def handle_connection(self):
99
 
        req = self.readline()
 
73
        req = self.rfile.readline()
100
74
        if not req:
101
75
            self.done = True
102
76
        elif req == 'ping\n':
103
 
            self.request.sendall('pong\n')
 
77
            self.wfile.write('pong\n')
104
78
        else:
105
79
            raise ValueError('[%s] not understood' % req)
106
80
 
107
81
 
108
82
class TestTCPServerInAThread(tests.TestCase):
109
83
 
110
 
    scenarios = [
 
84
    scenarios = [ 
111
85
        (name, {'server_class': getattr(test_server, name)})
112
86
        for name in
113
87
        ('TestingTCPServer', 'TestingThreadingTCPServer')]
120
94
            self.server_class = server_class
121
95
        if connection_handler_class is None:
122
96
            connection_handler_class = TCPConnectionHandler
123
 
        server = test_server.TestingTCPServerInAThread(
 
97
        server =  test_server.TestingTCPServerInAThread(
124
98
            ('localhost', 0), self.server_class, connection_handler_class)
125
99
        server.start_server()
126
100
        self.addCleanup(server.stop_server)
184
158
        # The server won't fail until a client connect
185
159
        client = self.get_client()
186
160
        client.connect((server.host, server.port))
187
 
        # We make sure the server wants to handle a request, but the request is
188
 
        # guaranteed to fail. However, the server should make sure that the
189
 
        # connection gets closed, and stop_server should then raise the
190
 
        # original exception.
191
 
        client.write('ping\n')
192
161
        try:
193
 
            self.assertEqual('', client.read())
194
 
        except socket.error, e:
195
 
            # On Windows, failing during 'handle' means we get
196
 
            # 'forced-close-of-connection'. Possibly because we haven't
197
 
            # processed the write request before we close the socket.
198
 
            WSAECONNRESET = 10054
199
 
            if e.errno in (WSAECONNRESET,):
200
 
                pass
 
162
            # Now we must force the server to answer by sending the request and
 
163
            # waiting for some answer. But since we don't control when the
 
164
            # server thread will be given cycles, we don't control either
 
165
            # whether our reads or writes may hang.
 
166
            client.sock.settimeout(0.1)
 
167
            client.write('ping\n')
 
168
            client.read()
 
169
        except socket.error:
 
170
            pass
201
171
        # Now the server has raised the exception in its own thread
202
172
        self.assertRaises(CantConnect, server.stop_server)
203
173
 
210
180
        class FailingDuringResponseHandler(TCPConnectionHandler):
211
181
 
212
182
            def handle_connection(self):
213
 
                req = self.readline()
 
183
                req = self.rfile.readline()
214
184
                threading.currentThread().set_sync_event(sync)
215
185
                raise FailToRespond()
216
186
 
220
190
        client.connect((server.host, server.port))
221
191
        client.write('ping\n')
222
192
        sync.wait()
223
 
        self.assertEqual('', client.read()) # connection closed
224
193
        self.assertRaises(FailToRespond, server.pending_exception)
225
194
 
226
195
    def test_exception_swallowed_while_serving(self):
246
215
        client.connect((server.host, server.port))
247
216
        # Wait for the exception to propagate.
248
217
        sync.wait()
249
 
        self.assertEqual('', client.read()) # connection closed
250
218
        # The connection wasn't served properly but the exception should have
251
219
        # been swallowed.
252
220
        server.pending_exception()
253
 
 
254
 
    def test_handle_request_closes_if_it_doesnt_process(self):
255
 
        server = self.get_server()
256
 
        client = self.get_client()
257
 
        server.server.serving = False
258
 
        client.connect((server.host, server.port))
259
 
        self.assertEqual('', client.read())
260
 
 
261
 
 
262
 
class TestTestingSmartServer(tests.TestCase):
263
 
 
264
 
    def test_sets_client_timeout(self):
265
 
        server = test_server.TestingSmartServer(('localhost', 0), None, None,
266
 
            root_client_path='/no-such-client/path')
267
 
        self.assertEqual(test_server._DEFAULT_TESTING_CLIENT_TIMEOUT,
268
 
                         server._client_timeout)
269
 
        sock = socket.socket()
270
 
        h = server._make_handler(sock)
271
 
        self.assertEqual(test_server._DEFAULT_TESTING_CLIENT_TIMEOUT,
272
 
                         h._client_timeout)
273
 
 
274
 
 
275
 
class FakeServer(object):
276
 
    """Minimal implementation to pass to TestingSmartConnectionHandler"""
277
 
    backing_transport = None
278
 
    root_client_path = '/'
279
 
 
280
 
 
281
 
class TestTestingSmartConnectionHandler(tests.TestCase):
282
 
 
283
 
    def test_connection_timeout_suppressed(self):
284
 
        self.overrideAttr(test_server, '_DEFAULT_TESTING_CLIENT_TIMEOUT', 0.01)
285
 
        s = FakeServer()
286
 
        server_sock, client_sock = portable_socket_pair()
287
 
        # This should timeout quickly, but not generate an exception.
288
 
        handler = test_server.TestingSmartConnectionHandler(server_sock,
289
 
            server_sock.getpeername(), s)
290
 
 
291
 
    def test_connection_shutdown_while_serving_no_error(self):
292
 
        s = FakeServer()
293
 
        server_sock, client_sock = portable_socket_pair()
294
 
        class ShutdownConnectionHandler(
295
 
            test_server.TestingSmartConnectionHandler):
296
 
 
297
 
            def _build_protocol(self):
298
 
                self.finished = True
299
 
                return super(ShutdownConnectionHandler, self)._build_protocol()
300
 
        # This should trigger shutdown after the entering _build_protocol, and
301
 
        # we should exit cleanly, without raising an exception.
302
 
        handler = ShutdownConnectionHandler(server_sock,
303
 
            server_sock.getpeername(), s)