~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transport.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-08-17 07:52:09 UTC
  • mfrom: (1910.3.4 trivial)
  • Revision ID: pqm@pqm.ubuntu.com-20060817075209-e85a1f9e05ff8b87
(andrew) Trivial fixes to NotImplemented errors.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006 by 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
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
 
18
import os
 
19
import sys
 
20
import stat
18
21
from cStringIO import StringIO
19
22
 
20
23
import bzrlib
21
 
from bzrlib import (
22
 
    errors,
23
 
    osutils,
24
 
    urlutils,
25
 
    )
26
 
from bzrlib.errors import (DependencyNotPresent,
27
 
                           FileExists,
28
 
                           InvalidURLJoin,
29
 
                           NoSuchFile,
30
 
                           PathNotChild,
31
 
                           ReadError,
 
24
from bzrlib.errors import (NoSuchFile, FileExists,
 
25
                           TransportNotPossible,
 
26
                           ConnectionError,
 
27
                           DependencyNotPresent,
32
28
                           UnsupportedProtocol,
33
29
                           )
34
30
from bzrlib.tests import TestCase, TestCaseInTempDir
35
 
from bzrlib.transport import (_clear_protocol_handlers,
36
 
                              _CoalescedOffset,
37
 
                              ConnectedTransport,
 
31
from bzrlib.transport import (_CoalescedOffset,
38
32
                              _get_protocol_handlers,
39
 
                              _set_protocol_handlers,
40
33
                              _get_transport_modules,
41
34
                              get_transport,
42
 
                              LateReadError,
43
35
                              register_lazy_transport,
44
 
                              register_transport_proto,
 
36
                              _set_protocol_handlers,
45
37
                              Transport,
46
38
                              )
47
 
from bzrlib.transport.chroot import ChrootServer
48
39
from bzrlib.transport.memory import MemoryTransport
49
 
from bzrlib.transport.local import (LocalTransport,
50
 
                                    EmulatedWin32LocalTransport)
51
 
from bzrlib.transport.remote import (
52
 
    BZR_DEFAULT_PORT,
53
 
    RemoteTCPTransport
54
 
    )
55
 
 
56
 
 
57
 
# TODO: Should possibly split transport-specific tests into their own files.
 
40
from bzrlib.transport.local import LocalTransport
58
41
 
59
42
 
60
43
class TestTransport(TestCase):
62
45
 
63
46
    def test__get_set_protocol_handlers(self):
64
47
        handlers = _get_protocol_handlers()
65
 
        self.assertNotEqual([], handlers.keys( ))
 
48
        self.assertNotEqual({}, handlers)
66
49
        try:
67
 
            _clear_protocol_handlers()
68
 
            self.assertEqual([], _get_protocol_handlers().keys())
 
50
            _set_protocol_handlers({})
 
51
            self.assertEqual({}, _get_protocol_handlers())
69
52
        finally:
70
53
            _set_protocol_handlers(handlers)
71
54
 
72
55
    def test_get_transport_modules(self):
73
56
        handlers = _get_protocol_handlers()
74
 
        # don't pollute the current handlers
75
 
        _clear_protocol_handlers()
76
57
        class SampleHandler(object):
77
58
            """I exist, isnt that enough?"""
78
59
        try:
79
 
            _clear_protocol_handlers()
80
 
            register_transport_proto('foo')
 
60
            my_handlers = {}
 
61
            _set_protocol_handlers(my_handlers)
81
62
            register_lazy_transport('foo', 'bzrlib.tests.test_transport', 'TestTransport.SampleHandler')
82
 
            register_transport_proto('bar')
83
63
            register_lazy_transport('bar', 'bzrlib.tests.test_transport', 'TestTransport.SampleHandler')
84
 
            self.assertEqual([SampleHandler.__module__, 'bzrlib.transport.chroot'],
 
64
            self.assertEqual([SampleHandler.__module__],
85
65
                             _get_transport_modules())
86
66
        finally:
87
67
            _set_protocol_handlers(handlers)
89
69
    def test_transport_dependency(self):
90
70
        """Transport with missing dependency causes no error"""
91
71
        saved_handlers = _get_protocol_handlers()
92
 
        # don't pollute the current handlers
93
 
        _clear_protocol_handlers()
94
72
        try:
95
 
            register_transport_proto('foo')
96
73
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
97
74
                    'BadTransportHandler')
98
75
            try:
113
90
        """Transport with missing dependency causes no error"""
114
91
        saved_handlers = _get_protocol_handlers()
115
92
        try:
116
 
            _clear_protocol_handlers()
117
 
            register_transport_proto('foo')
 
93
            _set_protocol_handlers({})
118
94
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
119
95
                    'BackupTransportHandler')
120
96
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
124
100
        finally:
125
101
            _set_protocol_handlers(saved_handlers)
126
102
 
127
 
    def test_LateReadError(self):
128
 
        """The LateReadError helper should raise on read()."""
129
 
        a_file = LateReadError('a path')
130
 
        try:
131
 
            a_file.read()
132
 
        except ReadError, error:
133
 
            self.assertEqual('a path', error.path)
134
 
        self.assertRaises(ReadError, a_file.read, 40)
135
 
        a_file.close()
136
 
 
137
 
    def test__combine_paths(self):
138
 
        t = Transport('/')
139
 
        self.assertEqual('/home/sarah/project/foo',
140
 
                         t._combine_paths('/home/sarah', 'project/foo'))
141
 
        self.assertEqual('/etc',
142
 
                         t._combine_paths('/home/sarah', '../../etc'))
143
 
        self.assertEqual('/etc',
144
 
                         t._combine_paths('/home/sarah', '../../../etc'))
145
 
        self.assertEqual('/etc',
146
 
                         t._combine_paths('/home/sarah', '/etc'))
147
 
 
148
 
    def test_local_abspath_non_local_transport(self):
149
 
        # the base implementation should throw
150
 
        t = MemoryTransport()
151
 
        e = self.assertRaises(errors.NotLocalUrl, t.local_abspath, 't')
152
 
        self.assertEqual('memory:///t is not a local path.', str(e))
153
 
 
154
103
 
155
104
class TestCoalesceOffsets(TestCase):
156
105
    
219
168
    def test_clone(self):
220
169
        transport = MemoryTransport()
221
170
        self.assertTrue(isinstance(transport, MemoryTransport))
222
 
        self.assertEqual("memory:///", transport.clone("/").base)
223
171
 
224
172
    def test_abspath(self):
225
173
        transport = MemoryTransport()
226
174
        self.assertEqual("memory:///relpath", transport.abspath('relpath'))
227
175
 
228
 
    def test_abspath_of_root(self):
229
 
        transport = MemoryTransport()
230
 
        self.assertEqual("memory:///", transport.base)
231
 
        self.assertEqual("memory:///", transport.abspath('/'))
232
 
 
233
 
    def test_abspath_of_relpath_starting_at_root(self):
234
 
        transport = MemoryTransport()
235
 
        self.assertEqual("memory:///foo", transport.abspath('/foo'))
 
176
    def test_relpath(self):
 
177
        transport = MemoryTransport()
236
178
 
237
179
    def test_append_and_get(self):
238
180
        transport = MemoryTransport()
239
 
        transport.append_bytes('path', 'content')
 
181
        transport.append('path', StringIO('content'))
240
182
        self.assertEqual(transport.get('path').read(), 'content')
241
 
        transport.append_file('path', StringIO('content'))
 
183
        transport.append('path', StringIO('content'))
242
184
        self.assertEqual(transport.get('path').read(), 'contentcontent')
243
185
 
244
186
    def test_put_and_get(self):
245
187
        transport = MemoryTransport()
246
 
        transport.put_file('path', StringIO('content'))
 
188
        transport.put('path', StringIO('content'))
247
189
        self.assertEqual(transport.get('path').read(), 'content')
248
 
        transport.put_bytes('path', 'content')
 
190
        transport.put('path', StringIO('content'))
249
191
        self.assertEqual(transport.get('path').read(), 'content')
250
192
 
251
193
    def test_append_without_dir_fails(self):
252
194
        transport = MemoryTransport()
253
195
        self.assertRaises(NoSuchFile,
254
 
                          transport.append_bytes, 'dir/path', 'content')
 
196
                          transport.append, 'dir/path', StringIO('content'))
