/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
1
# Copyright (C) 2010 Canonical Ltd
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
27
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
28
29
def load_tests(basic_tests, module, loader):
30
    suite = loader.suiteClass()
31
    server_tests, remaining_tests = tests.split_suite_by_condition(
32
        basic_tests, tests.condition_isinstance(TestTCPServerInAThread))
33
    server_scenarios = [ (name, {'server_class': getattr(test_server, name)})
34
                         for name in
35
                         ('TestingTCPServer', 'TestingThreadingTCPServer')]
36
    tests.multiply_tests(server_tests, server_scenarios, suite)
37
    suite.addTest(remaining_tests)
38
    return suite
39
40
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
41
class TCPClient(object):
42
43
    def __init__(self):
44
        self.sock = None
45
46
    def connect(self, addr):
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
47
        if self.sock is not None:
48
            raise AssertionError('Already connected to %r'
49
                                 % (self.sock.getsockname(),))
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
50
        self.sock = osutils.connect_socket(addr)
51
52
    def disconnect(self):
53
        if self.sock is not None:
5247.3.10 by Vincent Ladeuil
Test errors during server life.
54
            try:
55
                self.sock.shutdown(socket.SHUT_RDWR)
56
                self.sock.close()
57
            except socket.error, e:
58
                if e[0] in (errno.EBADF, errno.ENOTCONN):
59
                    # Right, the socket is already down
60
                    pass
61
                else:
62
                    raise
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
63
            self.sock = None
64
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
65
    def write(self, s):
66
        return self.sock.sendall(s)
67
68
    def read(self, bufsize=4096):
69
        return self.sock.recv(bufsize)
70
71
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
72
class TCPConnectionHandler(SocketServer.StreamRequestHandler):
73
74
    def handle(self):
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
75
        self.done = False
76
        self.handle_connection()
77
        while not self.done:
78
            self.handle_connection()
79
80
    def handle_connection(self):
81
        req = self.rfile.readline()
82
        if not req:
83
            self.done = True
84
        elif req == 'ping\n':
85
            self.wfile.write('pong\n')
86
        else:
87
            raise ValueError('[%s] not understood' % req)
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
88
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
89
90
class TestTCPServerInAThread(tests.TestCase):
91
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
92
    # Set by load_tests()
93
    server_class = None
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
94
5247.3.10 by Vincent Ladeuil
Test errors during server life.
95
    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.
96
        if server_class is not None:
97
            self.server_class = server_class
5247.3.10 by Vincent Ladeuil
Test errors during server life.
98
        if connection_handler_class is None:
99
            connection_handler_class = TCPConnectionHandler
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
100
        server =  test_server.TestingTCPServerInAThread(
101
            ('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
102
        server.start_server()
103
        self.addCleanup(server.stop_server)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
104
        return server
105
106
    def get_client(self):
107
        client = TCPClient()
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
108
        self.addCleanup(client.disconnect)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
109
        return client
110
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
111
    def get_server_connection(self, server, conn_rank):
112
        return server.server.clients[conn_rank]
113
114
    def assertClientAddr(self, client, server, conn_rank):
115
        conn = self.get_server_connection(server, conn_rank)
116
        self.assertEquals(client.sock.getsockname(), conn[1])
117
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
118
    def test_start_stop(self):
119
        server = self.get_server()
120
        client = self.get_client()
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
121
        server.stop_server()
122
        # since the server doesn't accept connections anymore attempting to
123
        # connect should fail
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
124
        client = self.get_client()
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
125
        self.assertRaises(socket.error, client.connect, server.server_address)
126
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
127
    def test_client_talks_server_respond(self):
128
        server = self.get_server()
129
        client = self.get_client()
130
        client.connect(server.server_address)
131
        self.assertIs(None, client.write('ping\n'))
132
        resp = client.read()
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
133
        self.assertClientAddr(client, server, 0)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
134
        self.assertEquals('pong\n', resp)
5247.3.10 by Vincent Ladeuil
Test errors during server life.
135
136
    def test_server_fails_to_start(self):
137
        class CantStart(Exception):
138
            pass
139
140
        class CantStartServer(test_server.TestingTCPServer):
141
142
            def server_bind(self):
143
                raise CantStart()
144
145
        # The exception is raised in the main thread
146
        self.assertRaises(CantStart,
147
                          self.get_server, server_class=CantStartServer)
148
5247.5.10 by Vincent Ladeuil
Fix broken test.
149
    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.
150
        class CantConnect(Exception):
5247.3.10 by Vincent Ladeuil
Test errors during server life.
151
            pass
152
153
        class FailingConnectionHandler(TCPConnectionHandler):
154
155
            def handle(self):
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
156
                raise CantConnect()
5247.3.10 by Vincent Ladeuil
Test errors during server life.
157
158
        server = self.get_server(
159
            connection_handler_class=FailingConnectionHandler)
160
        # The server won't fail until a client connect
161
        client = self.get_client()
162
        client.connect(server.server_address)
163
        try:
164
            # Now we must force the server to answer by sending the request and
165
            # waiting for some answer. But since we don't control when the
166
            # server thread will be given cycles, we don't control either
167
            # whether our reads or writes may hang.
168
            client.sock.settimeout(0.1)
169
            client.write('ping\n')
170
            client.read()
171
        except socket.error:
172
            pass
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
173
        # 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.
174
        self.assertRaises(CantConnect, server.stop_server)
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
175
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
176
    def test_server_crash_while_responding(self):
177
        sync = threading.Event()
178
        sync.clear()
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
179
        class FailToRespond(Exception):
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
180
            pass
181
182
        class FailingDuringResponseHandler(TCPConnectionHandler):
183
184
            def handle_connection(self):
185
                req = self.rfile.readline()
5247.5.11 by Vincent Ladeuil
Use a better sync for test_server_crash_while_responding test.
186
                threading.currentThread().set_event(sync)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
187
                raise FailToRespond()
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
188
189
        server = self.get_server(
190
            connection_handler_class=FailingDuringResponseHandler)
191
        client = self.get_client()
192
        client.connect(server.server_address)
193
        client.write('ping\n')
194
        sync.wait()
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
195
        self.assertRaises(FailToRespond, server.pending_exception)
196
197
    def test_exception_swallowed_while_serving(self):
198
        sync = threading.Event()
199
        sync.clear()
200
        class CantServe(Exception):
201
            pass
202
203
        class FailingWhileServingConnectionHandler(TCPConnectionHandler):
204
205
            def handle(self):
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
206
                # We want to sync with the thread that is serving the
207
                # connection.
208
                threading.currentThread().set_event(sync)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
209
                raise CantServe()
210
211
        server = self.get_server(
212
            connection_handler_class=FailingWhileServingConnectionHandler)
213
        # Install the exception swallower
214
        server.set_ignored_exceptions(CantServe)
215
        client = self.get_client()
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
216
        # Connect to the server so the exception is raised there
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
217
        client.connect(server.server_address)
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
218
        # Wait for the exception to propagate.
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
219
        sync.wait()
220
        # The connection wasn't served properly but the exception should have
221
        # been swallowed.
222
        server.pending_exception()