/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2010, 2011 Canonical Ltd
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
2
#
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.
7
#
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.
12
#
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
16
5247.3.10 by Vincent Ladeuil
Test errors during server life.
17
import errno
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
18
import socket
19
import SocketServer
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
20
import threading
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
21
22
from bzrlib import (
23
    osutils,
24
    tests,
25
    )
26
from bzrlib.tests import test_server
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
27
from bzrlib.tests.scenarios import load_tests_apply_scenarios
28
29
30
load_tests = load_tests_apply_scenarios
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
31
32
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
33
class TCPClient(object):
34
35
    def __init__(self):
36
        self.sock = None
37
38
    def connect(self, addr):
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
39
        if self.sock is not None:
40
            raise AssertionError('Already connected to %r'
41
                                 % (self.sock.getsockname(),))
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
42
        self.sock = osutils.connect_socket(addr)
43
44
    def disconnect(self):
45
        if self.sock is not None:
5247.3.10 by Vincent Ladeuil
Test errors during server life.
46
            try:
47
                self.sock.shutdown(socket.SHUT_RDWR)
48
                self.sock.close()
49
            except socket.error, e:
50
                if e[0] in (errno.EBADF, errno.ENOTCONN):
51
                    # Right, the socket is already down
52
                    pass
53
                else:
54
                    raise
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
55
            self.sock = None
56
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
57
    def write(self, s):
58
        return self.sock.sendall(s)
59
60
    def read(self, bufsize=4096):
61
        return self.sock.recv(bufsize)
62
63
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
64
class TCPConnectionHandler(SocketServer.StreamRequestHandler):
65
66
    def handle(self):
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
67
        self.done = False
68
        self.handle_connection()
69
        while not self.done:
70
            self.handle_connection()
71
72
    def handle_connection(self):
73
        req = self.rfile.readline()
74
        if not req:
75
            self.done = True
76
        elif req == 'ping\n':
77
            self.wfile.write('pong\n')
78
        else:
79
            raise ValueError('[%s] not understood' % req)
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
80
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
81
82
class TestTCPServerInAThread(tests.TestCase):
83
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
84
    scenarios = [ 
85
        (name, {'server_class': getattr(test_server, name)})
86
        for name in
87
        ('TestingTCPServer', 'TestingThreadingTCPServer')]
88
5247.3.10 by Vincent Ladeuil
Test errors during server life.
89
    def get_server(self, server_class=None, connection_handler_class=None):
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
90
        if server_class is not None:
91
            self.server_class = server_class
5247.3.10 by Vincent Ladeuil
Test errors during server life.
92
        if connection_handler_class is None:
93
            connection_handler_class = TCPConnectionHandler
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
94
        server =  test_server.TestingTCPServerInAThread(
95
            ('localhost', 0), self.server_class, connection_handler_class)
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
96
        server.start_server()
97
        self.addCleanup(server.stop_server)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
98
        return server
99
100
    def get_client(self):
101
        client = TCPClient()
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
102
        self.addCleanup(client.disconnect)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
103
        return client
104
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
105
    def get_server_connection(self, server, conn_rank):
106
        return server.server.clients[conn_rank]
107
108
    def assertClientAddr(self, client, server, conn_rank):
109
        conn = self.get_server_connection(server, conn_rank)
110
        self.assertEquals(client.sock.getsockname(), conn[1])
111
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
112
    def test_start_stop(self):
113
        server = self.get_server()
114
        client = self.get_client()
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
115
        server.stop_server()
116
        # since the server doesn't accept connections anymore attempting to
117
        # connect should fail
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
118
        client = self.get_client()
5247.3.18 by Vincent Ladeuil
Fix some fallouts from previous fixes, all tests passing (no more http leaks).
119
        self.assertRaises(socket.error,
120
                          client.connect, (server.host, server.port))
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
121
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
122
    def test_client_talks_server_respond(self):
123
        server = self.get_server()
124
        client = self.get_client()
5247.3.18 by Vincent Ladeuil
Fix some fallouts from previous fixes, all tests passing (no more http leaks).
125
        client.connect((server.host, server.port))
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
126
        self.assertIs(None, client.write('ping\n'))
127
        resp = client.read()
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
128
        self.assertClientAddr(client, server, 0)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
129
        self.assertEquals('pong\n', resp)
5247.3.10 by Vincent Ladeuil
Test errors during server life.
130
131
    def test_server_fails_to_start(self):
132
        class CantStart(Exception):
133
            pass
134
135
        class CantStartServer(test_server.TestingTCPServer):
136
137
            def server_bind(self):
138
                raise CantStart()
139
140
        # The exception is raised in the main thread
141
        self.assertRaises(CantStart,
142
                          self.get_server, server_class=CantStartServer)
143
5247.5.10 by Vincent Ladeuil
Fix broken test.
144
    def test_server_fails_while_serving_or_stopping(self):
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
145
        class CantConnect(Exception):
