~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/stub_sftp.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 16:38:10 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309163810-ujn8hcx08f75nlf1
Refined test to make use of locking hooks and also validate if lock is truly a checkout-lock.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008, 2009, 2010 Robey Pointer <robey@lag.net>, Canonical Ltd
 
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>, Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
20
20
"""
21
21
 
22
22
import os
23
 
import paramiko
24
 
import select
25
 
import socket
26
 
import SocketServer
 
23
from paramiko import ServerInterface, SFTPServerInterface, SFTPServer, SFTPAttributes, \
 
24
    SFTPHandle, SFTP_OK, AUTH_SUCCESSFUL, OPEN_SUCCEEDED
27
25
import sys
28
 
import threading
29
 
import time
30
 
 
31
 
from bzrlib import (
32
 
    osutils,
33
 
    trace,
34
 
    urlutils,
35
 
    )
36
 
from bzrlib.transport import (
37
 
    ssh,
38
 
    )
39
 
from bzrlib.tests import test_server
40
 
 
41
 
 
42
 
class StubServer(paramiko.ServerInterface):
43
 
 
44
 
    def __init__(self, test_case_server):
45
 
        paramiko.ServerInterface.__init__(self)
46
 
        self.log = test_case_server.log
 
26
 
 
27
from bzrlib.osutils import pathjoin
 
28
from bzrlib.trace import mutter
 
29
 
 
30
 
 
31
class StubServer (ServerInterface):
 
32
 
 
33
    def __init__(self, test_case):
 
34
        ServerInterface.__init__(self)
 
35
        self._test_case = test_case
47
36
 
48
37
    def check_auth_password(self, username, password):
49
38
        # all are allowed
50
 
        self.log('sftpserver - authorizing: %s' % (username,))
51
 
        return paramiko.AUTH_SUCCESSFUL
 
39
        self._test_case.log('sftpserver - authorizing: %s' % (username,))
 
40
        return AUTH_SUCCESSFUL
52
41
 
53
42
    def check_channel_request(self, kind, chanid):
54
 
        self.log('sftpserver - channel request: %s, %s' % (kind, chanid))
55
 
        return paramiko.OPEN_SUCCEEDED
56
 
 
57
 
 
58
 
class StubSFTPHandle(paramiko.SFTPHandle):
59
 
 
 
43
        self._test_case.log('sftpserver - channel request: %s, %s' % (kind, chanid))
 
44
        return OPEN_SUCCEEDED
 
45
 
 
46
 
 
47
class StubSFTPHandle (SFTPHandle):
60
48
    def stat(self):
61
49
        try:
62
 
            return paramiko.SFTPAttributes.from_stat(
63
 
                os.fstat(self.readfile.fileno()))
 
50
            return SFTPAttributes.from_stat(os.fstat(self.readfile.fileno()))
64
51
        except OSError, e:
65
 
            return paramiko.SFTPServer.convert_errno(e.errno)
 
52
            return SFTPServer.convert_errno(e.errno)
66
53
 
67
54
    def chattr(self, attr):
68
55
        # python doesn't have equivalents to fchown or fchmod, so we have to
69
56
        # use the stored filename
70
 
        trace.mutter('Changing permissions on %s to %s', self.filename, attr)
 
57
        mutter('Changing permissions on %s to %s', self.filename, attr)
71
58
        try:
72
 
            paramiko.SFTPServer.set_file_attr(self.filename, attr)
 
59
            SFTPServer.set_file_attr(self.filename, attr)
73
60
        except OSError, e:
74
 
            return paramiko.SFTPServer.convert_errno(e.errno)
75
 
 
76
 
 
77
 
class StubSFTPServer(paramiko.SFTPServerInterface):
 
61
            return SFTPServer.convert_errno(e.errno)
 
62
 
 
63
 
 
64
class StubSFTPServer (SFTPServerInterface):
78
65
 
79
66
    def __init__(self, server, root, home=None):
80
 
        paramiko.SFTPServerInterface.__init__(self, server)
 
67
        SFTPServerInterface.__init__(self, server)
81
68
        # All paths are actually relative to 'root'.
82
69
        # this is like implementing chroot().
83
70
        self.root = root
91
78
            self.home = home[len(self.root):]
92
79
        if self.home.startswith('/'):
93
80
            self.home = self.home[1:]
94
 
        server.log('sftpserver - new connection')
 
81
        server._test_case.log('sftpserver - new connection')
95
82
 
96
83
    def _realpath(self, path):
97
84
        # paths returned from self.canonicalize() always start with
126
113
 
127
114
    def chattr(self, path, attr):
128
115
        try:
129
 
            paramiko.SFTPServer.set_file_attr(path, attr)
 
116
            SFTPServer.set_file_attr(path, attr)
130
117
        except OSError, e:
131
 
            return paramiko.SFTPServer.convert_errno(e.errno)
132
 
        return paramiko.SFTP_OK
 
118
            return SFTPServer.convert_errno(e.errno)
 
119
        return SFTP_OK
133
120
 
134
121
    def list_folder(self, path):
135
122
        path = self._realpath(path)
136
123
        try:
137
124
            out = [ ]
138
125
            # TODO: win32 incorrectly lists paths with non-ascii if path is not
139
 
            # unicode. However on unix the server should only deal with
 
126
            # unicode. However on Linux the server should only deal with
140
127
            # bytestreams and posix.listdir does the right thing
141
128
            if sys.platform == 'win32':
142
129
                flist = [f.encode('utf8') for f in os.listdir(path)]
143
130
            else:
144
131
                flist = os.listdir(path)
145
132
            for fname in flist:
146
 
                attr = paramiko.SFTPAttributes.from_stat(
147
 
                    os.stat(osutils.pathjoin(path, fname)))
 
133
                attr = SFTPAttributes.from_stat(os.stat(pathjoin(path, fname)))
148
134
                attr.filename = fname
149
135
                out.append(attr)
150
136
            return out
151
137
        except OSError, e:
152
 
            return paramiko.SFTPServer.convert_errno(e.errno)
 
138
            return SFTPServer.convert_errno(e.errno)
153
139
 
154
140
    def stat(self, path):
155
141
        path = self._realpath(path)
156
142
        try:
157
 
            return paramiko.SFTPAttributes.from_stat(os.stat(path))
 
143
            return SFTPAttributes.from_stat(os.stat(path))
158
144
        except OSError, e:
159
 
            return paramiko.SFTPServer.convert_errno(e.errno)
 
145
            return SFTPServer.convert_errno(e.errno)
160
146
 
161
147
    def lstat(self, path):
162
148
        path = self._realpath(path)
163
149
        try:
164
 
            return paramiko.SFTPAttributes.from_stat(os.lstat(path))
 
150
            return SFTPAttributes.from_stat(os.lstat(path))
165
151
        except OSError, e:
166
 
            return paramiko.SFTPServer.convert_errno(e.errno)
 
152
            return SFTPServer.convert_errno(e.errno)
167
153
 
168
154
    def open(self, path, flags, attr):
169
155
        path = self._realpath(path)
176
162
                # an odd default mode for files
177
163
                fd = os.open(path, flags, 0666)
178
164
        except OSError, e:
179
 
            return paramiko.SFTPServer.convert_errno(e.errno)
 
165
            return SFTPServer.convert_errno(e.errno)
180
166
 
181
167
        if (flags & os.O_CREAT) and (attr is not None):
182
168
            attr._flags &= ~attr.FLAG_PERMISSIONS
183
 
            paramiko.SFTPServer.set_file_attr(path, attr)
 
169
            SFTPServer.set_file_attr(path, attr)
184
170
        if flags & os.O_WRONLY:
185
171
            fstr = 'wb'
186
172
        elif flags & os.O_RDWR:
191
177
        try:
192
178
            f = os.fdopen(fd, fstr)
193
179
        except (IOError, OSError), e:
194
 
            return paramiko.SFTPServer.convert_errno(e.errno)
 
180
            return SFTPServer.convert_errno(e.errno)
195
181
        fobj = StubSFTPHandle()
196
182
        fobj.filename = path
197
183
        fobj.readfile = f
203
189
        try:
204
190
            os.remove(path)
205
191
        except OSError, e:
206
 
            return paramiko.SFTPServer.convert_errno(e.errno)
207
 
        return paramiko.SFTP_OK
 
192
            return SFTPServer.convert_errno(e.errno)
 
193
        return SFTP_OK
208
194
 
209
195
    def rename(self, oldpath, newpath):
210
196
        oldpath = self._realpath(oldpath)
212
198
        try:
213
199
            os.rename(oldpath, newpath)
214
200
        except OSError, e:
215
 
            return paramiko.SFTPServer.convert_errno(e.errno)
216
 
        return paramiko.SFTP_OK
 
201
            return SFTPServer.convert_errno(e.errno)
 
202
        return SFTP_OK
217
203
 
218
204
    def mkdir(self, path, attr):
219
205
        path = self._realpath(path)
226
212
                os.mkdir(path)
227
213
            if attr is not None:
228
214
                attr._flags &= ~attr.FLAG_PERMISSIONS
229
 
                paramiko.SFTPServer.set_file_attr(path, attr)
 
215
                SFTPServer.set_file_attr(path, attr)
230
216
        except OSError, e:
231
 
            return paramiko.SFTPServer.convert_errno(e.errno)
232
 
        return paramiko.SFTP_OK
 
217
            return SFTPServer.convert_errno(e.errno)
 
218
        return SFTP_OK
233
219
 
234
220
    def rmdir(self, path):
235
221
        path = self._realpath(path)
236
222
        try:
237
223
            os.rmdir(path)
238
224
        except OSError, e:
239
 
            return paramiko.SFTPServer.convert_errno(e.errno)
240
 
        return paramiko.SFTP_OK
 
225
            return SFTPServer.convert_errno(e.errno)
 
226
        return SFTP_OK
241
227
 
242
228
    # removed: chattr, symlink, readlink
243
229
    # (nothing in bzr's sftp transport uses those)
244
 
 
245
 
 
246
 
# ------------- server test implementation --------------
247
 
 
248
 
STUB_SERVER_KEY = """
249
 
