~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transport.py

  • Committer: Martin Pool
  • Date: 2005-09-13 05:22:41 UTC
  • Revision ID: mbp@sourcefrog.net-20050913052241-52dbd8e8ced620f6
- better BZR_DEBUG trace output

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
 
18
 
import os
19
 
import sys
20
 
import stat
21
 
from cStringIO import StringIO
22
 
 
23
 
import bzrlib
24
 
from bzrlib import urlutils
25
 
from bzrlib.errors import (NoSuchFile, FileExists,
26
 
                           TransportNotPossible,
27
 
                           ConnectionError,
28
 
                           DependencyNotPresent,
29
 
                           UnsupportedProtocol,
30
 
                           )
31
 
from bzrlib.tests import TestCase, TestCaseInTempDir
32
 
from bzrlib.transport import (_CoalescedOffset,
33
 
                              _get_protocol_handlers,
34
 
                              _get_transport_modules,
35
 
                              get_transport,
36
 
                              register_lazy_transport,
37
 
                              _set_protocol_handlers,
38
 
                              Transport,
39
 
                              )
40
 
from bzrlib.transport.memory import MemoryTransport
41
 
from bzrlib.transport.local import LocalTransport
42
 
 
43
 
 
44
 
# TODO: Should possibly split transport-specific tests into their own files.
45
 
 
46
 
 
47
 
class TestTransport(TestCase):
48
 
    """Test the non transport-concrete class functionality."""
49
 
 
50
 
    def test__get_set_protocol_handlers(self):
51
 
        handlers = _get_protocol_handlers()
52
 
        self.assertNotEqual({}, handlers)
53
 
        try:
54
 
            _set_protocol_handlers({})
55
 
            self.assertEqual({}, _get_protocol_handlers())
56
 
        finally:
57
 
            _set_protocol_handlers(handlers)
58
 
 
59
 
    def test_get_transport_modules(self):
60
 
        handlers = _get_protocol_handlers()
61
 
        class SampleHandler(object):
62
 
            """I exist, isnt that enough?"""
63
 
        try:
64
 
            my_handlers = {}
65
 
            _set_protocol_handlers(my_handlers)
66
 
            register_lazy_transport('foo', 'bzrlib.tests.test_transport', 'TestTransport.SampleHandler')
67
 
            register_lazy_transport('bar', 'bzrlib.tests.test_transport', 'TestTransport.SampleHandler')
68
 
            self.assertEqual([SampleHandler.__module__],
69
 
                             _get_transport_modules())
70
 
        finally:
71
 
            _set_protocol_handlers(handlers)
72
 
 
73
 
    def test_transport_dependency(self):
74
 
        """Transport with missing dependency causes no error"""
75
 
        saved_handlers = _get_protocol_handlers()
76
 
        try:
77
 
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
78
 
                    'BadTransportHandler')
79
 
            try:
80
 
                get_transport('foo://fooserver/foo')
81
 
            except UnsupportedProtocol, e:
82
 
                e_str = str(e)
83
 
                self.assertEquals('Unsupported protocol'
84
 
                                  ' for url "foo://fooserver/foo":'
85
 
                                  ' Unable to import library "some_lib":'
86
 
                                  ' testing missing dependency', str(e))
87
 
            else:
88
 
                self.fail('Did not raise UnsupportedProtocol')
89
 
        finally:
90
 
            # restore original values
91
 
            _set_protocol_handlers(saved_handlers)
92
 
            
93
 
    def test_transport_fallback(self):
94
 
        """Transport with missing dependency causes no error"""
95
 
        saved_handlers = _get_protocol_handlers()
96
 
        try:
97
 
            _set_protocol_handlers({})
98
 
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
99
 
                    'BackupTransportHandler')
100
 
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
101
 
                    'BadTransportHandler')
102
 
            t = get_transport('foo://fooserver/foo')
103
 
            self.assertTrue(isinstance(t, BackupTransportHandler))
104
 
        finally:
105
 
            _set_protocol_handlers(saved_handlers)
106
 
 
107
 
    def test__combine_paths(self):
108
 
        t = Transport('/')
109
 
        self.assertEqual('/home/sarah/project/foo',
110
 
                         t._combine_paths('/home/sarah', 'project/foo'))
