~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transport.py

  • Committer: Martin Pool
  • Date: 2007-04-04 06:17:31 UTC
  • mto: This revision was merged to the branch mainline in revision 2397.
  • Revision ID: mbp@sourcefrog.net-20070404061731-tt2xrzllqhbodn83
Contents of TODO file moved into bug tracker

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 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,
 
24
from bzrlib import urlutils
 
25
from bzrlib.errors import (ConnectionError,
 
26
                           DependencyNotPresent,
27
27
                           FileExists,
28
28
                           InvalidURLJoin,
29
29
                           NoSuchFile,
30
30
                           PathNotChild,
31
 
                           ReadError,
 
31
                           TransportNotPossible,
32
32
                           UnsupportedProtocol,
33
33
                           )
34
34
from bzrlib.tests import TestCase, TestCaseInTempDir
35
 
from bzrlib.transport import (_clear_protocol_handlers,
36
 
                              _CoalescedOffset,
37
 
                              ConnectedTransport,
 
35
from bzrlib.transport import (_CoalescedOffset,
38
36
                              _get_protocol_handlers,
39
 
                              _set_protocol_handlers,
40
37
                              _get_transport_modules,
41
38
                              get_transport,
42
 
                              LateReadError,
 
39
                              _protocol_handlers,
43
40
                              register_lazy_transport,
44
 
                              register_transport_proto,
 
41
                              _set_protocol_handlers,
45
42
                              Transport,
46
43
                              )
47
44
from bzrlib.transport.chroot import ChrootServer
58
55
 
59
56
    def test__get_set_protocol_handlers(self):
60
57
        handlers = _get_protocol_handlers()
61
 
        self.assertNotEqual([], handlers.keys( ))
 
58
        self.assertNotEqual({}, handlers)
62
59
        try:
63
 
            _clear_protocol_handlers()
64
 
            self.assertEqual([], _get_protocol_handlers().keys())
 
60
            _set_protocol_handlers({})
 
61
            self.assertEqual({}, _get_protocol_handlers())
65
62
        finally:
66
63
            _set_protocol_handlers(handlers)
67
64
 
68
65
    def test_get_transport_modules(self):
69
66
        handlers = _get_protocol_handlers()
70
 
        # don't pollute the current handlers
71
 
        _clear_protocol_handlers()
72
67
        class SampleHandler(object):
73
68
            """I exist, isnt that enough?"""
74
69
        try:
75
 
            _clear_protocol_handlers()
76
 
            register_transport_proto('foo')
77
 
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
78
 
                                    'TestTransport.SampleHandler')
79
 
            register_transport_proto('bar')
80
 
            register_lazy_transport('bar', 'bzrlib.tests.test_transport',
81
 
                                    'TestTransport.SampleHandler')
82
 
            self.assertEqual([SampleHandler.__module__,
83
 
                              'bzrlib.transport.chroot'],
 
70
            my_handlers = {}
 
71
            _set_protocol_handlers(my_handlers)
 
72
            register_lazy_transport('foo', 'bzrlib.tests.test_transport', 'TestTransport.SampleHandler')
 
73
            register_lazy_transport('bar', 'bzrlib.tests.test_transport', 'TestTransport.SampleHandler')
 
74
            self.assertEqual([SampleHandler.__module__, 'bzrlib.transport.chroot'],
84
75
                             _get_transport_modules())
85
76
        finally:
86
77
            _set_protocol_handlers(handlers)
88
79
    def test_transport_dependency(self):
89
80
        """Transport with missing dependency causes no error"""
90
81
        saved_handlers = _get_protocol_handlers()
91
 
        # don't pollute the current handlers
92
 
        _clear_protocol_handlers()
93
82
        try:
94
 
            register_transport_proto('foo')
95
83
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
96
84
                    'BadTransportHandler')
97
85
            try:
112
100
        """Transport with missing dependency causes no error"""
113
101
        saved_handlers = _get_protocol_handlers()
114
102
        try:
115
 
            _clear_protocol_handlers()
116
 
            register_transport_proto('foo')
 
103
            _set_protocol_handlers({})
117
104
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
118
105
                    'BackupTransportHandler')
119
106
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
123
110
        finally:
124
111
            _set_protocol_handlers(saved_handlers)
125
112
 
126
 
    def test_LateReadError(self):
127
 
        """The LateReadError helper should raise on read()."""
128
 
        a_file = LateReadError('a path')
129
 
        try:
130
 
            a_file.read()
131
 
        except ReadError, error:
132
 
            self.assertEqual('a path', error.path)
133
 
        self.assertRaises(ReadError, a_file.read, 40)