255
197
 
256
198
    def test_put_without_dir_fails(self):
257
199
        transport = MemoryTransport()
258
200
        self.assertRaises(NoSuchFile,
259
 
                          transport.put_file, 'dir/path', StringIO('content'))
 
201
                          transport.put, 'dir/path', StringIO('content'))
260
202
 
261
203
    def test_get_missing(self):
262
204
        transport = MemoryTransport()
268
210
 
269
211
    def test_has_present(self):
270
212
        transport = MemoryTransport()
271
 
        transport.append_bytes('foo', 'content')
 
213
        transport.append('foo', StringIO('content'))
272
214
        self.assertEquals(True, transport.has('foo'))
273
215
 
274
 
    def test_list_dir(self):
275
 
        transport = MemoryTransport()
276
 
        transport.put_bytes('foo', 'content')
277
 
        transport.mkdir('dir')
278
 
        transport.put_bytes('dir/subfoo', 'content')
279
 
        transport.put_bytes('dirlike', 'content')
280
 
 
281
 
        self.assertEquals(['dir', 'dirlike', 'foo'], sorted(transport.list_dir('.')))
282
 
        self.assertEquals(['subfoo'], sorted(transport.list_dir('dir')))
283
 
 
284
216
    def test_mkdir(self):
285
217
        transport = MemoryTransport()