111
 
        self.assertEqual('/etc',
112
 
                         t._combine_paths('/home/sarah', '../../etc'))
113
 
        self.assertEqual('/etc',
114
 
                         t._combine_paths('/home/sarah', '../../../etc'))
115
 
        self.assertEqual('/etc',
116
 
                         t._combine_paths('/home/sarah', '/etc'))
117
 
 
118
 
 
119
 
class TestCoalesceOffsets(TestCase):
120
 
    
121
 
    def check(self, expected, offsets, limit=0, fudge=0):
122
 
        coalesce = Transport._coalesce_offsets
123
 
        exp = [_CoalescedOffset(*x) for x in expected]
124
 
        out = list(coalesce(offsets, limit=limit, fudge_factor=fudge))
125
 
        self.assertEqual(exp, out)
126
 
 
127
 
    def test_coalesce_empty(self):
128
 
        self.check([], [])
129
 
 
130
 
    def test_coalesce_simple(self):
131
 
        self.check([(0, 10, [(0, 10)])], [(0, 10)])
132
 
 
133
 
    def test_coalesce_unrelated(self):
134
 
        self.check([(0, 10, [(0, 10)]),
135
 
                    (20, 10, [(0, 10)]),
136
 
                   ], [(0, 10), (20, 10)])
137
 
            
138
 
    def test_coalesce_unsorted(self):
139
 
        self.check([(20, 10, [(0, 10)]),
140
 
                    (0, 10, [(0, 10)]),
141
 
                   ], [(20, 10), (0, 10)])
142
 
 
143
 
    def test_coalesce_nearby(self):
144
 
        self.check([(0, 20, [(0, 10), (10, 10)])],
145
 
                   [(0, 10), (10, 10)])
146
 
 
147
 
    def test_coalesce_overlapped(self):
148
 
        self.check([(0, 15, [(0, 10), (5, 10)])],
149
 
                   [(0, 10), (5, 10)])
150
 
 
151
 
    def test_coalesce_limit(self):
152
 
        self.check([(10, 50, [(0, 10), (10, 10), (20, 10),
153
 
                              (30, 10), (40, 10)]),
154
 
                    (60, 50, [(0, 10), (10, 10), (20, 10),
155
 
                              (30, 10), (40, 10)]),
156
 
                   ], [(10, 10), (20, 10), (30, 10), (40, 10),
157
 
                       (50, 10), (60, 10), (70, 10), (80, 10),
158
 
                       (90, 10), (100, 10)],
159
 
                    limit=5)
160
 
 
161
 
    def test_coalesce_no_limit(self):
162
 
        self.check([(10, 100, [(0, 10), (10, 10), (20, 10),
163
 
                               (30, 10), (40, 10), (50, 10),
164
 
                               (60, 10), (70, 10), (80, 10),
165
 
                               (90, 10)]),
166
 
                   ], [(10, 10), (20, 10), (30, 10), (40, 10),
167
 
                       (50, 10), (60, 10), (70, 10), (80, 10),
168
 
                       (90, 10), (100, 10)])
169
 
 
170
 
    def test_coalesce_fudge(self):
171
 
        self.check([(10, 30, [(0, 10), (20, 10)]),
172
 
                    (100, 10, [(0, 10),]),
173
 
                   ], [(10, 10), (30, 10), (100, 10)],
174
 
                   fudge=10
175
 
                  )
176
 
 
177
 
 
178
 
class TestMemoryTransport(TestCase):
179
 
 
180
 
    def test_get_transport(self):
181
 
        MemoryTransport()
182
 
 
183
 
    def test_clone(self):
184
 
        transport = MemoryTransport()
185
 
        self.assertTrue(isinstance(transport, MemoryTransport))
186
 
        self.assertEqual("memory:///", transport.clone("/").base)
187
 
 
188
 
    def test_abspath(self):
189
 
        transport = MemoryTransport()
190
 
        self.assertEqual("memory:///relpath", transport.abspath('relpath'))
191
 
 
192
 
    def test_abspath_of_root(self):
193
 
        transport = MemoryTransport()
194
 
        self.assertEqual("memory:///", transport.base)
