/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
1
# Copyright (C) 2010 Canonical Ltd
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
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.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
17
import socket
18
import select
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
19
import sys
20
import threading
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
21
22
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
23
from bzrlib import (
24
    transport,
5017.3.15 by Vincent Ladeuil
Fix missing import.
25
    urlutils,
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
26
    )
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
27
from bzrlib.transport import (
5017.3.20 by Vincent Ladeuil
Move TestingChrootServer to bzrlib.tests.test_server
28
    chroot,
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
29
    pathfilter,
30
    )
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
31
from bzrlib.smart import server
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
32
33
34
class TestServer(transport.Server):
35
    """A Transport Server dedicated to tests.
36
37
    The TestServer interface provides a server for a given transport. We use
38
    these servers as loopback testing tools. For any given transport the
39
    Servers it provides must either allow writing, or serve the contents
40
    of os.getcwdu() at the time start_server is called.
41
42
    Note that these are real servers - they must implement all the things
43
    that we want bzr transports to take advantage of.
44
    """
45
46
    def get_url(self):
47
        """Return a url for this server.
48
49
        If the transport does not represent a disk directory (i.e. it is
50
        a database like svn, or a memory only transport, it should return
51
        a connection to a newly established resource for this Server.
52
        Otherwise it should return a url that will provide access to the path
53
        that was os.getcwdu() when start_server() was called.
54
55
        Subsequent calls will return the same resource.
56
        """
57
        raise NotImplementedError
58
59
    def get_bogus_url(self):
60
        """Return a url for this protocol, that will fail to connect.
61
62
        This may raise NotImplementedError to indicate that this server cannot
63
        provide bogus urls.
64
        """
65
        raise NotImplementedError
66
67
5017.3.6 by Vincent Ladeuil
Fix some fallouts of moving test servers around.
68
class LocalURLServer(TestServer):
5017.3.3 by Vincent Ladeuil
Move LocalURLServer to bzrlib.tests.test_server
69
    """A pretend server for local transports, using file:// urls.
70
71
    Of course no actual server is required to access the local filesystem, so
72
    this just exists to tell the test code how to get to it.
73
    """
74
75
    def start_server(self):
76
        pass
77
78
    def get_url(self):
79
        """See Transport.Server.get_url."""
80
        return urlutils.local_path_to_url('')
81
82
5017.3.6 by Vincent Ladeuil
Fix some fallouts of moving test servers around.
83
class DecoratorServer(TestServer):
5017.3.2 by Vincent Ladeuil
Move DecoratorServer to test_server.py
84
    """Server for the TransportDecorator for testing with.
85
86
    To use this when subclassing TransportDecorator, override override the
87
    get_decorator_class method.
88
    """
89
90
    def start_server(self, server=None):
91
        """See bzrlib.transport.Server.start_server.
92
93
        :server: decorate the urls given by server. If not provided a
94
        LocalServer is created.
95
        """
96
        if server is not None:
97
            self._made_server = False
98
            self._server = server
99
        else:
100
            self._made_server = True
101
            self._server = LocalURLServer()
102
            self._server.start_server()
103
104
    def stop_server(self):
105
        if self._made_server:
106
            self._server.stop_server()
107
108
    def get_decorator_class(self):
109
        """Return the class of the decorators we should be constructing."""
110
        raise NotImplementedError(self.get_decorator_class)
111
112
    def get_url_prefix(self):
113
        """What URL prefix does this decorator produce?"""
114
        return self.get_decorator_class()._get_url_prefix()
115
116
    def get_bogus_url(self):
117
        """See bzrlib.transport.Server.get_bogus_url."""
118
        return self.get_url_prefix() + self._server.get_bogus_url()
119
120
    def get_url(self):
121
        """See bzrlib.transport.Server.get_url."""
122
        return self.get_url_prefix() + self._server.get_url()
123
124
5017.3.8 by Vincent Ladeuil
Move BrokenRenameServer to bzrlib.tests.test_server
125
class BrokenRenameServer(DecoratorServer):
126
    """Server for the BrokenRenameTransportDecorator for testing with."""