286
218
        transport.mkdir('dir')
287
 
        transport.append_bytes('dir/path', 'content')
 
219
        transport.append('dir/path', StringIO('content'))
288
220
        self.assertEqual(transport.get('dir/path').read(), 'content')
289
221
 
290
222
    def test_mkdir_missing_parent(self):
300
232
    def test_parameters(self):
301
233
        transport = MemoryTransport()
302
234
        self.assertEqual(True, transport.listable())
 
235
        self.assertEqual(False, transport.should_cache())
303
236
        self.assertEqual(False, transport.is_readonly())
304
237
 
305
238
    def test_iter_files_recursive(self):
306
239
        transport = MemoryTransport()
307
240
        transport.mkdir('dir')
308
 
        transport.put_bytes('dir/foo', 'content')
309
 
        transport.put_bytes('dir/bar', 'content')
310
 
        transport.put_bytes('bar', 'content')
 
241
        transport.put('dir/foo', StringIO('content'))
 
242
        transport.put('dir/bar', StringIO('content'))
 
243
        transport.put('bar', StringIO('content'))
311
244
        paths = set(transport.iter_files_recursive())
312
245
        self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
313
246
 
314
247
    def test_stat(self):
315
248
        transport = MemoryTransport()
316
 
        transport.put_bytes('foo', 'content')
317
 
        transport.put_bytes('bar', 'phowar')
 
249
        transport.put('foo', StringIO('content'))
 
250
        transport.put('bar', StringIO('phowar'))
318
251
        self.assertEqual(7, transport.stat('foo').st_size)
319
252
        self.assertEqual(6, transport.stat('bar').st_size)
320
253
 
321
 
 
322
 
class ChrootDecoratorTransportTest(TestCase):
323
 
    """Chroot decoration specific tests."""
324
 
 
325
 
    def test_abspath(self):
326
 
        # The abspath is always relative to the chroot_url.
327
 
        server = ChrootServer(get_transport('memory:///foo/bar/'))
328
 
        server.setUp()
329
 
        transport = get_transport(server.get_url())
330
 
        self.assertEqual(server.get_url(), transport.abspath('/'))
331
 
 
332
 
        subdir_transport = transport.clone('subdir')
333
 
        self.assertEqual(server.get_url(), subdir_transport.abspath('/'))
334
 
        server.tearDown()
335
 
 
336
 
    def test_clone(self):
337
 
        server = ChrootServer(get_transport('memory:///foo/bar/'))
338
 
        server.setUp()
339
 
        transport = get_transport(server.get_url())
340
 
        # relpath from root and root path are the same
341
 
        relpath_cloned = transport.clone('foo')
342
 
        abspath_cloned = transport.clone('/foo')
343
 
        self.assertEqual(server, relpath_cloned.server)
344
 
        self.assertEqual(server, abspath_cloned.server)
345
 
        server.tearDown()
346
 
    
347
 
    def test_chroot_url_preserves_chroot(self):
348
 
        """Calling get_transport on a chroot transport's base should produce a
349
 
        transport with exactly the same behaviour as the original chroot
350
 
        transport.
351
 
 
352
 
        This is so that it is not possible to escape a chroot by doing::
353
 
            url = chroot_transport.base
354
 
            parent_url = urlutils.join(url, '..')
355
 
            new_transport = get_transport(parent_url)
356
 
        """
357
 
        server = ChrootServer(get_transport('memory:///path/subpath'))
358
 
        server.setUp()
359
 
        transport = get_transport(server.get_url())