195
 
        self.assertEqual("memory:///", transport.abspath('/'))
196
 
 
197
 
    def test_abspath_of_relpath_starting_at_root(self):
198
 
        transport = MemoryTransport()
199
 
        self.assertEqual("memory:///foo", transport.abspath('/foo'))
200
 
 
201
 
    def test_append_and_get(self):
202
 
        transport = MemoryTransport()
203
 
        transport.append_bytes('path', 'content')
204
 
        self.assertEqual(transport.get('path').read(), 'content')
205
 
        transport.append_file('path', StringIO('content'))
206
 
        self.assertEqual(transport.get('path').read(), 'contentcontent')
207
 
 
208
 
    def test_put_and_get(self):
209
 
        transport = MemoryTransport()
210
 
        transport.put_file('path', StringIO('content'))
211
 
        self.assertEqual(transport.get('path').read(), 'content')
212
 
        transport.put_bytes('path', 'content')
213
 
        self.assertEqual(transport.get('path').read(), 'content')
214
 
 
215
 
    def test_append_without_dir_fails(self):
216
 
        transport = MemoryTransport()
217
 
        self.assertRaises(NoSuchFile,
218
 
                          transport.append_bytes, 'dir/path', 'content')
219
 
 
220
 
    def test_put_without_dir_fails(self):
221
 
        transport = MemoryTransport()
222
 
        self.assertRaises(NoSuchFile,
223
 
                          transport.put_file, 'dir/path', StringIO('content'))
224
 
 
225
 
    def test_get_missing(self):
226
 
        transport = MemoryTransport()
227
 
        self.assertRaises(NoSuchFile, transport.get, 'foo')
228
 
 
229
 
    def test_has_missing(self):
230
 
        transport = MemoryTransport()
231
 
        self.assertEquals(False, transport.has('foo'))
232
 
 
233
 
    def test_has_present(self):
234
 
        transport = MemoryTransport()
235
 
        transport.append_bytes('foo', 'content')
236
 
        self.assertEquals(True, transport.has('foo'))
237
 
 
238
 
    def test_mkdir(self):
239
 
        transport = MemoryTransport()
240
 
        transport.mkdir('dir')
241
 
        transport.append_bytes('dir/path', 'content')
242
 
        self.assertEqual(transport.get('dir/path').read(), 'content')
243
 
 
244
 
    def test_mkdir_missing_parent(self):
245
 
        transport = MemoryTransport()
246
 
        self.assertRaises(NoSuchFile,
247
 
                          transport.mkdir, 'dir/dir')
248
 
 
249
 
    def test_mkdir_twice(self):
250
 
        transport = MemoryTransport()
251
 
        transport.mkdir('dir')
252
 
        self.assertRaises(FileExists, transport.mkdir, 'dir')
253
 
 
254
 
    def test_parameters(self):
255
 
        transport = MemoryTransport()
256
 
        self.assertEqual(True, transport.listable())
257
 
        self.assertEqual(False, transport.should_cache())
258
 
        self.assertEqual(False, transport.is_readonly())
259
 
 
260
 
    def test_iter_files_recursive(self):
261
 
        transport = MemoryTransport()
262
 
        transport.mkdir('dir')
263
 
        transport.put_bytes('dir/foo', 'content')
264
 
        transport.put_bytes('dir/bar', 'content')
265
 
        transport.put_bytes('bar', 'content')
266
 
        paths = set(transport.iter_files_recursive())
267
 
        self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
268
 
 
269
 
    def test_stat(self):
270
 
        transport = MemoryTransport()
271
 
        transport.put_bytes('foo', 'content')
272
 
        transport.put_bytes('bar', 'phowar')
273
 
        self.assertEqual(7, transport.stat('foo').st_size)
274
 
        self.assertEqual(6, transport.stat('bar').st_size)
275
 
 
276
 
        
277
 
class ReadonlyDecoratorTransportTest(TestCase):
278
 
    """Readonly decoration specific tests."""
279
 
 
280
 
    def test_local_parameters(self):
281
 
        import bzrlib.transport.readonly as readonly
282
 
        # connect to . in readonly mode
