~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transport.py

  • Committer: Kent Gibson
  • Date: 2006-12-08 12:59:11 UTC
  • mto: (2178.1.1 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 2179.
  • Revision ID: warthog618@gmail.com-20061208125911-wsnbxy7aydzjb47s
Implement JAM's review suggestions.

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_list_dir(self):
 
239
        transport = MemoryTransport()
 
240
        transport.put_bytes('foo', 'content')
 
241
        transport.mkdir('dir')
 
242
        transport.put_bytes('dir/subfoo', 'content')
 
243
        transport.put_bytes('dirlike', 'content')
 
244
 
 
245
        self.assertEquals(['dir', 'dirlike', 'foo'], sorted(transport.list_dir('.')))
 
246
        self.assertEquals(['subfoo'], sorted(transport.list_dir('dir')))
 
247
 
 
248
    def test_mkdir(self):
 
249
        transport = MemoryTransport()
 
250
        transport.mkdir('dir')
 
251
        transport.append_bytes('dir/path', 'content')
 
252
        self.assertEqual(transport.get('dir/path').read(), 'content')
 
253
 
 
254
    def test_mkdir_missing_parent(self):
 
255
        transport = MemoryTransport()
 
256
        self.assertRaises(NoSuchFile,
 
257
                          transport.mkdir, 'dir/dir')
 
258
 
 
259
    def test_mkdir_twice(self):
 
260
        transport = MemoryTransport()
 
261
        transport.mkdir('dir')
 
262
        self.assertRaises(FileExists, transport.mkdir, 'dir')
 
263
 
 
264
    def test_parameters(self):
 
265
        transport = MemoryTransport()
 
266
        self.assertEqual(True, transport.listable())
 
267
        self.assertEqual(False, transport.should_cache())
 
268
        self.assertEqual(False, transport.is_readonly())
 
269
 
 
270
    def test_iter_files_recursive(self):
 
271
        transport = MemoryTransport()
 
272
        transport.mkdir('dir')
 
273
        transport.put_bytes('dir/foo', 'content')
 
274
        transport.put_bytes('dir/bar', 'content')
 
275
        transport.put_bytes('bar', 'content')
 
276
        paths = set(transport.iter_files_recursive())
 
277
        self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
 
278
 
 
279
    def test_stat(self):
 
280
        transport = MemoryTransport()
 
281
        transport.put_bytes('foo', 'content')
 
282
        transport.put_bytes('bar', 'phowar')
 
283
        self.assertEqual(7, transport.stat('foo').st_size)
 
284
        self.assertEqual(6, transport.stat('bar').st_size)
 
285
 
 
286
        
 
287
class ReadonlyDecoratorTransportTest(TestCase):
 
288
    """Readonly decoration specific tests."""
 
289
 
 
290
    def test_local_parameters(self):
 
291
        import bzrlib.transport.readonly as readonly
 
292
        # connect to . in readonly mode
 
293
        transport = readonly.ReadonlyTransportDecorator('readonly+.')
 
294
        self.assertEqual(True, transport.listable())
 
295
        self.assertEqual(False, transport.should_cache())
 
296
        self.assertEqual(True, transport.is_readonly())
 
297
 
 
298
    def test_http_parameters(self):
 
299
        import bzrlib.transport.readonly as readonly
 
300
        from bzrlib.transport.http import HttpServer
 
301
        # connect to . via http which is not listable
 
302
        server = HttpServer()
 
303
        server.setUp()
 
304
        try:
 
305
            transport = get_transport('readonly+' + server.get_url())
 
306
            self.failUnless(isinstance(transport,
 
307
                                       readonly.ReadonlyTransportDecorator))
 
308
            self.assertEqual(False, transport.listable())
 
309
            self.assertEqual(True, transport.should_cache())
 
310
            self.assertEqual(True, transport.is_readonly())
 
311
        finally:
 
312
            server.tearDown()
 
313
 
 
314
 
 
315
class FakeNFSDecoratorTests(TestCaseInTempDir):
 
316
    """NFS decorator specific tests."""
 
317
 
 
318
    def get_nfs_transport(self, url):
 
319
        import bzrlib.transport.fakenfs as fakenfs
 
320
        # connect to url with nfs decoration
 
321
        return fakenfs.FakeNFSTransportDecorator('fakenfs+' + url)
 
322
 
 
323
    def test_local_parameters(self):
 
324
        # the listable, should_cache and is_readonly parameters
 
325
        # are not changed by the fakenfs decorator
 
326
        transport = self.get_nfs_transport('.')
 
327
        self.assertEqual(True, transport.listable())
 
328
        self.assertEqual(False, transport.should_cache())
 
329
        self.assertEqual(False, transport.is_readonly())
 
330
 
 
331
    def test_http_parameters(self):
 
332
        # the listable, should_cache and is_readonly parameters
 
333
        # are not changed by the fakenfs decorator
 
334
        from bzrlib.transport.http import HttpServer
 
335
        # connect to . via http which is not listable
 
336
        server = HttpServer()
 
337
        server.setUp()
 
338
        try:
 
339
            transport = self.get_nfs_transport(server.get_url())
 
340
            self.assertIsInstance(
 
341
                transport, bzrlib.transport.fakenfs.FakeNFSTransportDecorator)
 
342
            self.assertEqual(False, transport.listable())
 
343
            self.assertEqual(True, transport.should_cache())
 
344
            self.assertEqual(True, transport.is_readonly())
 
345
        finally:
 
346
            server.tearDown()
 
347
 
 
348
    def test_fakenfs_server_default(self):
 
349
        # a FakeNFSServer() should bring up a local relpath server for itself
 
350
        import bzrlib.transport.fakenfs as fakenfs
 
351
        server = fakenfs.FakeNFSServer()
 
352
        server.setUp()
 
353
        try:
 
354
            # the url should be decorated appropriately
 
355
            self.assertStartsWith(server.get_url(), 'fakenfs+')
 
356
            # and we should be able to get a transport for it
 
357
            transport = get_transport(server.get_url())
 
358
            # which must be a FakeNFSTransportDecorator instance.
 
359
            self.assertIsInstance(
 
360
                transport, fakenfs.FakeNFSTransportDecorator)
 
361
        finally:
 
362
            server.tearDown()
 
363
 
 
364
    def test_fakenfs_rename_semantics(self):
 
365
        # a FakeNFS transport must mangle the way rename errors occur to
 
366
        # look like NFS problems.
 
367
        transport = self.get_nfs_transport('.')
 
368
        self.build_tree(['from/', 'from/foo', 'to/', 'to/bar'],
 
369
                        transport=transport)
 
370
        self.assertRaises(bzrlib.errors.ResourceBusy,
 
371
                          transport.rename, 'from', 'to')
 
372
 
 
373
 
 
374
class FakeVFATDecoratorTests(TestCaseInTempDir):
 
375
    """Tests for simulation of VFAT restrictions"""
 
376
 
 
377
    def get_vfat_transport(self, url):
 
378
        """Return vfat-backed transport for test directory"""
 
379
        from bzrlib.transport.fakevfat import FakeVFATTransportDecorator
 
380
        return FakeVFATTransportDecorator('vfat+' + url)
 
381
 
 
382
    def test_transport_creation(self):
 
383
        from bzrlib.transport.fakevfat import FakeVFATTransportDecorator
 
384
        transport = self.get_vfat_transport('.')
 
385
        self.assertIsInstance(transport, FakeVFATTransportDecorator)
 
386
 
 
387
    def test_transport_mkdir(self):
 
388
        transport = self.get_vfat_transport('.')
 
389
        transport.mkdir('HELLO')
 
390
        self.assertTrue(transport.has('hello'))
 
391
        self.assertTrue(transport.has('Hello'))
 
392
 
 
393
    def test_forbidden_chars(self):
 
394
        transport = self.get_vfat_transport('.')
 
395
        self.assertRaises(ValueError, transport.has, "<NU>")
 
396
 
 
397
 
 
398
class BadTransportHandler(Transport):
 
399
    def __init__(self, base_url):
 
400
        raise DependencyNotPresent('some_lib', 'testing missing dependency')
 
401
 
 
402
 
 
403
class BackupTransportHandler(Transport):
 
404
    """Test transport that works as a backup for the BadTransportHandler"""
 
405
    pass
 
406
 
 
407
 
 
408
class TestTransportImplementation(TestCaseInTempDir):
 
409
    """Implementation verification for transports.
 
410
    
 
411
    To verify a transport we need a server factory, which is a callable
 
412
    that accepts no parameters and returns an implementation of
 
413
    bzrlib.transport.Server.
 
414
    
 
415
    That Server is then used to construct transport instances and test
 
416
    the transport via loopback activity.
 
417
 
 
418
    Currently this assumes that the Transport object is connected to the 
 
419
    current working directory.  So that whatever is done 
 
420
    through the transport, should show up in the working 
 
421
    directory, and vice-versa. This is a bug, because its possible to have
 
422
    URL schemes which provide access to something that may not be 
 
423
    result in storage on the local disk, i.e. due to file system limits, or 
 
424
    due to it being a database or some other non-filesystem tool.
 
425
 
 
426
    This also tests to make sure that the functions work with both
 
427
    generators and lists (assuming iter(list) is effectively a generator)
 
428
    """
 
429
    
 
430
    def setUp(self):
 
431
        super(TestTransportImplementation, self).setUp()
 
432
        self._server = self.transport_server()
 
433
        self._server.setUp()
 
434
 
 
435
    def tearDown(self):
 
436
        super(TestTransportImplementation, self).tearDown()
 
437
        self._server.tearDown()
 
438
        
 
439
    def get_transport(self):
 
440
        """Return a connected transport to the local directory."""
 
441
        base_url = self._server.get_url()
 
442
        # try getting the transport via the regular interface:
 
443
        t = get_transport(base_url)
 
444
        if not isinstance(t, self.transport_class):
 
445
            # we did not get the correct transport class type. Override the
 
446
            # regular connection behaviour by direct construction.
 
447
            t = self.transport_class(base_url)
 
448
        return t
 
449
 
 
450
 
 
451
class TestLocalTransports(TestCase):
 
452
 
 
453
    def test_get_transport_from_abspath(self):
 
454
        here = os.path.abspath('.')
 
455
        t = get_transport(here)
 
456
        self.assertIsInstance(t, LocalTransport)
 
457
        self.assertEquals(t.base, urlutils.local_path_to_url(here) + '/')
 
458
 
 
459
    def test_get_transport_from_relpath(self):
 
460
        here = os.path.abspath('.')
 
461
        t = get_transport('.')
 
462
        self.assertIsInstance(t, LocalTransport)
 
463
        self.assertEquals(t.base, urlutils.local_path_to_url('.') + '/')
 
464
 
 
465
    def test_get_transport_from_local_url(self):
 
466
        here = os.path.abspath('.')
 
467
        here_url = urlutils.local_path_to_url(here) + '/'
 
468
        t = get_transport(here_url)
 
469
        self.assertIsInstance(t, LocalTransport)
 
470
        self.assertEquals(t.base, here_url)