360
 
        new_transport = get_transport(transport.base)
361
 
        self.assertEqual(transport.server, new_transport.server)
362
 
        self.assertEqual(transport.base, new_transport.base)
363
 
        server.tearDown()
364
254
        
365
 
    def test_urljoin_preserves_chroot(self):
366
 
        """Using urlutils.join(url, '..') on a chroot URL should not produce a
367
 
        URL that escapes the intended chroot.
368
 
 
369
 
        This is so that it is not possible to escape a chroot by doing::
370
 
            url = chroot_transport.base
371
 
            parent_url = urlutils.join(url, '..')
372
 
            new_transport = get_transport(parent_url)
373
 
        """
374
 
        server = ChrootServer(get_transport('memory:///path/'))
375
 
        server.setUp()
376
 
        transport = get_transport(server.get_url())
377
 
        self.assertRaises(
378
 
            InvalidURLJoin, urlutils.join, transport.base, '..')
379
 
        server.tearDown()
380
 
 
381
 
 
382
 
class ChrootServerTest(TestCase):
383
 
 
384
 
    def test_construct(self):
385
 
        backing_transport = MemoryTransport()
386
 
        server = ChrootServer(backing_transport)
387
 
        self.assertEqual(backing_transport, server.backing_transport)
388
 
 
389
 
    def test_setUp(self):
390
 
        backing_transport = MemoryTransport()
391
 
        server = ChrootServer(backing_transport)
392
 
        server.setUp()
393
 
        self.assertTrue(server.scheme in _get_protocol_handlers().keys())
394
 
 
395
 
    def test_tearDown(self):
396
 
        backing_transport = MemoryTransport()
397
 
        server = ChrootServer(backing_transport)
398
 
        server.setUp()
399
 
        server.tearDown()
400
 
        self.assertFalse(server.scheme in _get_protocol_handlers().keys())
401
 
 
402
 
    def test_get_url(self):
403
 
        backing_transport = MemoryTransport()
404
 
        server = ChrootServer(backing_transport)
405
 
        server.setUp()
406
 
        self.assertEqual('chroot-%d:///' % id(server), server.get_url())
407
 
        server.tearDown()
408
 
 
409
 
 
410
255
class ReadonlyDecoratorTransportTest(TestCase):
411
256
    """Readonly decoration specific tests."""
412
257
 
415
260
        # connect to . in readonly mode
416
261
        transport = readonly.ReadonlyTransportDecorator('readonly+.')
417
262
        self.assertEqual(True, transport.listable())
 
263
        self.assertEqual(False, transport.should_cache())
418
264
        self.assertEqual(True, transport.is_readonly())
419
265
 
420
266
    def test_http_parameters(self):
421
 
        from bzrlib.tests.HttpServer import HttpServer
422
267
        import bzrlib.transport.readonly as readonly
 
268
        from bzrlib.transport.http import HttpServer
423
269
        # connect to . via http which is not listable
424
270
        server = HttpServer()
425
271
        server.setUp()
428
274
            self.failUnless(isinstance(transport,
429
275
                                       readonly.ReadonlyTransportDecorator))
430
276
            self.assertEqual(False, transport.listable())
 
277
            self.assertEqual(True, transport.should_cache())
431
278
            self.assertEqual(True, transport.is_readonly())
432
279
        finally:
433
280
            server.tearDown()
442
289
        return fakenfs.FakeNFSTransportDecorator('fakenfs+' + url)
443
290
 
444
291
    def test_local_parameters(self):
445
 
        # the listable and is_readonly parameters
 
292
        # the listable, should_cache and is_readonly parameters
446
293
        # are not changed by the fakenfs decorator
447
294
        transport = self.get_nfs_transport('.')
448
295
        self.assertEqual(True, transport.listable())
 
296
        self.assertEqual(False, transport.should_cache())
449
297
        self.assertEqual(False, transport.is_readonly())
450
298
 
451
299
    def test_http_parameters(self):
452
 
        # the listable and is_readonly parameters
 
300
        # the listable, should_cache and is_readonly parameters
453
301
        # are not changed by the fakenfs decorator
454
 
        from bzrlib.tests.HttpServer import HttpServer
 
302
        from bzrlib.transport.http import HttpServer
455
303
        # connect to . via http which is not listable
456
304
        server = HttpServer()
457
305
        server.setUp()
460
308
            self.assertIsInstance(
461
309
                transport, bzrlib.transport.fakenfs.FakeNFSTransportDecorator)