127
128
    def get_decorator_class(self):
129
        from bzrlib.transport import brokenrename
130
        return brokenrename.BrokenRenameTransportDecorator
131
132
5017.3.7 by Vincent Ladeuil
Move FakeNFSServer to bzrlib.tests.test_server
133
class FakeNFSServer(DecoratorServer):
134
    """Server for the FakeNFSTransportDecorator for testing with."""
135
136
    def get_decorator_class(self):
137
        from bzrlib.transport import fakenfs
138
        return fakenfs.FakeNFSTransportDecorator
139
140
5017.3.9 by Vincent Ladeuil
Move FakeVFATServer to bzrlib.tests.test_server
141
class FakeVFATServer(DecoratorServer):
142
    """A server that suggests connections through FakeVFATTransportDecorator
143
144
    For use in testing.
145
    """
146
147
    def get_decorator_class(self):
148
        from bzrlib.transport import fakevfat
5017.3.14 by Vincent Ladeuil
Fix some missing prefixes.
149
        return fakevfat.FakeVFATTransportDecorator
5017.3.9 by Vincent Ladeuil
Move FakeVFATServer to bzrlib.tests.test_server
150
151
5017.3.11 by Vincent Ladeuil
Move LogDecoratorServer to bzrlib.tests.test_server
152
class LogDecoratorServer(DecoratorServer):
153
    """Server for testing."""
154
155
    def get_decorator_class(self):
156
        from bzrlib.transport import log
157
        return log.TransportLogDecorator
158
159
5017.3.12 by Vincent Ladeuil
Move NoSmartTransportServer to bzrlib.tests.test_server
160
class NoSmartTransportServer(DecoratorServer):
161
    """Server for the NoSmartTransportDecorator for testing with."""
162
163
    def get_decorator_class(self):
164
        from bzrlib.transport import nosmart
5017.3.14 by Vincent Ladeuil
Fix some missing prefixes.
165
        return nosmart.NoSmartTransportDecorator
5017.3.12 by Vincent Ladeuil
Move NoSmartTransportServer to bzrlib.tests.test_server
166
167
5017.3.5 by Vincent Ladeuil
Move ReadonlyServer to bzrlib.tests.readonly
168
class ReadonlyServer(DecoratorServer):
169
    """Server for the ReadonlyTransportDecorator for testing with."""
170
171
    def get_decorator_class(self):
172
        from bzrlib.transport import readonly
173
        return readonly.ReadonlyTransportDecorator
174
175
5017.3.10 by Vincent Ladeuil
Move TraceServer to bzrlib.tests.test_server
176
class TraceServer(DecoratorServer):
177
    """Server for the TransportTraceDecorator for testing with."""
178
179
    def get_decorator_class(self):
180
        from bzrlib.transport import trace
5017.3.14 by Vincent Ladeuil
Fix some missing prefixes.
181
        return trace.TransportTraceDecorator
5017.3.10 by Vincent Ladeuil
Move TraceServer to bzrlib.tests.test_server
182
183
5017.3.13 by Vincent Ladeuil
Move UnlistableServer to bzrlib.tests.test_server
184
class UnlistableServer(DecoratorServer):
185
    """Server for the UnlistableTransportDecorator for testing with."""
186
187
    def get_decorator_class(self):
188
        from bzrlib.transport import unlistable
189
        return unlistable.UnlistableTransportDecorator
190
191
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
192
class TestingPathFilteringServer(pathfilter.PathFilteringServer):
193
194
    def __init__(self):
5017.3.20 by Vincent Ladeuil
Move TestingChrootServer to bzrlib.tests.test_server
195
        """TestingPathFilteringServer is not usable until start_server
196
        is called."""
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
197
198
    def start_server(self, backing_server=None):
199
        """Setup the Chroot on backing_server."""
200
        if backing_server is not None:
201
            self.backing_transport = transport.get_transport(
202
                backing_server.get_url())
203
        else:
204
            self.backing_transport = transport.get_transport('.')
205
        self.backing_transport.clone('added-by-filter').ensure_base()