283
 
        transport = readonly.ReadonlyTransportDecorator('readonly+.')
284
 
        self.assertEqual(True, transport.listable())
285
 
        self.assertEqual(False, transport.should_cache())
286
 
        self.assertEqual(True, transport.is_readonly())
287
 
 
288
 
    def test_http_parameters(self):
289
 
        import bzrlib.transport.readonly as readonly
290
 
        from bzrlib.transport.http import HttpServer
291
 
        # connect to . via http which is not listable
292
 
        server = HttpServer()
293
 
        server.setUp()
294
 
        try:
295
 
            transport = get_transport('readonly+' + server.get_url())
296
 
            self.failUnless(isinstance(transport,
297
 
                                       readonly.ReadonlyTransportDecorator))
298
 
            self.assertEqual(False, transport.listable())
299
 
            self.assertEqual(True, transport.should_cache())
300
 
            self.assertEqual(True, transport.is_readonly())
301
 
        finally:
302
 
            server.tearDown()
303
 
 
304
 
 
305
 
class FakeNFSDecoratorTests(TestCaseInTempDir):
306
 
    """NFS decorator specific tests."""
307
 
 
308
 
    def get_nfs_transport(self, url):
309
 
        import bzrlib.transport.fakenfs as fakenfs
310
 
        # connect to url with nfs decoration
311
 
        return fakenfs.FakeNFSTransportDecorator('fakenfs+' + url)
312
 
 
313
 
    def test_local_parameters(self):
314
 
        # the listable, should_cache and is_readonly parameters
315
 
        # are not changed by the fakenfs decorator
316
 
        transport = self.get_nfs_transport('.')
317
 
        self.assertEqual(True, transport.listable())
318
 
        self.assertEqual(False, transport.should_cache())
319
 
        self.assertEqual(False, transport.is_readonly())
320
 
 
321
 
    def test_http_parameters(self):
322
 
        # the listable, should_cache and is_readonly parameters
323
 
        # are not changed by the fakenfs decorator
324
 
        from bzrlib.transport.http import HttpServer
325
 
        # connect to . via http which is not listable
326
 
        server = HttpServer()
327
 
        server.setUp()
328
 
        try:
329
 
            transport = self.get_nfs_transport(server.get_url())
330
 
            self.assertIsInstance(
331
 
                transport, bzrlib.transport.fakenfs.FakeNFSTransportDecorator)
332
 
            self.assertEqual(False, transport.listable())
333
 
            self.assertEqual(True, transport.should_cache())
334
 
            self.assertEqual(True, transport.is_readonly())
335
 
        finally:
336
 
            server.tearDown()
337
 
 
338
 
    def test_fakenfs_server_default(self):
339
 
        # a FakeNFSServer() should bring up a local relpath server for itself
340
 
        import bzrlib.transport.fakenfs as fakenfs
341
 
        server = fakenfs.FakeNFSServer()
342
 
        server.setUp()
343
 
        try:
344
 
            # the url should be decorated appropriately
345
 
            self.assertStartsWith(server.get_url(), 'fakenfs+')
346
 
            # and we should be able to get a transport for it
347
 
            transport = get_transport(server.get_url())
348
 
            # which must be a FakeNFSTransportDecorator instance.
349
 
            self.assertIsInstance(
350
 
                transport, fakenfs.FakeNFSTransportDecorator)
351
 
        finally:
352
 
            server.tearDown()
353
 
 
354
 
    def test_fakenfs_rename_semantics(self):
355
 
        # a FakeNFS transport must mangle the way rename errors occur to
356
 
        # look like NFS problems.
357
 
        transport = self.get_nfs_transport('.')
358
 
        self.build_tree(['from/', 'from/foo', 'to/', 'to/bar'],
359
 
                        transport=transport)
360
 
        self.assertRaises(bzrlib.errors.ResourceBusy,
361
 
                          transport.rename, 'from', 'to')
362
 
 
363
 
 
364
 
class FakeVFATDecoratorTests(TestCaseInTempDir):
365
 
    """Tests for simulation of VFAT restrictions"""
366
 
 
367
 
    def get_vfat_transport(self, url):
368
 
        """Return vfat-backed transport for test directory"""