5247.3.10 by Vincent Ladeuil
Test errors during server life.
146
            pass
147
148
        class FailingConnectionHandler(TCPConnectionHandler):
149
150
            def handle(self):
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
151
                raise CantConnect()
5247.3.10 by Vincent Ladeuil
Test errors during server life.
152
153
        server = self.get_server(
154
            connection_handler_class=FailingConnectionHandler)
155
        # The server won't fail until a client connect
156
        client = self.get_client()
5247.3.18 by Vincent Ladeuil
Fix some fallouts from previous fixes, all tests passing (no more http leaks).
157
        client.connect((server.host, server.port))
5247.3.10 by Vincent Ladeuil
Test errors during server life.
158
        try:
159
            # Now we must force the server to answer by sending the request and
160
            # waiting for some answer. But since we don't control when the
161
            # server thread will be given cycles, we don't control either
162
            # whether our reads or writes may hang.
163
            client.sock.settimeout(0.1)
164
            client.write('ping\n')
165
            client.read()
166
        except socket.error:
167
            pass
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
168
        # Now the server has raised the exception in its own thread
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
169
        self.assertRaises(CantConnect, server.stop_server)
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
170
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
171
    def test_server_crash_while_responding(self):
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
172
        # We want to ensure the exception has been caught
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
173
        caught = threading.Event()
174
        caught.clear()
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
175
        # The thread that will serve the client, this needs to be an attribute
176
        # so the handler below can modify it when it's executed (it's
177
        # instantiated when the request is processed)
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
178
        self.connection_thread = None
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
179
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
180
        class FailToRespond(Exception):
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
181
            pass
182
183
        class FailingDuringResponseHandler(TCPConnectionHandler):
184
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
185
            def handle_connection(request):
186
                req = request.rfile.readline()
187
                # Capture the thread and make it use 'caught' so we can wait on
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
188
                # the even that will be set when the exception is caught. We
189
                # also capture the thread to know where to look.
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
190
                self.connection_thread = threading.currentThread()
191
                self.connection_thread.set_sync_event(caught)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
192
                raise FailToRespond()
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
193
194
        server = self.get_server(
195
            connection_handler_class=FailingDuringResponseHandler)
196
        client = self.get_client()
5247.3.21 by Vincent Ladeuil
Merge propagate-exceptions into http-leaks
197
        client.connect((server.host, server.port))
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
198
        client.write('ping\n')
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
199
        # Wait for the exception to be caught
200
        caught.wait()
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
201
        # Check that the connection thread did catch the exception,
202
        # http://pad.lv/869366 was wrongly checking the server thread which
203
        # works for TestingTCPServer where the connection is handled in the
204
        # same thread than the server one but is racy for
205
        # TestingThreadingTCPServer where the server thread may be in a
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
206
        # blocking accept() call (or not).
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
207
        try:
208
            self.connection_thread.pending_exception()
209
        except FailToRespond:
210
            # Great, the test succeeded
211
            pass
212
        else:
213
            # If the exception is not in the connection thread anymore, it's in
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
214
            # the server's one. 
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
215
            server.server.stopped.wait()
216
            # The exception is available now
217
            self.assertRaises(FailToRespond, server.pending_exception)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
218
219
    def test_exception_swallowed_while_serving(self):
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
220
        # We need to ensure the exception has been caught
221
        caught = threading.Event()
222
        caught.clear()
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
223
        # The thread that will serve the client, this needs to be an attribute
224
        # so the handler below can access it when it's executed (it's
225
        # instantiated when the request is processed)
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
226
        self.connection_thread = None
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
227
        class CantServe(Exception):
228
            pass
229
230
        class FailingWhileServingConnectionHandler(TCPConnectionHandler):
231
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
232
            def handle(request):
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
233
                # Capture the thread and make it use 'caught' so we can wait on
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
234
                # the even that will be set when the exception is caught. We
235
                # also capture the thread to know where to look.
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
236
                self.connection_thread = threading.currentThread()
237
                self.connection_thread.set_sync_event(caught)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
238
                raise CantServe()
239
240
        server = self.get_server(
241
            connection_handler_class=FailingWhileServingConnectionHandler)
242
        # Install the exception swallower
243
        server.set_ignored_exceptions(CantServe)
244
        client = self.get_client()
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
245
        # Connect to the server so the exception is raised there
5247.3.21 by Vincent Ladeuil
Merge propagate-exceptions into http-leaks
246
        client.connect((server.host, server.port))
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
247
        # Wait for the exception to be caught
248
        caught.wait()
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
249
        # The connection wasn't served properly but the exception should have
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
250
        # been swallowed (see test_server_crash_while_responding remark about
251
        # http://pad.lv/869366 explaining why we can't check the server thread
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
252
        # here). More precisely, the exception *has* been caught and captured
253
        # but it is cleared when joining the thread (or trying to acquire the
254
        # exception) and as such won't propagate to the server thread.
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
255
        self.connection_thread.pending_exception()
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
256
        server.pending_exception()