-----BEGIN RSA PRIVATE KEY-----
250
 
MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz
251
 
oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/
252
 
d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB
253
 
gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0
254
 
EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon
255
 
soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H
256
 
tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU
257
 
avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA
258
 
4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g
259
 
H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv
260
 
qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV
261
 
HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc
262
 
nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7
263
 
-----END RSA PRIVATE KEY-----
264
 
"""
265
 
 
266
 
 
267
 
class SocketDelay(object):
268
 
    """A socket decorator to make TCP appear slower.
269
 
 
270
 
    This changes recv, send, and sendall to add a fixed latency to each python
271
 
    call if a new roundtrip is detected. That is, when a recv is called and the
272
 
    flag new_roundtrip is set, latency is charged. Every send and send_all
273
 
    sets this flag.
274
 
 
275
 
    In addition every send, sendall and recv sleeps a bit per character send to
276
 
    simulate bandwidth.
277
 
 
278
 
    Not all methods are implemented, this is deliberate as this class is not a
279
 
    replacement for the builtin sockets layer. fileno is not implemented to
280
 
    prevent the proxy being bypassed.
281
 
    """
282
 
 
283
 
    simulated_time = 0
284
 
    _proxied_arguments = dict.fromkeys([
285
 
        "close", "getpeername", "getsockname", "getsockopt", "gettimeout",
286
 
        "setblocking", "setsockopt", "settimeout", "shutdown"])
287
 
 
288
 
    def __init__(self, sock, latency, bandwidth=1.0,
289
 
                 really_sleep=True):
290
 
        """