462
310
            self.assertEqual(False, transport.listable())
 
311
            self.assertEqual(True, transport.should_cache())
463
312
            self.assertEqual(True, transport.is_readonly())
464
313
        finally:
465
314
            server.tearDown()
470
319
        server = fakenfs.FakeNFSServer()
471
320
        server.setUp()
472
321
        try:
473
 
            # the url should be decorated appropriately
474
 
            self.assertStartsWith(server.get_url(), 'fakenfs+')
 
322
            # the server should be a relpath localhost server
 
323
            self.assertEqual(server.get_url(), 'fakenfs+.')
475
324
            # and we should be able to get a transport for it
476
325
            transport = get_transport(server.get_url())
477
326
            # which must be a FakeNFSTransportDecorator instance.
486
335
        transport = self.get_nfs_transport('.')
487
336
        self.build_tree(['from/', 'from/foo', 'to/', 'to/bar'],
488
337
                        transport=transport)
489
 
        self.assertRaises(errors.ResourceBusy,
 
338
        self.assertRaises(bzrlib.errors.ResourceBusy,
490
339
                          transport.rename, 'from', 'to')
491
340
 
492
341
 
550
399
        super(TestTransportImplementation, self).setUp()
551
400
        self._server = self.transport_server()
552
401
        self._server.setUp()
553
 
        self.addCleanup(self._server.tearDown)
554
 
 
555
 
    def get_transport(self, relpath=None):
556
 
        """Return a connected transport to the local directory.
557
 
 
558
 
        :param relpath: a path relative to the base url.
559
 
        """
 
402
 
 
403
    def tearDown(self):
 
404
        super(TestTransportImplementation, self).tearDown()
 
405
        self._server.tearDown()
 
406
        
 
407
    def get_transport(self):
 
408
        """Return a connected transport to the local directory."""
560
409
        base_url = self._server.get_url()
561
 
        url = self._adjust_url(base_url, relpath)
562
410
        # try getting the transport via the regular interface:
563
 
        t = get_transport(url)
564
 
        # vila--20070607 if the following are commented out the test suite
565
 
        # still pass. Is this really still needed or was it a forgotten
566
 
        # temporary fix ?
567
 
        if not isinstance(t, self.transport_class):
 
411
        t = get_transport(base_url)
 
412
        if not isinstance(t, self.transport_class): 
568
413
            # we did not get the correct transport class type. Override the
569
414
            # regular connection behaviour by direct construction.
570
 
            t = self.transport_class(url)
 
415
            t = self.transport_class(base_url)
571
416
        return t
572
 
 
573
 
 
574
 
class TestLocalTransports(TestCase):
575
 
 
576
 
    def test_get_transport_from_abspath(self):
577
 
        here = osutils.abspath('.')
578
 
        t = get_transport(here)
579
 
        self.assertIsInstance(t, LocalTransport)
580
 
        self.assertEquals(t.base, urlutils.local_path_to_url(here) + '/')
581
 
 
582
 
    def test_get_transport_from_relpath(self):
583
 
        here = osutils.abspath('.')
584
 
        t = get_transport('.')
585
 
        self.assertIsInstance(t, LocalTransport)
586
 
        self.assertEquals(t.base, urlutils.local_path_to_url('.') + '/')
587
 
 
588
 
    def test_get_transport_from_local_url(self):
589
 
        here = osutils.abspath('.')
590
 
        here_url = urlutils.local_path_to_url(here) + '/'
591
 
        t = get_transport(here_url)
592
 
        self.assertIsInstance(t, LocalTransport)
593
 
        self.assertEquals(t.base, here_url)
594
 
 
595
 
    def test_local_abspath(self):
596
 
        here = osutils.abspath('.')
597
 
        t = get_transport(here)
598
 
        self.assertEquals(t.local_abspath(''), here)
599
 
 
600
 
 
601
 
class TestWin32LocalTransport(TestCase):
602
 
 
603
 
    def test_unc_clone_to_root(self):
604
 
        # Win32 UNC path like \\HOST\path
605
 
        # clone to root should stop at least at \\HOST part
606
 
        # not on \\
607
 
        t = EmulatedWin32LocalTransport('file://HOST/path/to/some/dir/')
608
 
        for i in xrange(4):
609
 
            t = t.clone('..')
610
 
        self.assertEquals(t.base, 'file://HOST/')
611
 
        # make sure we reach the root
612
 
        t = t.clone('..')
613
 
        self.assertEquals(t.base, 'file://HOST/')
614
 
 
615
 
 
616
 
class TestConnectedTransport(TestCase):
617
 
    """Tests for connected to remote server transports"""
618
 
 
619
 
    def test_parse_url(self):
620
 
        t = ConnectedTransport('http://simple.example.com/home/source')
621
 
        self.assertEquals(t._host, 'simple.example.com')
622
 
        self.assertEquals(t._port, 80)
623
 
        self.assertEquals(t._path, '/home/source/')
624
 
        self.failUnless(t._user is None)
625
 
        self.failUnless(t._password is None)
626
 
 
627
 
        self.assertEquals(t.base, 'http://simple.example.com/home/source/')
628
 
 
629
 
    def test_parse_quoted_url(self):
630
 
        t = ConnectedTransport('http://ro%62ey:h%40t@ex%41mple.com:2222/path')
631
 
        self.assertEquals(t._host, 'exAmple.com')
632
 
        self.assertEquals(t._port, 2222)
633
 
        self.assertEquals(t._user, 'robey')
634
 
        self.assertEquals(t._password, 'h@t')
635
 
        self.assertEquals(t._path, '/path/')
636
 
 
637
 
        # Base should not keep track of the password
638
 
        self.assertEquals(t.base, 'http://robey@exAmple.com:2222/path/')
639
 
 
640
 
    def test_parse_invalid_url(self):
641
 
        self.assertRaises(errors.InvalidURL,
642
 
                          ConnectedTransport,
643
 
                          'sftp://lily.org:~janneke/public/bzr/gub')
644
 
 
645
 
    def test_relpath(self):
646
 
        t = ConnectedTransport('sftp://user@host.com/abs/path')
647
 
 
648
 
        self.assertEquals(t.relpath('sftp://user@host.com/abs/path/sub'), 'sub')
649
 
        self.assertRaises(errors.PathNotChild, t.relpath,
650
 
                          'http://user@host.com/abs/path/sub')
651
 
        self.assertRaises(errors.PathNotChild, t.relpath,
652
 
                          'sftp://user2@host.com/abs/path/sub')
653
 
        self.assertRaises(errors.PathNotChild, t.relpath,
654
 
                          'sftp://user@otherhost.com/abs/path/sub')
655
 
        self.assertRaises(errors.PathNotChild, t.relpath,
656
 
                          'sftp://user@host.com:33/abs/path/sub')
657
 
        # Make sure it works when we don't supply a username
658
 
        t = ConnectedTransport('sftp://host.com/abs/path')
659
 
        self.assertEquals(t.relpath('sftp://host.com/abs/path/sub'), 'sub')
660
 
 
661
 
        # Make sure it works when parts of the path will be url encoded
662
 
        t = ConnectedTransport('sftp://host.com/dev/%path')
663
 
        self.assertEquals(t.relpath('sftp://host.com/dev/%path/sub'), 'sub')
664
 
 
665
 
    def test_connection_sharing_propagate_credentials(self):
666
 
        t = ConnectedTransport('foo://user@host.com/abs/path')
667
 
        self.assertIs(None, t._get_connection())
668
 
        self.assertIs(None, t._password)
669
 
        c = t.clone('subdir')
670
 
        self.assertEquals(None, c._get_connection())
671
 
        self.assertIs(None, t._password)
672
 
 
673
 
        # Simulate the user entering a password
674
 
        password = 'secret'
675
 
        connection = object()
676
 
        t._set_connection(connection, password)
677
 
        self.assertIs(connection, t._get_connection())
678
 
        self.assertIs(password, t._get_credentials())
679
 
        self.assertIs(connection, c._get_connection())
680
 
        self.assertIs(password, c._get_credentials())
681
 
 
682
 
        # credentials can be updated
683
 
        new_password = 'even more secret'
684
 
        c._update_credentials(new_password)
685
 
        self.assertIs(connection, t._get_connection())
686
 
        self.assertIs(new_password, t._get_credentials())
687
 
        self.assertIs(connection, c._get_connection())
688
 
        self.assertIs(new_password, c._get_credentials())
689
 
 
690
 
 
691
 
class TestReusedTransports(TestCase):
692
 
    """Tests for transport reuse"""
693
 
 
694
 
    def test_reuse_same_transport(self):
695
 
        possible_transports = []
696
 
        t1 = get_transport('http://foo/',
697
 
                           possible_transports=possible_transports)
698
 
        self.assertEqual([t1], possible_transports)
699
 
        t2 = get_transport('http://foo/', possible_transports=[t1])
700
 
        self.assertIs(t1, t2)
701
 
 
702
 
        # Also check that final '/' are handled correctly
703
 
        t3 = get_transport('http://foo/path/')
704
 
        t4 = get_transport('http://foo/path', possible_transports=[t3])
705
 
        self.assertIs(t3, t4)
706
 
 
707
 
        t5 = get_transport('http://foo/path')
708
 
        t6 = get_transport('http://foo/path/', possible_transports=[t5])
709
 
        self.assertIs(t5, t6)
710
 
 
711
 
    def test_don_t_reuse_different_transport(self):
712
 
        t1 = get_transport('http://foo/path')
713
 
        t2 = get_transport('http://bar/path', possible_transports=[t1])
714
 
        self.assertIsNot(t1, t2)
715
 
 
716
 
 
717
 
class TestRemoteTCPTransport(TestCase):
718
 
    """Tests for bzr:// transport (RemoteTCPTransport)."""
719
 
 
720
 
    def test_relpath_with_implicit_port(self):
721
 
        """Connected transports with the same URL are the same, even if the
722
 
        port is implicit.
723
 
 
724
 
        So t.relpath(url) should always be '' if t.base is the same as url, or
725
 
        if the only difference is that one explicitly specifies the default
726
 
        port and the other doesn't specify a port.
727
 
        """
728
 
        t_implicit_port = RemoteTCPTransport('bzr://host.com/')
729
 
        self.assertEquals('', t_implicit_port.relpath('bzr://host.com/'))
730
 
        self.assertEquals('', t_implicit_port.relpath('bzr://host.com:4155/'))
731
 
        t_explicit_port = RemoteTCPTransport('bzr://host.com:4155/')
732
 
        self.assertEquals('', t_explicit_port.relpath('bzr://host.com/'))
733
 
        self.assertEquals('', t_explicit_port.relpath('bzr://host.com:4155/'))
734
 
 
735
 
    def test_construct_uses_default_port(self):
736
 
        """If no port is specified, then RemoteTCPTransport uses
737
 
        BZR_DEFAULT_PORT.
738
 
        """
739
 
        t = get_transport('bzr://host.com/')
740
 
        self.assertEquals(BZR_DEFAULT_PORT, t._port)
741
 
 
742
 
    def test_url_omits_default_port(self):
743
 
        """If a RemoteTCPTransport uses the default port, then its base URL
744
 
        will omit the port.
745
 
 
746
 
        This is like how ":80" is omitted from "http://example.com/".
747
 
        """
748
 
        t = get_transport('bzr://host.com:4155/')
749
 
        self.assertEquals('bzr://host.com/', t.base)
750
 
 
751
 
    def test_url_includes_non_default_port(self):
752
 
        """Non-default ports are included in the transport's URL.
753
 
 
754
 
        Contrast this to `test_url_omits_default_port`.
755
 
        """
756
 
        t = get_transport('bzr://host.com:666/')
757
 
        self.assertEquals('bzr://host.com:666/', t.base)
758
 
 
759
 
 
760
 
class SSHPortTestMixin(object):
761
 
    """Mixin class for testing SSH-based transports' use of ports in URLs.
762
 
    
763
 
    Unlike other connected transports, SSH-based transports (sftp, bzr+ssh)
764
 
    don't have a default port, because the user may have OpenSSH configured to
765
 
    use a non-standard port.
766
 
    """
767
 
 
768
 
    def make_url(self, netloc):
769
 
        """Make a url for the given netloc, using the scheme defined on the
770
 
        TestCase.
771
 
        """
772
 
        return '%s://%s/' % (self.scheme, netloc)
773
 
 
774
 
    def test_relpath_with_implicit_port(self):
775
 
        """SSH-based transports with the same URL are the same.
776
 
        
777
 
        Note than an unspecified port number is different to port 22 (because
778
 
        OpenSSH may be configured to use a non-standard port).
779
 
 
780
 
        So t.relpath(url) should always be '' if t.base is the same as url, but
781
 
        raise PathNotChild if the ports in t and url are not both specified (or
782
 
        both unspecified).
783
 
        """
784
 
        url_implicit_port = self.make_url('host.com')
785
 
        url_explicit_port = self.make_url('host.com:22')
786
 
 
787
 
        t_implicit_port = get_transport(url_implicit_port)
788
 
        self.assertEquals('', t_implicit_port.relpath(url_implicit_port))
789
 
        self.assertRaises(
790
 
            PathNotChild, t_implicit_port.relpath, url_explicit_port)
791
 
        
792
 
        t_explicit_port = get_transport(url_explicit_port)
793
 
        self.assertRaises(
794
 
            PathNotChild, t_explicit_port.relpath, url_implicit_port)
795
 
        self.assertEquals('', t_explicit_port.relpath(url_explicit_port))
796
 
 
797
 
    def test_construct_with_no_port(self):
798
 
        """If no port is specified, then the SSH-based transport's _port will
799
 
        be None.
800
 
        """
801
 
        t = get_transport(self.make_url('host.com'))
802
 
        self.assertEquals(None, t._port)
803
 
 
804
 
    def test_url_with_no_port(self):
805
 
        """If no port was specified, none is shown in the base URL."""
806
 
        t = get_transport(self.make_url('host.com'))
807
 
        self.assertEquals(self.make_url('host.com'), t.base)
808
 
 
809
 
    def test_url_includes_port(self):
810
 
        """An SSH-based transport's base will show the port if one was
811
 
        specified, even if that port is 22, because we do not assume 22 is the
812
 
        default port.
813
 
        """
814
 
        # 22 is the "standard" port for SFTP.
815
 
        t = get_transport(self.make_url('host.com:22'))
816
 
        self.assertEquals(self.make_url('host.com:22'), t.base)
817
 
        # 666 is not a standard port.
818
 
        t = get_transport(self.make_url('host.com:666'))
819
 
        self.assertEquals(self.make_url('host.com:666'), t.base)
820
 
 
821
 
 
822
 
class SFTPTransportPortTest(TestCase, SSHPortTestMixin):
823
 
    """Tests for sftp:// transport (SFTPTransport)."""
824
 
 
825
 
    scheme = 'sftp'
826
 
 
827
 
 
828
 
class BzrSSHTransportPortTest(TestCase, SSHPortTestMixin):
829
 
    """Tests for bzr+ssh:// transport (RemoteSSHTransport)."""
830
 
 
831
 
    scheme = 'bzr+ssh'
832
 
 
833
 
 
834
 
class TestTransportTrace(TestCase):
835
 
 
836
 
    def test_get(self):
837
 
        transport = get_transport('trace+memory://')
838
 
        self.assertIsInstance(
839
 
            transport, bzrlib.transport.trace.TransportTraceDecorator)
840
 
 
841
 
    def test_clone_preserves_activity(self):
842
 
        transport = get_transport('trace+memory://')
843
 
        transport2 = transport.clone('.')
844
 
        self.assertTrue(transport is not transport2)
845
 
        self.assertTrue(transport._activity is transport2._activity)
846
 
 
847
 
    # the following specific tests are for the operations that have made use of
848
 
    # logging in tests; we could test every single operation but doing that
849
 
    # still won't cause a test failure when the top level Transport API
850
 
    # changes; so there is little return doing that.
851
 
    def test_get(self):
852
 
        transport = get_transport('trace+memory:///')
853
 
        transport.put_bytes('foo', 'barish')
854
 
        transport.get('foo')
855
 
        expected_result = []
856
 
        # put_bytes records the bytes, not the content to avoid memory
857
 
        # pressure.
858
 
        expected_result.append(('put_bytes', 'foo', 6, None))
859
 
        # get records the file name only.
860
 
        expected_result.append(('get', 'foo'))
861
 
        self.assertEqual(expected_result, transport._activity)
862
 
 
863
 
    def test_readv(self):
864
 
        transport = get_transport('trace+memory:///')
865
 
        transport.put_bytes('foo', 'barish')
866
 
        list(transport.readv('foo', [(0, 1), (3, 2)], adjust_for_latency=True,
867
 
            upper_limit=6))
868
 
        expected_result = []
869
 
        # put_bytes records the bytes, not the content to avoid memory
870
 
        # pressure.
871
 
        expected_result.append(('put_bytes', 'foo', 6, None))
872
 
        # readv records the supplied offset request
873
 
        expected_result.append(('readv', 'foo', [(0, 1), (3, 2)], True, 6))
874
 
        self.assertEqual(expected_result, transport._activity)