369
 
        from bzrlib.transport.fakevfat import FakeVFATTransportDecorator
370
 
        return FakeVFATTransportDecorator('vfat+' + url)
371
 
 
372
 
    def test_transport_creation(self):
373
 
        from bzrlib.transport.fakevfat import FakeVFATTransportDecorator
374
 
        transport = self.get_vfat_transport('.')
375
 
        self.assertIsInstance(transport, FakeVFATTransportDecorator)
376
 
 
377
 
    def test_transport_mkdir(self):
378
 
        transport = self.get_vfat_transport('.')
379
 
        transport.mkdir('HELLO')
380
 
        self.assertTrue(transport.has('hello'))
381
 
        self.assertTrue(transport.has('Hello'))
382
 
 
383
 
    def test_forbidden_chars(self):
384
 
        transport = self.get_vfat_transport('.')
385
 
        self.assertRaises(ValueError, transport.has, "<NU>")
386
 
 
387
 
 
388
 
class BadTransportHandler(Transport):
389
 
    def __init__(self, base_url):
390
 
        raise DependencyNotPresent('some_lib', 'testing missing dependency')
391
 
 
392
 
 
393
 
class BackupTransportHandler(Transport):
394
 
    """Test transport that works as a backup for the BadTransportHandler"""
395
 
    pass
396
 
 
397
 
 
398
 
class TestTransportImplementation(TestCaseInTempDir):
399
 
    """Implementation verification for transports.
400
 
    
401
 
    To verify a transport we need a server factory, which is a callable
402
 
    that accepts no parameters and returns an implementation of
403
 
    bzrlib.transport.Server.
404
 
    
405
 
    That Server is then used to construct transport instances and test
406
 
    the transport via loopback activity.
407
 
 
408
 
    Currently this assumes that the Transport object is connected to the 
409
 
    current working directory.  So that whatever is done 
410
 
    through the transport, should show up in the working 
411
 
    directory, and vice-versa. This is a bug, because its possible to have
412
 
    URL schemes which provide access to something that may not be 
413
 
    result in storage on the local disk, i.e. due to file system limits, or 
414
 
    due to it being a database or some other non-filesystem tool.
415
 
 
416
 
    This also tests to make sure that the functions work with both
417
 
    generators and lists (assuming iter(list) is effectively a generator)
418
 
    """
419
 
    
420
 
    def setUp(self):
421
 
        super(TestTransportImplementation, self).setUp()
422
 
        self._server = self.transport_server()
423
 
        self._server.setUp()
424
 
 
425
 
    def tearDown(self):
426
 
        super(TestTransportImplementation, self).tearDown()
427
 
        self._server.tearDown()
428
 
        
429
 
    def get_transport(self):
430
 
        """Return a connected transport to the local directory."""
431
 
        base_url = self._server.get_url()
432
 
        # try getting the transport via the regular interface:
433
 
        t = get_transport(base_url)
434
 
        if not isinstance(t, self.transport_class):
435
 
            # we did not get the correct transport class type. Override the
436
 
            # regular connection behaviour by direct construction.
437
 
            t = self.transport_class(base_url)
438
 
        return t
439
 
 
440
 
 
441
 
class TestLocalTransports(TestCase):
442
 
 
443
 
    def test_get_transport_from_abspath(self):
444
 
        here = os.path.abspath('.')
445
 
        t = get_transport(here)
446
 
        self.assertIsInstance(t, LocalTransport)
447
 
        self.assertEquals(t.base, urlutils.local_path_to_url(here) + '/')
448
 
 
449
 
    def test_get_transport_from_relpath(self):
450
 
        here = os.path.abspath('.')
451
 
        t = get_transport('.')
452
 
        self.assertIsInstance(t, LocalTransport)
453
 
        self.assertEquals(t.base, urlutils.local_path_to_url('.') + '/')
454
 
 
455
 
    def test_get_transport_from_local_url(self):
456
 
        here = os.path.abspath('.')
457
 
        here_url = urlutils.local_path_to_url(here) + '/'
458
 
        t = get_transport(here_url)
459
 
        self.assertIsInstance(t, LocalTransport)
460
 
        self.assertEquals(t.base, here_url)