134
 
        a_file.close()
135
 
 
136
113
    def test__combine_paths(self):
137
114
        t = Transport('/')
138
115
        self.assertEqual('/home/sarah/project/foo',
144
121
        self.assertEqual('/etc',
145
122
                         t._combine_paths('/home/sarah', '/etc'))
146
123
 
147
 
    def test_local_abspath_non_local_transport(self):
148
 
        # the base implementation should throw
149
 
        t = MemoryTransport()
150
 
        e = self.assertRaises(errors.NotLocalUrl, t.local_abspath, 't')
151
 
        self.assertEqual('memory:///t is not a local path.', str(e))
152
 
 
153
124
 
154
125
class TestCoalesceOffsets(TestCase):
155
 
 
156
 
    def check(self, expected, offsets, limit=0, max_size=0, fudge=0):
 
126
    
 
127
    def check(self, expected, offsets, limit=0, fudge=0):
157
128
        coalesce = Transport._coalesce_offsets
158
129
        exp = [_CoalescedOffset(*x) for x in expected]
159
 
        out = list(coalesce(offsets, limit=limit, fudge_factor=fudge,
160
 
                            max_size=max_size))
 
130
        out = list(coalesce(offsets, limit=limit, fudge_factor=fudge))
161
131
        self.assertEqual(exp, out)
162
132
 
163
133
    def test_coalesce_empty(self):
170
140
        self.check([(0, 10, [(0, 10)]),
171
141
                    (20, 10, [(0, 10)]),
172
142
                   ], [(0, 10), (20, 10)])
173
 
 
 
143
            
174
144
    def test_coalesce_unsorted(self):
175
145
        self.check([(20, 10, [(0, 10)]),
176
146
                    (0, 10, [(0, 10)]),
181
151
                   [(0, 10), (10, 10)])
182
152
 
183
153
    def test_coalesce_overlapped(self):
184
 
        self.assertRaises(ValueError,
185
 
            self.check, [(0, 15, [(0, 10), (5, 10)])],
186
 
                        [(0, 10), (5, 10)])
 
154
        self.check([(0, 15, [(0, 10), (5, 10)])],
 
155
                   [(0, 10), (5, 10)])
187
156
 
188
157
    def test_coalesce_limit(self):
189
158
        self.check([(10, 50, [(0, 10), (10, 10), (20, 10),
210
179
                   ], [(10, 10), (30, 10), (100, 10)],
211
180
                   fudge=10
212
181
                  )
213
 
    def test_coalesce_max_size(self):
214
 
        self.check([(10, 20, [(0, 10), (10, 10)]),
215
 
                    (30, 50, [(0, 50)]),
216
 
                    # If one range is above max_size, it gets its own coalesced
217
 
                    # offset
218
 
                    (100, 80, [(0, 80),]),],
219
 
                   [(10, 10), (20, 10), (30, 50), (100, 80)],
220
 
                   max_size=50
221
 
                  )
222
 
 
223
 
    def test_coalesce_no_max_size(self):
224
 
        self.check([(10, 170, [(0, 10), (10, 10), (20, 50), (70, 100)]),],
225
 
                   [(10, 10), (20, 10), (30, 50), (80, 100)],
226
 
                  )
227
 
 
228
 
    def test_coalesce_default_limit(self):
229
 
        # By default we use a 100MB max size.
230
 
        ten_mb = 10*1024*1024
231
 
        self.check([(0, 10*ten_mb, [(i*ten_mb, ten_mb) for i in range(10)]),
232
 
                    (10*ten_mb, ten_mb, [(0, ten_mb)])],
233
 
                   [(i*ten_mb, ten_mb) for i in range(11)])
234
 
        self.check([(0, 11*ten_mb, [(i*ten_mb, ten_mb) for i in range(11)]),],
235
 
                   [(i*ten_mb, ten_mb) for i in range(11)],
236
 
                   max_size=1*1024*1024*1024)
237
182
 
238
183
 
239
184
class TestMemoryTransport(TestCase):
325
270
    def test_parameters(self):
326
271
        transport = MemoryTransport()
327
272
        self.assertEqual(True, transport.listable())
 
273
        self.assertEqual(False, transport.should_cache())
328
274
        self.assertEqual(False, transport.is_readonly())
329
275
 
330
276
    def test_iter_files_recursive(self):
415
361
        backing_transport = MemoryTransport()
416
362
        server = ChrootServer(backing_transport)
417
363
        server.setUp()
418
 
        self.assertTrue(server.scheme in _get_protocol_handlers().keys())
 
364
        self.assertTrue(server.scheme in _protocol_handlers.keys())
419
365
 
420
366
    def test_tearDown(self):
421
367
        backing_transport = MemoryTransport()
422
368
        server = ChrootServer(backing_transport)
423
369
        server.setUp()
424
370
        server.tearDown()
425
 
        self.assertFalse(server.scheme in _get_protocol_handlers().keys())
 
371
        self.assertFalse(server.scheme in _protocol_handlers.keys())
426
372
 
427
373
    def test_get_url(self):
428
374
        backing_transport = MemoryTransport()
440
386
        # connect to . in readonly mode
441
387
        transport = readonly.ReadonlyTransportDecorator('readonly+.')
442
388
        self.assertEqual(True, transport.listable())
 
389
        self.assertEqual(False, transport.should_cache())
443
390
        self.assertEqual(True, transport.is_readonly())
444
391
 
445
392
    def test_http_parameters(self):
446
 
        from bzrlib.tests.http_server import HttpServer
 
393
        from bzrlib.tests.HttpServer import HttpServer
447
394
        import bzrlib.transport.readonly as readonly
448
 
        # connect to '.' via http which is not listable
 
395
        # connect to . via http which is not listable
449
396
        server = HttpServer()
450
397
        server.setUp()
451
398
        try:
453
400
            self.failUnless(isinstance(transport,
454
401
                                       readonly.ReadonlyTransportDecorator))
455
402
            self.assertEqual(False, transport.listable())
 
403
            self.assertEqual(True, transport.should_cache())
456
404
            self.assertEqual(True, transport.is_readonly())
457
405
        finally:
458
406
            server.tearDown()
467
415
        return fakenfs.FakeNFSTransportDecorator('fakenfs+' + url)
468
416
 
469
417
    def test_local_parameters(self):
470
 
        # the listable and is_readonly parameters
 
418
        # the listable, should_cache and is_readonly parameters
471
419
        # are not changed by the fakenfs decorator
472
420
        transport = self.get_nfs_transport('.')
473
421
        self.assertEqual(True, transport.listable())
 
422
        self.assertEqual(False, transport.should_cache())
474
423
        self.assertEqual(False, transport.is_readonly())
475
424
 
476
425
    def test_http_parameters(self):
477
 
        # the listable and is_readonly parameters
 
426
        # the listable, should_cache and is_readonly parameters
478
427
        # are not changed by the fakenfs decorator
479
 
        from bzrlib.tests.http_server import HttpServer
480
 
        # connect to '.' via http which is not listable
 
428
        from bzrlib.tests.HttpServer import HttpServer
 
429
        # connect to . via http which is not listable
481
430
        server = HttpServer()
482
431
        server.setUp()
483
432
        try:
485
434
            self.assertIsInstance(
486
435
                transport, bzrlib.transport.fakenfs.FakeNFSTransportDecorator)
487
436
            self.assertEqual(False, transport.listable())
 
437
            self.assertEqual(True, transport.should_cache())
488
438
            self.assertEqual(True, transport.is_readonly())
489
439
        finally:
490
440
            server.tearDown()
511
461
        transport = self.get_nfs_transport('.')
512
462
        self.build_tree(['from/', 'from/foo', 'to/', 'to/bar'],
513
463
                        transport=transport)
514
 
        self.assertRaises(errors.ResourceBusy,
 
464
        self.assertRaises(bzrlib.errors.ResourceBusy,
515
465
                          transport.rename, 'from', 'to')
516
466
 
517
467
 
577
527
        self._server.setUp()
578
528
        self.addCleanup(self._server.tearDown)
579
529
 
580
 
    def get_transport(self, relpath=None):
581
 
        """Return a connected transport to the local directory.
582
 
 
583
 
        :param relpath: a path relative to the base url.
584
 
        """
 
530
    def get_transport(self):
 
531
        """Return a connected transport to the local directory."""
585
532
        base_url = self._server.get_url()
586
 
        url = self._adjust_url(base_url, relpath)
587
533
        # try getting the transport via the regular interface:
588
 
        t = get_transport(url)
589
 
        # vila--20070607 if the following are commented out the test suite
590
 
        # still pass. Is this really still needed or was it a forgotten
591
 
        # temporary fix ?
 
534
        t = get_transport(base_url)
592
535
        if not isinstance(t, self.transport_class):
593
536
            # we did not get the correct transport class type. Override the
594
537
            # regular connection behaviour by direct construction.
595
 
            t = self.transport_class(url)
 
538
            t = self.transport_class(base_url)
596
539
        return t
597
540
 
598
541
 
599
542
class TestLocalTransports(TestCase):
600
543
 
601
544
    def test_get_transport_from_abspath(self):
602
 
        here = osutils.abspath('.')
 
545
        here = os.path.abspath('.')
603
546
        t = get_transport(here)
604
547
        self.assertIsInstance(t, LocalTransport)
605
548
        self.assertEquals(t.base, urlutils.local_path_to_url(here) + '/')
606
549
 
607
550
    def test_get_transport_from_relpath(self):
608
 
        here = osutils.abspath('.')
 
551
        here = os.path.abspath('.')
609
552
        t = get_transport('.')
610
553
        self.assertIsInstance(t, LocalTransport)
611
554
        self.assertEquals(t.base, urlutils.local_path_to_url('.') + '/')
612
555
 
613
556
    def test_get_transport_from_local_url(self):
614
 
        here = osutils.abspath('.')
 
557
        here = os.path.abspath('.')
615
558
        here_url = urlutils.local_path_to_url(here) + '/'
616
559
        t = get_transport(here_url)
617
560
        self.assertIsInstance(t, LocalTransport)
618
561
        self.assertEquals(t.base, here_url)
619
562
 
620
 
    def test_local_abspath(self):
621
 
        here = osutils.abspath('.')
622
 
        t = get_transport(here)
623
 
        self.assertEquals(t.local_abspath(''), here)
624
 
 
625
563
 
626
564
class TestWin32LocalTransport(TestCase):
627
565
 
636
574
        # make sure we reach the root
637
575
        t = t.clone('..')
638
576
        self.assertEquals(t.base, 'file://HOST/')
639
 
 
640
 
 
641
 
class TestConnectedTransport(TestCase):
642
 
    """Tests for connected to remote server transports"""
643
 
 
644
 
    def test_parse_url(self):
645
 
        t = ConnectedTransport('http://simple.example.com/home/source')
646
 
        self.assertEquals(t._host, 'simple.example.com')
647
 
        self.assertEquals(t._port, None)
648
 
        self.assertEquals(t._path, '/home/source/')
649
 
        self.failUnless(t._user is None)
650
 
        self.failUnless(t._password is None)
651
 
 
652
 
        self.assertEquals(t.base, 'http://simple.example.com/home/source/')
653
 
 
654
 
    def test_parse_url_with_at_in_user(self):
655
 
        # Bug 228058
656
 
        t = ConnectedTransport('ftp://user@host.com@www.host.com/')
657
 
        self.assertEquals(t._user, 'user@host.com')
658
 
 
659
 
    def test_parse_quoted_url(self):
660
 
        t = ConnectedTransport('http://ro%62ey:h%40t@ex%41mple.com:2222/path')
661
 
        self.assertEquals(t._host, 'exAmple.com')
662
 
        self.assertEquals(t._port, 2222)
663
 
        self.assertEquals(t._user, 'robey')
664
 
        self.assertEquals(t._password, 'h@t')
665
 
        self.assertEquals(t._path, '/path/')
666
 
 
667
 
        # Base should not keep track of the password
668
 
        self.assertEquals(t.base, 'http://robey@exAmple.com:2222/path/')
669
 
 
670
 
    def test_parse_invalid_url(self):
671
 
        self.assertRaises(errors.InvalidURL,
672
 
                          ConnectedTransport,
673
 
                          'sftp://lily.org:~janneke/public/bzr/gub')
674
 
 
675
 
    def test_relpath(self):
676
 
        t = ConnectedTransport('sftp://user@host.com/abs/path')
677
 
 
678
 
        self.assertEquals(t.relpath('sftp://user@host.com/abs/path/sub'), 'sub')
679
 
        self.assertRaises(errors.PathNotChild, t.relpath,
680
 
                          'http://user@host.com/abs/path/sub')
681
 
        self.assertRaises(errors.PathNotChild, t.relpath,
682
 
                          'sftp://user2@host.com/abs/path/sub')
683
 
        self.assertRaises(errors.PathNotChild, t.relpath,
684
 
                          'sftp://user@otherhost.com/abs/path/sub')
685
 
        self.assertRaises(errors.PathNotChild, t.relpath,
686
 
                          'sftp://user@host.com:33/abs/path/sub')
687
 
        # Make sure it works when we don't supply a username
688
 
        t = ConnectedTransport('sftp://host.com/abs/path')
689
 
        self.assertEquals(t.relpath('sftp://host.com/abs/path/sub'), 'sub')
690
 
 
691
 
        # Make sure it works when parts of the path will be url encoded
692
 
        t = ConnectedTransport('sftp://host.com/dev/%path')
693
 
        self.assertEquals(t.relpath('sftp://host.com/dev/%path/sub'), 'sub')
694
 
 
695
 
    def test_connection_sharing_propagate_credentials(self):
696
 
        t = ConnectedTransport('ftp://user@host.com/abs/path')
697
 
        self.assertEquals('user', t._user)
698
 
        self.assertEquals('host.com', t._host)
699
 
        self.assertIs(None, t._get_connection())
700
 
        self.assertIs(None, t._password)
701
 
        c = t.clone('subdir')
702
 
        self.assertIs(None, c._get_connection())
703
 
        self.assertIs(None, t._password)
704
 
 
705
 
        # Simulate the user entering a password
706
 
        password = 'secret'
707
 
        connection = object()
708
 
        t._set_connection(connection, password)
709
 
        self.assertIs(connection, t._get_connection())
710
 
        self.assertIs(password, t._get_credentials())
711
 
        self.assertIs(connection, c._get_connection())
712
 
        self.assertIs(password, c._get_credentials())
713
 
 
714
 
        # credentials can be updated
715
 
        new_password = 'even more secret'
716
 
        c._update_credentials(new_password)
717
 
        self.assertIs(connection, t._get_connection())
718
 
        self.assertIs(new_password, t._get_credentials())
719
 
        self.assertIs(connection, c._get_connection())
720
 
        self.assertIs(new_password, c._get_credentials())
721
 
 
722
 
 
723
 
class TestReusedTransports(TestCase):
724
 
    """Tests for transport reuse"""
725
 
 
726
 
    def test_reuse_same_transport(self):
727
 
        possible_transports = []
728
 
        t1 = get_transport('http://foo/',
729
 
                           possible_transports=possible_transports)
730
 
        self.assertEqual([t1], possible_transports)
731
 
        t2 = get_transport('http://foo/', possible_transports=[t1])
732
 
        self.assertIs(t1, t2)
733
 
 
734
 
        # Also check that final '/' are handled correctly
735
 
        t3 = get_transport('http://foo/path/')
736
 
        t4 = get_transport('http://foo/path', possible_transports=[t3])
737
 
        self.assertIs(t3, t4)
738
 
 
739
 
        t5 = get_transport('http://foo/path')
740
 
        t6 = get_transport('http://foo/path/', possible_transports=[t5])
741
 
        self.assertIs(t5, t6)
742
 
 
743
 
    def test_don_t_reuse_different_transport(self):
744
 
        t1 = get_transport('http://foo/path')
745
 
        t2 = get_transport('http://bar/path', possible_transports=[t1])
746
 
        self.assertIsNot(t1, t2)
747
 
 
748
 
 
749
 
class TestTransportTrace(TestCase):
750
 
 
751
 
    def test_get(self):
752
 
        transport = get_transport('trace+memory://')
753
 
        self.assertIsInstance(
754
 
            transport, bzrlib.transport.trace.TransportTraceDecorator)
755
 
 
756
 
    def test_clone_preserves_activity(self):
757
 
        transport = get_transport('trace+memory://')
758
 
        transport2 = transport.clone('.')
759
 
        self.assertTrue(transport is not transport2)
760
 
        self.assertTrue(transport._activity is transport2._activity)
761
 
 
762
 
    # the following specific tests are for the operations that have made use of
763
 
    # logging in tests; we could test every single operation but doing that
764
 
    # still won't cause a test failure when the top level Transport API
765
 
    # changes; so there is little return doing that.
766
 
    def test_get(self):
767
 
        transport = get_transport('trace+memory:///')
768
 
        transport.put_bytes('foo', 'barish')
769
 
        transport.get('foo')
770
 
        expected_result = []
771
 
        # put_bytes records the bytes, not the content to avoid memory
772
 
        # pressure.
773
 
        expected_result.append(('put_bytes', 'foo', 6, None))
774
 
        # get records the file name only.
775
 
        expected_result.append(('get', 'foo'))
776
 
        self.assertEqual(expected_result, transport._activity)
777
 
 
778
 
    def test_readv(self):
779
 
        transport = get_transport('trace+memory:///')
780
 
        transport.put_bytes('foo', 'barish')
781
 
        list(transport.readv('foo', [(0, 1), (3, 2)], adjust_for_latency=True,
782
 
            upper_limit=6))
783
 
        expected_result = []
784
 
        # put_bytes records the bytes, not the content to avoid memory
785
 
        # pressure.
786
 
        expected_result.append(('put_bytes', 'foo', 6, None))
787
 
        # readv records the supplied offset request
788
 
        expected_result.append(('readv', 'foo', [(0, 1), (3, 2)], True, 6))
789
 
        self.assertEqual(expected_result, transport._activity)