206
        self.filter_func = lambda x: 'added-by-filter/' + x
207
        super(TestingPathFilteringServer, self).start_server()
208
5017.3.20 by Vincent Ladeuil
Move TestingChrootServer to bzrlib.tests.test_server
209
    def get_bogus_url(self):
210
        raise NotImplementedError
211
212
213
class TestingChrootServer(chroot.ChrootServer):
214
215
    def __init__(self):
216
        """TestingChrootServer is not usable until start_server is called."""
217
        super(TestingChrootServer, self).__init__(None)
218
219
    def start_server(self, backing_server=None):
220
        """Setup the Chroot on backing_server."""
221
        if backing_server is not None:
222
            self.backing_transport = transport.get_transport(
223
                backing_server.get_url())
224
        else:
225
            self.backing_transport = transport.get_transport('.')
226
        super(TestingChrootServer, self).start_server()
227
228
    def get_bogus_url(self):
229
        raise NotImplementedError
230
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
231
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
232
class ThreadWithException(threading.Thread):
233
    """A catching exception thread.
234
235
    If an exception occurs during the thread execution, it's caught and
236
    re-raised when the thread is joined().
237
    """
238
239
    def __init__(self, *args, **kwargs):
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
240
        # There are cases where the calling thread must wait, yet, if an
241
        # exception occurs the event should be set so the caller is not
5247.2.5 by Vincent Ladeuil
Some cleanups.
242
        # blocked. The main example is a calling thread that want to wait for
243
        # the called thread to be in a given state before continuing.
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
244
        try:
245
            event = kwargs.pop('event')
246
        except KeyError:
247
            # If the caller didn't pass a specific event, create our own
248
            event = threading.Event()
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
249
        super(ThreadWithException, self).__init__(*args, **kwargs)
5247.2.5 by Vincent Ladeuil
Some cleanups.
250
        self.ready = event
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
251
        self.exception = None
252
253
    def run(self):
254
        """Overrides Thread.run to capture any exception."""
5247.2.5 by Vincent Ladeuil
Some cleanups.
255
        self.ready.clear()
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
256
        try:
257
            super(ThreadWithException, self).run()
258
        except Exception, e:
259
            self.exception = sys.exc_info()
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
260
        finally:
261
            # Make sure the calling thread is released
5247.2.5 by Vincent Ladeuil
Some cleanups.
262
            self.ready.set()
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
263
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
264
265
    def join(self, *args, **kwargs):
5247.2.3 by Vincent Ladeuil
join(timeout=0) is useful to check for an exception without stopping the thread.
266
        """Overrides Thread.join to raise any exception caught.
267
268
269
        Calling join(timeout=0) will raise the caught exception or return None
270
        is the thread is still alive.
271
        """
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
272
        # Note that we don't care about the timeout parameter here: either the
273
        # thread has raised an exception and it should be raised (and join()
274
        # should succeed whatever the timeout is) or it's still alive which
275
        # means it didn't encounter an exception.
276
        super(ThreadWithException, self).join(*args, **kwargs)
277
        if self.exception is not None:
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
278
            exc_class, exc_value, exc_tb = self.exception
279
            raise exc_class, exc_value, exc_tb
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
280
281
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
282
class SmartTCPServer_for_testing(server.SmartTCPServer):
283
    """Server suitable for use by transport tests.
284
285
    This server is backed by the process's cwd.
286
    """
287
288
    def __init__(self, thread_name_suffix=''):
289
        super(SmartTCPServer_for_testing, self).__init__(None)
290
        self.client_path_extra = None
291
        self.thread_name_suffix = thread_name_suffix
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
292
        # We collect the sockets/threads used by the clients so we can
293
        # close/join them when shutting down
294
        self.clients = []
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
295
296
    def get_backing_transport(self, backing_transport_server):
297
        """Get a backing transport from a server we are decorating."""
298
        return transport.get_transport(backing_transport_server.get_url())
299
300
    def start_server(self, backing_transport_server=None,
301
              client_path_extra='/extra/'):