291
 
        :param bandwith: simulated bandwith (MegaBit)
292
 
        :param really_sleep: If set to false, the SocketDelay will just
293
 
        increase a counter, instead of calling time.sleep. This is useful for
294
 
        unittesting the SocketDelay.
295
 
        """
296
 
        self.sock = sock
297
 
        self.latency = latency
298
 
        self.really_sleep = really_sleep
299
 
        self.time_per_byte = 1 / (bandwidth / 8.0 * 1024 * 1024)
300
 
        self.new_roundtrip = False
301
 
 
302
 
    def sleep(self, s):
303
 
        if self.really_sleep:
304
 
            time.sleep(s)
305
 
        else:
306
 
            SocketDelay.simulated_time += s
307
 
 
308
 
    def __getattr__(self, attr):
309
 
        if attr in SocketDelay._proxied_arguments:
310
 
            return getattr(self.sock, attr)
311
 
        raise AttributeError("'SocketDelay' object has no attribute %r" %
312
 
                             attr)
313
 
 
314
 
    def dup(self):
315
 
        return SocketDelay(self.sock.dup(), self.latency, self.time_per_byte,
316
 
                           self._sleep)
317
 
 
318
 
    def recv(self, *args):
319
 
        data = self.sock.recv(*args)
320
 
        if data and self.new_roundtrip:
321
 
            self.new_roundtrip = False
322
 
            self.sleep(self.latency)
323
 
        self.sleep(len(data) * self.time_per_byte)
324
 
        return data
325
 
 
326
 
    def sendall(self, data, flags=0):
327
 
        if not self.new_roundtrip:
328
 
            self.new_roundtrip = True
329
 
            self.sleep(self.latency)
330
 
        self.sleep(len(data) * self.time_per_byte)
331
 
        return self.sock.sendall(data, flags)
332
 
 
333
 
    def send(self, data, flags=0):
334
 
        if not self.new_roundtrip:
335
 
            self.new_roundtrip = True
336
 
            self.sleep(self.latency)
337
 
        bytes_sent = self.sock.send(data, flags)
338
 
        self.sleep(bytes_sent * self.time_per_byte)
339
 
        return bytes_sent
340
 
 
341
 
 
342
 
class TestingSFTPConnectionHandler(SocketServer.BaseRequestHandler):
343
 
 
344
 
    def setup(self):
345
 
        self.wrap_for_latency()
346
 
        tcs = self.server.test_case_server
347
 
        ssh_server = paramiko.Transport(self.request)
348
 
        ssh_server.add_server_key(tcs.get_host_key())
349
 
        ssh_server.set_subsystem_handler('sftp', paramiko.SFTPServer,
350
 
                                         StubSFTPServer, root=tcs._root,
351
 
                                         home=tcs._server_homedir)
352
 
        server = tcs._server_interface(tcs)
353
 
        ssh_server.start_server(None, server)
354
 
        # FIXME: Long story short:
355
 
        # bt.test_transport.TestSSHConnections.test_bzr_connect_to_bzr_ssh
356
 
        # fails if we wait less than 0.2 seconds... paramiko uses a lot of
357
 
        # timeouts internally which probably mask a synchronisation
358
 
        # problem. Note that this is the only test that requires this hack and
359
 
        # the test may need to be fixed instead, but it's late and the test is
360
 
        # horrible as mentioned in its comments :) -- vila 20100623
361
 
        import time
362
 
        time.sleep(0.2)
363
 
 
364
 
    def wrap_for_latency(self):
365
 
        tcs = self.server.test_case_server
366
 
        if tcs.add_latency:
367
 
            # Give the socket (which the request really is) a latency adding
368
 
            # decorator.
369
 
            self.request = SocketDelay(self.request, tcs.add_latency)
370
 
 
371
 
 
372
 
class TestingSFTPWithoutSSHConnectionHandler(TestingSFTPConnectionHandler):
373
 
 
374
 
    def setup(self):
375
 
        self.wrap_for_latency()
376
 
        # Re-import these as locals, so that they're still accessible during
377
 
        # interpreter shutdown (when all module globals get set to None, leading
378
 
        # to confusing errors like "'NoneType' object has no attribute 'error'".
379
 
        class FakeChannel(object):
380
 
            def get_transport(self):
381
 
                return self
382
 
            def get_log_channel(self):
383
 
                return 'paramiko'
384
 
            def get_name(self):
385
 
                return '1'
386
 
            def get_hexdump(self):
387
 
                return False
388
 
            def close(self):
389
 
                pass
390
 
 
391
 
        tcs = self.server.test_case_server
392
 
        server = paramiko.SFTPServer(
393
 
            FakeChannel(), 'sftp', StubServer(tcs), StubSFTPServer,
394
 
            root=tcs._root, home=tcs._server_homedir)
395
 
        try:
396
 
            server.start_subsystem(
397
 
                'sftp', None, ssh.SocketAsChannelAdapter(self.request))
398
 
        except socket.error, e:
399
 
            if (len(e.args) > 0) and (e.args[0] == errno.EPIPE):
400
 
                # it's okay for the client to disconnect abruptly
401
 
                # (bug in paramiko 1.6: it should absorb this exception)
402
 
                pass
403
 
            else:
404
 
                raise
405
 
        except Exception, e:
406
 
            # This typically seems to happen during interpreter shutdown, so
407
 
            # most of the useful ways to report this error won't work.
408
 
            # Writing the exception type, and then the text of the exception,
409
 
            # seems to be the best we can do.
410
 
            # FIXME: All interpreter shutdown errors should have been related
411
 
            # to daemon threads, cleanup needed -- vila 20100623
412
 
            import sys
413
 
            sys.stderr.write('\nEXCEPTION %r: ' % (e.__class__,))
414
 
            sys.stderr.write('%s\n\n' % (e,))
415
 
        server.finish_subsystem()
416
 
 
417
 
 
418
 
class TestingSFTPServer(test_server.TestingThreadingTCPServer):
419
 
 
420
 
    def __init__(self, server_address, request_handler_class, test_case_server):
421
 
        test_server.TestingThreadingTCPServer.__init__(
422
 
            self, server_address, request_handler_class)
423
 
        self.test_case_server = test_case_server
424
 
 
425
 
 
426
 
class SFTPServer(test_server.TestingTCPServerInAThread):
427
 
    """Common code for SFTP server facilities."""
428
 
 
429
 
    def __init__(self, server_interface=StubServer):
430
 
        self.host = '127.0.0.1'
431
 
        self.port = 0
432
 
        super(SFTPServer, self).__init__((self.host, self.port),
433
 
                                         TestingSFTPServer,
434
 
                                         TestingSFTPConnectionHandler)
435
 
        self._original_vendor = None
436
 
        self._vendor = ssh.ParamikoVendor()
437
 
        self._server_interface = server_interface
438
 
        self._host_key = None
439
 
        self.logs = []
440
 
        self.add_latency = 0
441
 
        self._homedir = None
442
 
        self._server_homedir = None
443
 
        self._root = None
444
 
 
445
 
    def _get_sftp_url(self, path):
446
 
        """Calculate an sftp url to this server for path."""
447
 
        return "sftp://foo:bar@%s:%s/%s" % (self.host, self.port, path)
448
 
 
449
 
    def log(self, message):
450
 
        """StubServer uses this to log when a new server is created."""
451
 
        self.logs.append(message)
452
 
 
453
 
    def create_server(self):
454
 
        server = self.server_class((self.host, self.port),
455
 
                                   self.request_handler_class,
456
 
                                   self)
457
 
        return server
458
 
 
459
 
    def get_host_key(self):
460
 
        if self._host_key is None:
461
 
            key_file = osutils.pathjoin(self._homedir, 'test_rsa.key')
462
 
            f = open(key_file, 'w')
463
 
            try:
464
 
                f.write(STUB_SERVER_KEY)
465
 
            finally:
466
 
                f.close()
467
 
            self._host_key = paramiko.RSAKey.from_private_key_file(key_file)
468
 
        return self._host_key
469
 
 
470
 
    def start_server(self, backing_server=None):
471
 
        # XXX: TODO: make sftpserver back onto backing_server rather than local
472
 
        # disk.
473
 
        if not (backing_server is None or
474
 
                isinstance(backing_server, test_server.LocalURLServer)):
475
 
            raise AssertionError(
476
 
                'backing_server should not be %r, because this can only serve '
477
 
                'the local current working directory.' % (backing_server,))
478
 
        self._original_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
479
 
        ssh._ssh_vendor_manager._cached_ssh_vendor = self._vendor
480
 
        if sys.platform == 'win32':
481
 
            # Win32 needs to use the UNICODE api
482
 
            self._homedir = os.getcwdu()
483
 
            # Normalize the path or it will be wrongly escaped
484
 
            self._homedir = osutils.normpath(self._homedir)
485
 
        else:
486
 
            # But unix SFTP servers should just deal in bytestreams
487
 
            self._homedir = os.getcwd()
488
 
        if self._server_homedir is None:
489
 
            self._server_homedir = self._homedir
490
 
        self._root = '/'
491
 
        if sys.platform == 'win32':
492
 
            self._root = ''
493
 
        super(SFTPServer, self).start_server()
494
 
 
495
 
    def stop_server(self):
496
 
        try:
497
 
            super(SFTPServer, self).stop_server()
498
 
        finally:
499
 
            ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
500
 
 
501
 
    def get_bogus_url(self):
502
 
        """See bzrlib.transport.Server.get_bogus_url."""
503
 
        # this is chosen to try to prevent trouble with proxies, weird dns, etc
504
 
        # we bind a random socket, so that we get a guaranteed unused port
505
 
        # we just never listen on that port
506
 
        s = socket.socket()
507
 
        s.bind(('localhost', 0))
508
 
        return 'sftp://%s:%s/' % s.getsockname()
509
 
 
510
 
 
511
 
class SFTPFullAbsoluteServer(SFTPServer):
512
 
    """A test server for sftp transports, using absolute urls and ssh."""
513
 
 
514
 
    def get_url(self):
515
 
        """See bzrlib.transport.Server.get_url."""
516
 
        homedir = self._homedir
517
 
        if sys.platform != 'win32':
518
 
            # Remove the initial '/' on all platforms but win32
519
 
            homedir = homedir[1:]
520
 
        return self._get_sftp_url(urlutils.escape(homedir))
521
 
 
522
 
 
523
 
class SFTPServerWithoutSSH(SFTPServer):
524
 
    """An SFTP server that uses a simple TCP socket pair rather than SSH."""
525
 
 
526
 
    def __init__(self):
527
 
        super(SFTPServerWithoutSSH, self).__init__()
528
 
        self._vendor = ssh.LoopbackVendor()
529
 
        self.request_handler_class = TestingSFTPWithoutSSHConnectionHandler
530
 
 
531
 
    def get_host_key():
532
 
        return None
533
 
 
534
 
 
535
 
class SFTPAbsoluteServer(SFTPServerWithoutSSH):
536
 
    """A test server for sftp transports, using absolute urls."""
537
 
 
538
 
    def get_url(self):
539
 
        """See bzrlib.transport.Server.get_url."""
540
 
        homedir = self._homedir
541
 
        if sys.platform != 'win32':
542
 
            # Remove the initial '/' on all platforms but win32
543
 
            homedir = homedir[1:]
544
 
        return self._get_sftp_url(urlutils.escape(homedir))
545
 
 
546
 
 
547
 
class SFTPHomeDirServer(SFTPServerWithoutSSH):
548
 
    """A test server for sftp transports, using homedir relative urls."""
549
 
 
550
 
    def get_url(self):
551
 
        """See bzrlib.transport.Server.get_url."""
552
 
        return self._get_sftp_url("~/")
553
 
 
554
 
 
555
 
class SFTPSiblingAbsoluteServer(SFTPAbsoluteServer):
556
 
    """A test server for sftp transports where only absolute paths will work.
557
 
 
558
 
    It does this by serving from a deeply-nested directory that doesn't exist.
559
 
    """
560
 
 
561
 
    def create_server(self):
562
 
        # FIXME: Can't we do that in a cleaner way ? -- vila 20100623
563
 
        server = super(SFTPSiblingAbsoluteServer, self).create_server()
564
 
        server._server_homedir = '/dev/noone/runs/tests/here'
565
 
        return server
566