302
        """Set up server for testing.
303
304
        :param backing_transport_server: backing server to use.  If not
305
            specified, a LocalURLServer at the current working directory will
306
            be used.
307
        :param client_path_extra: a path segment starting with '/' to append to
308
            the root URL for this server.  For instance, a value of '/foo/bar/'
309
            will mean the root of the backing transport will be published at a
310
            URL like `bzr://127.0.0.1:nnnn/foo/bar/`, rather than
311
            `bzr://127.0.0.1:nnnn/`.  Default value is `extra`, so that tests
312
            by default will fail unless they do the necessary path translation.
313
        """
314
        if not client_path_extra.startswith('/'):
315
            raise ValueError(client_path_extra)
316
        from bzrlib.transport.chroot import ChrootServer
317
        if backing_transport_server is None:
318
            backing_transport_server = LocalURLServer()
319
        self.chroot_server = ChrootServer(
320
            self.get_backing_transport(backing_transport_server))
321
        self.chroot_server.start_server()
322
        self.backing_transport = transport.get_transport(
323
            self.chroot_server.get_url())
324
        self.root_client_path = self.client_path_extra = client_path_extra
325
        self.start_background_thread(self.thread_name_suffix)
326
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
327
    def serve_conn(self, conn, thread_name_suffix):
328
        conn_thread = super(SmartTCPServer_for_testing, self).serve_conn(
329
            conn, thread_name_suffix)
330
        self.clients.append((conn, conn_thread))
331
        return conn_thread
332
333
    def shutdown_client(self, client_socket):
334
        """Properly shutdown a client socket.
335
336
        Under some circumstances (as in bug #383920), we need to force the
337
        shutdown as python delays it until gc occur otherwise and the client
338
        may hang.
339
340
        This should be called only when no other thread is trying to use the
341
        socket.
342
        """
343
        try:
344
            # The request process has been completed, the thread is about to
345
            # die, let's shutdown the socket if we can.
346
            client_socket.shutdown(socket.SHUT_RDWR)
347
        except (socket.error, select.error), e:
348
            if e[0] in (errno.EBADF, errno.ENOTCONN):
349
                # Right, the socket is already down
350
                pass
351
            else:
352
                raise
353
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
354
    def stop_server(self):
355
        self.stop_background_thread()
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
356
        # Let's close all our pending clients too
357
        for sock, thread in self.clients:
358
            self.shutdown_client(sock)
359
            thread.join()
360
            del thread
361
        self.clients = []
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
362
        self.chroot_server.stop_server()
363
364
    def get_url(self):
365
        url = super(SmartTCPServer_for_testing, self).get_url()
366
        return url[:-1] + self.client_path_extra
367
368
    def get_bogus_url(self):
369
        """Return a URL which will fail to connect"""
370
        return 'bzr://127.0.0.1:1/'
371
372
373
class ReadonlySmartTCPServer_for_testing(SmartTCPServer_for_testing):
374
    """Get a readonly server for testing."""
375
376
    def get_backing_transport(self, backing_transport_server):
377
        """Get a backing transport from a server we are decorating."""
378
        url = 'readonly+' + backing_transport_server.get_url()
379
        return transport.get_transport(url)
380
381
382
class SmartTCPServer_for_testing_v2_only(SmartTCPServer_for_testing):
383
    """A variation of SmartTCPServer_for_testing that limits the client to
384
    using RPCs in protocol v2 (i.e. bzr <= 1.5).
385
    """
386
387
    def get_url(self):
388
        url = super(SmartTCPServer_for_testing_v2_only, self).get_url()
389
        url = 'bzr-v2://' + url[len('bzr://'):]
390
        return url
391
392
393
class ReadonlySmartTCPServer_for_testing_v2_only(
394
    SmartTCPServer_for_testing_v2_only):
395
    """Get a readonly server for testing."""
396
397
    def get_backing_transport(self, backing_transport_server):
398
        """Get a backing transport from a server we are decorating."""
399
        url = 'readonly+' + backing_transport_server.get_url()
400
        return transport.get_transport(url)
401
402
403
404