13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
from cStringIO import StringIO
24
21
from bzrlib import (
31
from bzrlib.directory_service import directories
32
from bzrlib.transport import (
42
import bzrlib.transport.trace
43
from bzrlib.tests import (
26
from bzrlib.errors import (DependencyNotPresent,
34
from bzrlib.tests import TestCase, TestCaseInTempDir
35
from bzrlib.transport import (_clear_protocol_handlers,
38
_get_protocol_handlers,
39
_set_protocol_handlers,
40
_get_transport_modules,
43
register_lazy_transport,
44
register_transport_proto,
47
from bzrlib.transport.chroot import ChrootServer
48
from bzrlib.transport.memory import MemoryTransport
49
from bzrlib.transport.local import (LocalTransport,
50
EmulatedWin32LocalTransport)
49
53
# TODO: Should possibly split transport-specific tests into their own files.
52
class TestTransport(tests.TestCase):
56
class TestTransport(TestCase):
53
57
"""Test the non transport-concrete class functionality."""
55
59
def test__get_set_protocol_handlers(self):
56
handlers = transport._get_protocol_handlers()
57
self.assertNotEqual([], handlers.keys())
58
transport._clear_protocol_handlers()
59
self.addCleanup(transport._set_protocol_handlers, handlers)
60
self.assertEqual([], transport._get_protocol_handlers().keys())
60
handlers = _get_protocol_handlers()
61
self.assertNotEqual([], handlers.keys( ))
63
_clear_protocol_handlers()
64
self.assertEqual([], _get_protocol_handlers().keys())
66
_set_protocol_handlers(handlers)
62
68
def test_get_transport_modules(self):
63
handlers = transport._get_protocol_handlers()
64
self.addCleanup(transport._set_protocol_handlers, handlers)
69
handlers = _get_protocol_handlers()
65
70
# don't pollute the current handlers
66
transport._clear_protocol_handlers()
71
_clear_protocol_handlers()
68
72
class SampleHandler(object):
69
73
"""I exist, isnt that enough?"""
70
transport._clear_protocol_handlers()
71
transport.register_transport_proto('foo')
72
transport.register_lazy_transport('foo',
73
'bzrlib.tests.test_transport',
74
'TestTransport.SampleHandler')
75
transport.register_transport_proto('bar')
76
transport.register_lazy_transport('bar',
77
'bzrlib.tests.test_transport',
78
'TestTransport.SampleHandler')
79
self.assertEqual([SampleHandler.__module__,
80
'bzrlib.transport.chroot',
81
'bzrlib.transport.pathfilter'],
82
transport._get_transport_modules())
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'],
84
_get_transport_modules())
86
_set_protocol_handlers(handlers)
84
88
def test_transport_dependency(self):
85
89
"""Transport with missing dependency causes no error"""
86
saved_handlers = transport._get_protocol_handlers()
87
self.addCleanup(transport._set_protocol_handlers, saved_handlers)
90
saved_handlers = _get_protocol_handlers()
88
91
# don't pollute the current handlers
89
transport._clear_protocol_handlers()
90
transport.register_transport_proto('foo')
91
transport.register_lazy_transport(
92
'foo', 'bzrlib.tests.test_transport', 'BadTransportHandler')
92
_clear_protocol_handlers()
94
transport.get_transport_from_url('foo://fooserver/foo')
95
except errors.UnsupportedProtocol, e:
97
self.assertEquals('Unsupported protocol'
98
' for url "foo://fooserver/foo":'
99
' Unable to import library "some_lib":'
100
' testing missing dependency', str(e))
102
self.fail('Did not raise UnsupportedProtocol')
94
register_transport_proto('foo')
95
register_lazy_transport('foo', 'bzrlib.tests.test_transport',
96
'BadTransportHandler')
98
get_transport('foo://fooserver/foo')
99
except UnsupportedProtocol, e:
101
self.assertEquals('Unsupported protocol'
102
' for url "foo://fooserver/foo":'
103
' Unable to import library "some_lib":'
104
' testing missing dependency', str(e))
106
self.fail('Did not raise UnsupportedProtocol')
108
# restore original values
109
_set_protocol_handlers(saved_handlers)
104
111
def test_transport_fallback(self):
105
112
"""Transport with missing dependency causes no error"""
106
saved_handlers = transport._get_protocol_handlers()
107
self.addCleanup(transport._set_protocol_handlers, saved_handlers)
108
transport._clear_protocol_handlers()
109
transport.register_transport_proto('foo')
110
transport.register_lazy_transport(
111
'foo', 'bzrlib.tests.test_transport', 'BackupTransportHandler')
112
transport.register_lazy_transport(
113
'foo', 'bzrlib.tests.test_transport', 'BadTransportHandler')
114
t = transport.get_transport_from_url('foo://fooserver/foo')
115
self.assertTrue(isinstance(t, BackupTransportHandler))
117
def test_ssh_hints(self):
118
"""Transport ssh:// should raise an error pointing out bzr+ssh://"""
113
saved_handlers = _get_protocol_handlers()
120
transport.get_transport_from_url('ssh://fooserver/foo')
121
except errors.UnsupportedProtocol, e:
123
self.assertEquals('Unsupported protocol'
124
' for url "ssh://fooserver/foo":'
125
' bzr supports bzr+ssh to operate over ssh,'
126
' use "bzr+ssh://fooserver/foo".',
129
self.fail('Did not raise UnsupportedProtocol')
115
_clear_protocol_handlers()
116
register_transport_proto('foo')
117
register_lazy_transport('foo', 'bzrlib.tests.test_transport',
118
'BackupTransportHandler')
119
register_lazy_transport('foo', 'bzrlib.tests.test_transport',
120
'BadTransportHandler')
121
t = get_transport('foo://fooserver/foo')
122
self.assertTrue(isinstance(t, BackupTransportHandler))
124
_set_protocol_handlers(saved_handlers)
131
126
def test_LateReadError(self):
132
127
"""The LateReadError helper should raise on read()."""
133
a_file = transport.LateReadError('a path')
128
a_file = LateReadError('a path')
136
except errors.ReadError, error:
131
except ReadError, error:
137
132
self.assertEqual('a path', error.path)
138
self.assertRaises(errors.ReadError, a_file.read, 40)
133
self.assertRaises(ReadError, a_file.read, 40)
136
def test__combine_paths(self):
138
self.assertEqual('/home/sarah/project/foo',
139
t._combine_paths('/home/sarah', 'project/foo'))
140
self.assertEqual('/etc',
141
t._combine_paths('/home/sarah', '../../etc'))
142
self.assertEqual('/etc',
143
t._combine_paths('/home/sarah', '../../../etc'))
144
self.assertEqual('/etc',
145
t._combine_paths('/home/sarah', '/etc'))
141
147
def test_local_abspath_non_local_transport(self):
142
148
# the base implementation should throw
143
t = memory.MemoryTransport()
149
t = MemoryTransport()
144
150
e = self.assertRaises(errors.NotLocalUrl, t.local_abspath, 't')
145
151
self.assertEqual('memory:///t is not a local path.', str(e))
148
class TestCoalesceOffsets(tests.TestCase):
154
class TestCoalesceOffsets(TestCase):
150
156
def check(self, expected, offsets, limit=0, max_size=0, fudge=0):
151
coalesce = transport.Transport._coalesce_offsets
152
exp = [transport._CoalescedOffset(*x) for x in expected]
157
coalesce = Transport._coalesce_offsets
158
exp = [_CoalescedOffset(*x) for x in expected]
153
159
out = list(coalesce(offsets, limit=limit, fudge_factor=fudge,
154
160
max_size=max_size))
155
161
self.assertEqual(exp, out)
201
207
def test_coalesce_fudge(self):
202
208
self.check([(10, 30, [(0, 10), (20, 10)]),
203
(100, 10, [(0, 10)]),
209
(100, 10, [(0, 10),]),
204
210
], [(10, 10), (30, 10), (100, 10)],
207
213
def test_coalesce_max_size(self):
208
214
self.check([(10, 20, [(0, 10), (10, 10)]),
209
215
(30, 50, [(0, 50)]),
210
216
# If one range is above max_size, it gets its own coalesced
212
(100, 80, [(0, 80)]),],
218
(100, 80, [(0, 80),]),],
213
219
[(10, 10), (20, 10), (30, 50), (100, 80)],
216
223
def test_coalesce_no_max_size(self):
217
self.check([(10, 170, [(0, 10), (10, 10), (20, 50), (70, 100)])],
224
self.check([(10, 170, [(0, 10), (10, 10), (20, 50), (70, 100)]),],
218
225
[(10, 10), (20, 10), (30, 50), (80, 100)],
221
def test_coalesce_default_limit(self):
222
# By default we use a 100MB max size.
223
ten_mb = 10 * 1024 * 1024
224
self.check([(0, 10 * ten_mb, [(i * ten_mb, ten_mb) for i in range(10)]),
225
(10*ten_mb, ten_mb, [(0, ten_mb)])],
226
[(i*ten_mb, ten_mb) for i in range(11)])
227
self.check([(0, 11 * ten_mb, [(i * ten_mb, ten_mb) for i in range(11)])],
228
[(i * ten_mb, ten_mb) for i in range(11)],
229
max_size=1*1024*1024*1024)
232
class TestMemoryServer(tests.TestCase):
234
def test_create_server(self):
235
server = memory.MemoryServer()
236
server.start_server()
237
url = server.get_url()
238
self.assertTrue(url in transport.transport_list_registry)
239
t = transport.get_transport_from_url(url)
242
self.assertFalse(url in transport.transport_list_registry)
243
self.assertRaises(errors.UnsupportedProtocol,
244
transport.get_transport, url)
247
class TestMemoryTransport(tests.TestCase):
229
class TestMemoryTransport(TestCase):
249
231
def test_get_transport(self):
250
memory.MemoryTransport()
252
234
def test_clone(self):
253
t = memory.MemoryTransport()
254
self.assertTrue(isinstance(t, memory.MemoryTransport))
255
self.assertEqual("memory:///", t.clone("/").base)
235
transport = MemoryTransport()
236
self.assertTrue(isinstance(transport, MemoryTransport))
237
self.assertEqual("memory:///", transport.clone("/").base)
257
239
def test_abspath(self):
258
t = memory.MemoryTransport()
259
self.assertEqual("memory:///relpath", t.abspath('relpath'))
240
transport = MemoryTransport()
241
self.assertEqual("memory:///relpath", transport.abspath('relpath'))
261
243
def test_abspath_of_root(self):
262
t = memory.MemoryTransport()
263
self.assertEqual("memory:///", t.base)
264
self.assertEqual("memory:///", t.abspath('/'))
244
transport = MemoryTransport()
245
self.assertEqual("memory:///", transport.base)
246
self.assertEqual("memory:///", transport.abspath('/'))
266
248
def test_abspath_of_relpath_starting_at_root(self):
267
t = memory.MemoryTransport()
268
self.assertEqual("memory:///foo", t.abspath('/foo'))
249
transport = MemoryTransport()
250
self.assertEqual("memory:///foo", transport.abspath('/foo'))
270
252
def test_append_and_get(self):
271
t = memory.MemoryTransport()
272
t.append_bytes('path', 'content')
273
self.assertEqual(t.get('path').read(), 'content')
274
t.append_file('path', StringIO('content'))
275
self.assertEqual(t.get('path').read(), 'contentcontent')
253
transport = MemoryTransport()
254
transport.append_bytes('path', 'content')
255
self.assertEqual(transport.get('path').read(), 'content')
256
transport.append_file('path', StringIO('content'))
257
self.assertEqual(transport.get('path').read(), 'contentcontent')
277
259
def test_put_and_get(self):
278
t = memory.MemoryTransport()
279
t.put_file('path', StringIO('content'))
280
self.assertEqual(t.get('path').read(), 'content')
281
t.put_bytes('path', 'content')
282
self.assertEqual(t.get('path').read(), 'content')
260
transport = MemoryTransport()
261
transport.put_file('path', StringIO('content'))
262
self.assertEqual(transport.get('path').read(), 'content')
263
transport.put_bytes('path', 'content')
264
self.assertEqual(transport.get('path').read(), 'content')
284
266
def test_append_without_dir_fails(self):
285
t = memory.MemoryTransport()
286
self.assertRaises(errors.NoSuchFile,
287
t.append_bytes, 'dir/path', 'content')
267
transport = MemoryTransport()
268
self.assertRaises(NoSuchFile,
269
transport.append_bytes, 'dir/path', 'content')
289
271
def test_put_without_dir_fails(self):
290
t = memory.MemoryTransport()
291
self.assertRaises(errors.NoSuchFile,
292
t.put_file, 'dir/path', StringIO('content'))
272
transport = MemoryTransport()
273
self.assertRaises(NoSuchFile,
274
transport.put_file, 'dir/path', StringIO('content'))
294
276
def test_get_missing(self):
295
transport = memory.MemoryTransport()
296
self.assertRaises(errors.NoSuchFile, transport.get, 'foo')
277
transport = MemoryTransport()
278
self.assertRaises(NoSuchFile, transport.get, 'foo')
298
280
def test_has_missing(self):
299
t = memory.MemoryTransport()
300
self.assertEquals(False, t.has('foo'))
281
transport = MemoryTransport()
282
self.assertEquals(False, transport.has('foo'))
302
284
def test_has_present(self):
303
t = memory.MemoryTransport()
304
t.append_bytes('foo', 'content')
305
self.assertEquals(True, t.has('foo'))
285
transport = MemoryTransport()
286
transport.append_bytes('foo', 'content')
287
self.assertEquals(True, transport.has('foo'))
307
289
def test_list_dir(self):
308
t = memory.MemoryTransport()
309
t.put_bytes('foo', 'content')
311
t.put_bytes('dir/subfoo', 'content')
312
t.put_bytes('dirlike', 'content')
290
transport = MemoryTransport()
291
transport.put_bytes('foo', 'content')
292
transport.mkdir('dir')
293
transport.put_bytes('dir/subfoo', 'content')
294
transport.put_bytes('dirlike', 'content')
314
self.assertEquals(['dir', 'dirlike', 'foo'], sorted(t.list_dir('.')))
315
self.assertEquals(['subfoo'], sorted(t.list_dir('dir')))
296
self.assertEquals(['dir', 'dirlike', 'foo'], sorted(transport.list_dir('.')))
297
self.assertEquals(['subfoo'], sorted(transport.list_dir('dir')))
317
299
def test_mkdir(self):
318
t = memory.MemoryTransport()
320
t.append_bytes('dir/path', 'content')
321
self.assertEqual(t.get('dir/path').read(), 'content')
300
transport = MemoryTransport()
301
transport.mkdir('dir')
302
transport.append_bytes('dir/path', 'content')
303
self.assertEqual(transport.get('dir/path').read(), 'content')
323
305
def test_mkdir_missing_parent(self):
324
t = memory.MemoryTransport()
325
self.assertRaises(errors.NoSuchFile, t.mkdir, 'dir/dir')
306
transport = MemoryTransport()
307
self.assertRaises(NoSuchFile,
308
transport.mkdir, 'dir/dir')
327
310
def test_mkdir_twice(self):
328
t = memory.MemoryTransport()
330
self.assertRaises(errors.FileExists, t.mkdir, 'dir')
311
transport = MemoryTransport()
312
transport.mkdir('dir')
313
self.assertRaises(FileExists, transport.mkdir, 'dir')
332
315
def test_parameters(self):
333
t = memory.MemoryTransport()
334
self.assertEqual(True, t.listable())
335
self.assertEqual(False, t.is_readonly())
316
transport = MemoryTransport()
317
self.assertEqual(True, transport.listable())
318
self.assertEqual(False, transport.is_readonly())
337
320
def test_iter_files_recursive(self):
338
t = memory.MemoryTransport()
340
t.put_bytes('dir/foo', 'content')
341
t.put_bytes('dir/bar', 'content')
342
t.put_bytes('bar', 'content')
343
paths = set(t.iter_files_recursive())
321
transport = MemoryTransport()
322
transport.mkdir('dir')
323
transport.put_bytes('dir/foo', 'content')
324
transport.put_bytes('dir/bar', 'content')
325
transport.put_bytes('bar', 'content')
326
paths = set(transport.iter_files_recursive())
344
327
self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
346
329
def test_stat(self):
347
t = memory.MemoryTransport()
348
t.put_bytes('foo', 'content')
349
t.put_bytes('bar', 'phowar')
350
self.assertEqual(7, t.stat('foo').st_size)
351
self.assertEqual(6, t.stat('bar').st_size)
354
class ChrootDecoratorTransportTest(tests.TestCase):
330
transport = MemoryTransport()
331
transport.put_bytes('foo', 'content')
332
transport.put_bytes('bar', 'phowar')
333
self.assertEqual(7, transport.stat('foo').st_size)
334
self.assertEqual(6, transport.stat('bar').st_size)
337
class ChrootDecoratorTransportTest(TestCase):
355
338
"""Chroot decoration specific tests."""
357
340
def test_abspath(self):
358
341
# The abspath is always relative to the chroot_url.
359
server = chroot.ChrootServer(
360
transport.get_transport_from_url('memory:///foo/bar/'))
361
self.start_server(server)
362
t = transport.get_transport_from_url(server.get_url())
363
self.assertEqual(server.get_url(), t.abspath('/'))
342
server = ChrootServer(get_transport('memory:///foo/bar/'))
344
transport = get_transport(server.get_url())
345
self.assertEqual(server.get_url(), transport.abspath('/'))
365
subdir_t = t.clone('subdir')
366
self.assertEqual(server.get_url(), subdir_t.abspath('/'))
347
subdir_transport = transport.clone('subdir')
348
self.assertEqual(server.get_url(), subdir_transport.abspath('/'))
368
351
def test_clone(self):
369
server = chroot.ChrootServer(
370
transport.get_transport_from_url('memory:///foo/bar/'))
371
self.start_server(server)
372
t = transport.get_transport_from_url(server.get_url())
352
server = ChrootServer(get_transport('memory:///foo/bar/'))
354
transport = get_transport(server.get_url())
373
355
# relpath from root and root path are the same
374
relpath_cloned = t.clone('foo')
375
abspath_cloned = t.clone('/foo')
356
relpath_cloned = transport.clone('foo')
357
abspath_cloned = transport.clone('/foo')
376
358
self.assertEqual(server, relpath_cloned.server)
377
359
self.assertEqual(server, abspath_cloned.server)
379
362
def test_chroot_url_preserves_chroot(self):
380
363
"""Calling get_transport on a chroot transport's base should produce a
381
364
transport with exactly the same behaviour as the original chroot
401
384
This is so that it is not possible to escape a chroot by doing::
402
385
url = chroot_transport.base
403
386
parent_url = urlutils.join(url, '..')
404
new_t = transport.get_transport_from_url(parent_url)
387
new_transport = get_transport(parent_url)
406
server = chroot.ChrootServer(
407
transport.get_transport_from_url('memory:///path/'))
408
self.start_server(server)
409
t = transport.get_transport_from_url(server.get_url())
389
server = ChrootServer(get_transport('memory:///path/'))
391
transport = get_transport(server.get_url())
410
392
self.assertRaises(
411
errors.InvalidURLJoin, urlutils.join, t.base, '..')
414
class TestChrootServer(tests.TestCase):
393
InvalidURLJoin, urlutils.join, transport.base, '..')
397
class ChrootServerTest(TestCase):
416
399
def test_construct(self):
417
backing_transport = memory.MemoryTransport()
418
server = chroot.ChrootServer(backing_transport)
400
backing_transport = MemoryTransport()
401
server = ChrootServer(backing_transport)
419
402
self.assertEqual(backing_transport, server.backing_transport)
421
404
def test_setUp(self):
422
backing_transport = memory.MemoryTransport()
423
server = chroot.ChrootServer(backing_transport)
424
server.start_server()
425
self.addCleanup(server.stop_server)
426
self.assertTrue(server.scheme
427
in transport._get_protocol_handlers().keys())
405
backing_transport = MemoryTransport()
406
server = ChrootServer(backing_transport)
408
self.assertTrue(server.scheme in _get_protocol_handlers().keys())
429
def test_stop_server(self):
430
backing_transport = memory.MemoryTransport()
431
server = chroot.ChrootServer(backing_transport)
432
server.start_server()
434
self.assertFalse(server.scheme
435
in transport._get_protocol_handlers().keys())
410
def test_tearDown(self):
411
backing_transport = MemoryTransport()
412
server = ChrootServer(backing_transport)
415
self.assertFalse(server.scheme in _get_protocol_handlers().keys())
437
417
def test_get_url(self):
438
backing_transport = memory.MemoryTransport()
439
server = chroot.ChrootServer(backing_transport)
440
server.start_server()
441
self.addCleanup(server.stop_server)
418
backing_transport = MemoryTransport()
419
server = ChrootServer(backing_transport)
442
421
self.assertEqual('chroot-%d:///' % id(server), server.get_url())
445
class PathFilteringDecoratorTransportTest(tests.TestCase):
446
"""Pathfilter decoration specific tests."""
448
def test_abspath(self):
449
# The abspath is always relative to the base of the backing transport.
450
server = pathfilter.PathFilteringServer(
451
transport.get_transport_from_url('memory:///foo/bar/'),
453
server.start_server()
454
t = transport.get_transport_from_url(server.get_url())
455
self.assertEqual(server.get_url(), t.abspath('/'))
457
subdir_t = t.clone('subdir')
458
self.assertEqual(server.get_url(), subdir_t.abspath('/'))
461
def make_pf_transport(self, filter_func=None):
462
"""Make a PathFilteringTransport backed by a MemoryTransport.
464
:param filter_func: by default this will be a no-op function. Use this
465
parameter to override it."""
466
if filter_func is None:
467
filter_func = lambda x: x
468
server = pathfilter.PathFilteringServer(
469
transport.get_transport_from_url('memory:///foo/bar/'), filter_func)
470
server.start_server()
471
self.addCleanup(server.stop_server)
472
return transport.get_transport_from_url(server.get_url())
474
def test__filter(self):
475
# _filter (with an identity func as filter_func) always returns
476
# paths relative to the base of the backing transport.
477
t = self.make_pf_transport()
478
self.assertEqual('foo', t._filter('foo'))
479
self.assertEqual('foo/bar', t._filter('foo/bar'))
480
self.assertEqual('', t._filter('..'))
481
self.assertEqual('', t._filter('/'))
482
# The base of the pathfiltering transport is taken into account too.
483
t = t.clone('subdir1/subdir2')
484
self.assertEqual('subdir1/subdir2/foo', t._filter('foo'))
485
self.assertEqual('subdir1/subdir2/foo/bar', t._filter('foo/bar'))
486
self.assertEqual('subdir1', t._filter('..'))
487
self.assertEqual('', t._filter('/'))
489
def test_filter_invocation(self):
493
filter_log.append(path)
495
t = self.make_pf_transport(filter)
497
self.assertEqual(['abc'], filter_log)
499
t.clone('abc').has('xyz')
500
self.assertEqual(['abc/xyz'], filter_log)
503
self.assertEqual(['abc'], filter_log)
505
def test_clone(self):
506
t = self.make_pf_transport()
507
# relpath from root and root path are the same
508
relpath_cloned = t.clone('foo')
509
abspath_cloned = t.clone('/foo')
510
self.assertEqual(t.server, relpath_cloned.server)
511
self.assertEqual(t.server, abspath_cloned.server)
513
def test_url_preserves_pathfiltering(self):
514
"""Calling get_transport on a pathfiltered transport's base should
515
produce a transport with exactly the same behaviour as the original
516
pathfiltered transport.
518
This is so that it is not possible to escape (accidentally or
519
otherwise) the filtering by doing::
520
url = filtered_transport.base
521
parent_url = urlutils.join(url, '..')
522
new_t = transport.get_transport_from_url(parent_url)
524
t = self.make_pf_transport()
525
new_t = transport.get_transport_from_url(t.base)
526
self.assertEqual(t.server, new_t.server)
527
self.assertEqual(t.base, new_t.base)
530
class ReadonlyDecoratorTransportTest(tests.TestCase):
425
class ReadonlyDecoratorTransportTest(TestCase):
531
426
"""Readonly decoration specific tests."""
533
428
def test_local_parameters(self):
429
import bzrlib.transport.readonly as readonly
534
430
# connect to . in readonly mode
535
t = readonly.ReadonlyTransportDecorator('readonly+.')
536
self.assertEqual(True, t.listable())
537
self.assertEqual(True, t.is_readonly())
431
transport = readonly.ReadonlyTransportDecorator('readonly+.')
432
self.assertEqual(True, transport.listable())
433
self.assertEqual(True, transport.is_readonly())
539
435
def test_http_parameters(self):
540
436
from bzrlib.tests.http_server import HttpServer
437
import bzrlib.transport.readonly as readonly
541
438
# connect to '.' via http which is not listable
542
439
server = HttpServer()
543
self.start_server(server)
544
t = transport.get_transport_from_url('readonly+' + server.get_url())
545
self.assertIsInstance(t, readonly.ReadonlyTransportDecorator)
546
self.assertEqual(False, t.listable())
547
self.assertEqual(True, t.is_readonly())
550
class FakeNFSDecoratorTests(tests.TestCaseInTempDir):
442
transport = get_transport('readonly+' + server.get_url())
443
self.failUnless(isinstance(transport,
444
readonly.ReadonlyTransportDecorator))
445
self.assertEqual(False, transport.listable())
446
self.assertEqual(True, transport.is_readonly())
451
class FakeNFSDecoratorTests(TestCaseInTempDir):
551
452
"""NFS decorator specific tests."""
553
454
def get_nfs_transport(self, url):
455
import bzrlib.transport.fakenfs as fakenfs
554
456
# connect to url with nfs decoration
555
457
return fakenfs.FakeNFSTransportDecorator('fakenfs+' + url)
557
459
def test_local_parameters(self):
558
460
# the listable and is_readonly parameters
559
461
# are not changed by the fakenfs decorator
560
t = self.get_nfs_transport('.')
561
self.assertEqual(True, t.listable())
562
self.assertEqual(False, t.is_readonly())
462
transport = self.get_nfs_transport('.')
463
self.assertEqual(True, transport.listable())
464
self.assertEqual(False, transport.is_readonly())
564
466
def test_http_parameters(self):
565
467
# the listable and is_readonly parameters
567
469
from bzrlib.tests.http_server import HttpServer
568
470
# connect to '.' via http which is not listable
569
471
server = HttpServer()
570
self.start_server(server)
571
t = self.get_nfs_transport(server.get_url())
572
self.assertIsInstance(t, fakenfs.FakeNFSTransportDecorator)
573
self.assertEqual(False, t.listable())
574
self.assertEqual(True, t.is_readonly())
474
transport = self.get_nfs_transport(server.get_url())
475
self.assertIsInstance(
476
transport, bzrlib.transport.fakenfs.FakeNFSTransportDecorator)
477
self.assertEqual(False, transport.listable())
478
self.assertEqual(True, transport.is_readonly())
576
482
def test_fakenfs_server_default(self):
577
483
# a FakeNFSServer() should bring up a local relpath server for itself
578
server = test_server.FakeNFSServer()
579
self.start_server(server)
580
# the url should be decorated appropriately
581
self.assertStartsWith(server.get_url(), 'fakenfs+')
582
# and we should be able to get a transport for it
583
t = transport.get_transport_from_url(server.get_url())
584
# which must be a FakeNFSTransportDecorator instance.
585
self.assertIsInstance(t, fakenfs.FakeNFSTransportDecorator)
484
import bzrlib.transport.fakenfs as fakenfs
485
server = fakenfs.FakeNFSServer()
488
# the url should be decorated appropriately
489
self.assertStartsWith(server.get_url(), 'fakenfs+')
490
# and we should be able to get a transport for it
491
transport = get_transport(server.get_url())
492
# which must be a FakeNFSTransportDecorator instance.
493
self.assertIsInstance(
494
transport, fakenfs.FakeNFSTransportDecorator)
587
498
def test_fakenfs_rename_semantics(self):
588
499
# a FakeNFS transport must mangle the way rename errors occur to
589
500
# look like NFS problems.
590
t = self.get_nfs_transport('.')
501
transport = self.get_nfs_transport('.')
591
502
self.build_tree(['from/', 'from/foo', 'to/', 'to/bar'],
593
self.assertRaises(errors.ResourceBusy, t.rename, 'from', 'to')
596
class FakeVFATDecoratorTests(tests.TestCaseInTempDir):
504
self.assertRaises(errors.ResourceBusy,
505
transport.rename, 'from', 'to')
508
class FakeVFATDecoratorTests(TestCaseInTempDir):
597
509
"""Tests for simulation of VFAT restrictions"""
599
511
def get_vfat_transport(self, url):
604
516
def test_transport_creation(self):
605
517
from bzrlib.transport.fakevfat import FakeVFATTransportDecorator
606
t = self.get_vfat_transport('.')
607
self.assertIsInstance(t, FakeVFATTransportDecorator)
518
transport = self.get_vfat_transport('.')
519
self.assertIsInstance(transport, FakeVFATTransportDecorator)
609
521
def test_transport_mkdir(self):
610
t = self.get_vfat_transport('.')
612
self.assertTrue(t.has('hello'))
613
self.assertTrue(t.has('Hello'))
522
transport = self.get_vfat_transport('.')
523
transport.mkdir('HELLO')
524
self.assertTrue(transport.has('hello'))
525
self.assertTrue(transport.has('Hello'))
615
527
def test_forbidden_chars(self):
616
t = self.get_vfat_transport('.')
617
self.assertRaises(ValueError, t.has, "<NU>")
620
class BadTransportHandler(transport.Transport):
528
transport = self.get_vfat_transport('.')
529
self.assertRaises(ValueError, transport.has, "<NU>")
532
class BadTransportHandler(Transport):
621
533
def __init__(self, base_url):
622
raise errors.DependencyNotPresent('some_lib',
623
'testing missing dependency')
626
class BackupTransportHandler(transport.Transport):
534
raise DependencyNotPresent('some_lib', 'testing missing dependency')
537
class BackupTransportHandler(Transport):
627
538
"""Test transport that works as a backup for the BadTransportHandler"""
631
class TestTransportImplementation(tests.TestCaseInTempDir):
542
class TestTransportImplementation(TestCaseInTempDir):
632
543
"""Implementation verification for transports.
634
545
To verify a transport we need a server factory, which is a callable
635
546
that accepts no parameters and returns an implementation of
636
547
bzrlib.transport.Server.
638
549
That Server is then used to construct transport instances and test
639
550
the transport via loopback activity.
641
Currently this assumes that the Transport object is connected to the
642
current working directory. So that whatever is done
643
through the transport, should show up in the working
552
Currently this assumes that the Transport object is connected to the
553
current working directory. So that whatever is done
554
through the transport, should show up in the working
644
555
directory, and vice-versa. This is a bug, because its possible to have
645
URL schemes which provide access to something that may not be
646
result in storage on the local disk, i.e. due to file system limits, or
556
URL schemes which provide access to something that may not be
557
result in storage on the local disk, i.e. due to file system limits, or
647
558
due to it being a database or some other non-filesystem tool.
649
560
This also tests to make sure that the functions work with both
650
561
generators and lists (assuming iter(list) is effectively a generator)
654
565
super(TestTransportImplementation, self).setUp()
655
566
self._server = self.transport_server()
656
self.start_server(self._server)
568
self.addCleanup(self._server.tearDown)
658
570
def get_transport(self, relpath=None):
659
571
"""Return a connected transport to the local directory.
677
class TestTransportFromPath(tests.TestCaseInTempDir):
679
def test_with_path(self):
680
t = transport.get_transport_from_path(self.test_dir)
681
self.assertIsInstance(t, local.LocalTransport)
682
self.assertEquals(t.base.rstrip("/"),
683
urlutils.local_path_to_url(self.test_dir))
685
def test_with_url(self):
686
t = transport.get_transport_from_path("file:")
687
self.assertIsInstance(t, local.LocalTransport)
688
self.assertEquals(t.base.rstrip("/"),
689
urlutils.local_path_to_url(os.path.join(self.test_dir, "file:")))
692
class TestTransportFromUrl(tests.TestCaseInTempDir):
694
def test_with_path(self):
695
self.assertRaises(errors.InvalidURL, transport.get_transport_from_url,
698
def test_with_url(self):
699
url = urlutils.local_path_to_url(self.test_dir)
700
t = transport.get_transport_from_url(url)
701
self.assertIsInstance(t, local.LocalTransport)
702
self.assertEquals(t.base.rstrip("/"), url)
704
def test_with_url_and_segment_parameters(self):
705
url = urlutils.local_path_to_url(self.test_dir)+",branch=foo"
706
t = transport.get_transport_from_url(url)
707
self.assertIsInstance(t, local.LocalTransport)
708
self.assertEquals(t.base.rstrip("/"), url)
709
with open(os.path.join(self.test_dir, "afile"), 'w') as f:
711
self.assertTrue(t.has("afile"))
714
class TestLocalTransports(tests.TestCase):
589
class TestLocalTransports(TestCase):
716
591
def test_get_transport_from_abspath(self):
717
592
here = osutils.abspath('.')
718
t = transport.get_transport(here)
719
self.assertIsInstance(t, local.LocalTransport)
593
t = get_transport(here)
594
self.assertIsInstance(t, LocalTransport)
720
595
self.assertEquals(t.base, urlutils.local_path_to_url(here) + '/')
722
597
def test_get_transport_from_relpath(self):
723
598
here = osutils.abspath('.')
724
t = transport.get_transport('.')
725
self.assertIsInstance(t, local.LocalTransport)
599
t = get_transport('.')
600
self.assertIsInstance(t, LocalTransport)
726
601
self.assertEquals(t.base, urlutils.local_path_to_url('.') + '/')
728
603
def test_get_transport_from_local_url(self):
729
604
here = osutils.abspath('.')
730
605
here_url = urlutils.local_path_to_url(here) + '/'
731
t = transport.get_transport(here_url)
732
self.assertIsInstance(t, local.LocalTransport)
606
t = get_transport(here_url)
607
self.assertIsInstance(t, LocalTransport)
733
608
self.assertEquals(t.base, here_url)
735
610
def test_local_abspath(self):
736
611
here = osutils.abspath('.')
737
t = transport.get_transport(here)
612
t = get_transport(here)
738
613
self.assertEquals(t.local_abspath(''), here)
741
class TestLocalTransportWriteStream(tests.TestCaseWithTransport):
743
def test_local_fdatasync_calls_fdatasync(self):
744
"""Check fdatasync on a stream tries to flush the data to the OS.
746
We can't easily observe the external effect but we can at least see
750
fdatasync = getattr(os, 'fdatasync', sentinel)
751
if fdatasync is sentinel:
752
raise tests.TestNotApplicable('fdatasync not supported')
753
t = self.get_transport('.')
754
calls = self.recordCalls(os, 'fdatasync')
755
w = t.open_write_stream('out')
758
with open('out', 'rb') as f:
759
# Should have been flushed.
760
self.assertEquals(f.read(), 'foo')
761
self.assertEquals(len(calls), 1, calls)
763
def test_missing_directory(self):
764
t = self.get_transport('.')
765
self.assertRaises(errors.NoSuchFile, t.open_write_stream, 'dir/foo')
768
class TestWin32LocalTransport(tests.TestCase):
616
class TestWin32LocalTransport(TestCase):
770
618
def test_unc_clone_to_root(self):
771
619
# Win32 UNC path like \\HOST\path
772
620
# clone to root should stop at least at \\HOST part
774
t = local.EmulatedWin32LocalTransport('file://HOST/path/to/some/dir/')
622
t = EmulatedWin32LocalTransport('file://HOST/path/to/some/dir/')
775
623
for i in xrange(4):
776
624
t = t.clone('..')
777
625
self.assertEquals(t.base, 'file://HOST/')
780
628
self.assertEquals(t.base, 'file://HOST/')
783
class TestConnectedTransport(tests.TestCase):
631
class TestConnectedTransport(TestCase):
784
632
"""Tests for connected to remote server transports"""
786
634
def test_parse_url(self):
787
t = transport.ConnectedTransport(
788
'http://simple.example.com/home/source')
789
self.assertEquals(t._parsed_url.host, 'simple.example.com')
790
self.assertEquals(t._parsed_url.port, None)
791
self.assertEquals(t._parsed_url.path, '/home/source/')
792
self.assertTrue(t._parsed_url.user is None)
793
self.assertTrue(t._parsed_url.password is None)
635
t = ConnectedTransport('http://simple.example.com/home/source')
636
self.assertEquals(t._host, 'simple.example.com')
637
self.assertEquals(t._port, None)
638
self.assertEquals(t._path, '/home/source/')
639
self.failUnless(t._user is None)
640
self.failUnless(t._password is None)
795
642
self.assertEquals(t.base, 'http://simple.example.com/home/source/')
797
def test_parse_url_with_at_in_user(self):
799
t = transport.ConnectedTransport('ftp://user@host.com@www.host.com/')
800
self.assertEquals(t._parsed_url.user, 'user@host.com')
802
644
def test_parse_quoted_url(self):
803
t = transport.ConnectedTransport(
804
'http://ro%62ey:h%40t@ex%41mple.com:2222/path')
805
self.assertEquals(t._parsed_url.host, 'exAmple.com')
806
self.assertEquals(t._parsed_url.port, 2222)
807
self.assertEquals(t._parsed_url.user, 'robey')
808
self.assertEquals(t._parsed_url.password, 'h@t')
809
self.assertEquals(t._parsed_url.path, '/path/')
645
t = ConnectedTransport('http://ro%62ey:h%40t@ex%41mple.com:2222/path')
646
self.assertEquals(t._host, 'exAmple.com')
647
self.assertEquals(t._port, 2222)
648
self.assertEquals(t._user, 'robey')
649
self.assertEquals(t._password, 'h@t')
650
self.assertEquals(t._path, '/path/')
811
652
# Base should not keep track of the password
812
self.assertEquals(t.base, 'http://ro%62ey@ex%41mple.com:2222/path/')
653
self.assertEquals(t.base, 'http://robey@exAmple.com:2222/path/')
814
655
def test_parse_invalid_url(self):
815
656
self.assertRaises(errors.InvalidURL,
816
transport.ConnectedTransport,
817
658
'sftp://lily.org:~janneke/public/bzr/gub')
819
660
def test_relpath(self):
820
t = transport.ConnectedTransport('sftp://user@host.com/abs/path')
661
t = ConnectedTransport('sftp://user@host.com/abs/path')
822
self.assertEquals(t.relpath('sftp://user@host.com/abs/path/sub'),
663
self.assertEquals(t.relpath('sftp://user@host.com/abs/path/sub'), 'sub')
824
664
self.assertRaises(errors.PathNotChild, t.relpath,
825
665
'http://user@host.com/abs/path/sub')
826
666
self.assertRaises(errors.PathNotChild, t.relpath,
865
705
self.assertIs(new_password, c._get_credentials())
868
class TestReusedTransports(tests.TestCase):
708
class TestReusedTransports(TestCase):
869
709
"""Tests for transport reuse"""
871
711
def test_reuse_same_transport(self):
872
712
possible_transports = []
873
t1 = transport.get_transport_from_url('http://foo/',
874
possible_transports=possible_transports)
713
t1 = get_transport('http://foo/',
714
possible_transports=possible_transports)
875
715
self.assertEqual([t1], possible_transports)
876
t2 = transport.get_transport_from_url('http://foo/',
877
possible_transports=[t1])
716
t2 = get_transport('http://foo/', possible_transports=[t1])
878
717
self.assertIs(t1, t2)
880
719
# Also check that final '/' are handled correctly
881
t3 = transport.get_transport_from_url('http://foo/path/')
882
t4 = transport.get_transport_from_url('http://foo/path',
883
possible_transports=[t3])
720
t3 = get_transport('http://foo/path/')
721
t4 = get_transport('http://foo/path', possible_transports=[t3])
884
722
self.assertIs(t3, t4)
886
t5 = transport.get_transport_from_url('http://foo/path')
887
t6 = transport.get_transport_from_url('http://foo/path/',
888
possible_transports=[t5])
724
t5 = get_transport('http://foo/path')
725
t6 = get_transport('http://foo/path/', possible_transports=[t5])
889
726
self.assertIs(t5, t6)
891
728
def test_don_t_reuse_different_transport(self):
892
t1 = transport.get_transport_from_url('http://foo/path')
893
t2 = transport.get_transport_from_url('http://bar/path',
894
possible_transports=[t1])
729
t1 = get_transport('http://foo/path')
730
t2 = get_transport('http://bar/path', possible_transports=[t1])
895
731
self.assertIsNot(t1, t2)
898
class TestTransportTrace(tests.TestCase):
734
class TestTransportTrace(TestCase):
900
def test_decorator(self):
901
t = transport.get_transport_from_url('trace+memory://')
737
transport = get_transport('trace+memory://')
902
738
self.assertIsInstance(
903
t, bzrlib.transport.trace.TransportTraceDecorator)
739
transport, bzrlib.transport.trace.TransportTraceDecorator)
905
741
def test_clone_preserves_activity(self):
906
t = transport.get_transport_from_url('trace+memory://')
908
self.assertTrue(t is not t2)
909
self.assertTrue(t._activity is t2._activity)
742
transport = get_transport('trace+memory://')
743
transport2 = transport.clone('.')
744
self.assertTrue(transport is not transport2)
745
self.assertTrue(transport._activity is transport2._activity)
911
747
# the following specific tests are for the operations that have made use of
912
748
# logging in tests; we could test every single operation but doing that
913
749
# still won't cause a test failure when the top level Transport API
914
750
# changes; so there is little return doing that.
915
751
def test_get(self):
916
t = transport.get_transport_from_url('trace+memory:///')
917
t.put_bytes('foo', 'barish')
752
transport = get_transport('trace+memory:///')
753
transport.put_bytes('foo', 'barish')
919
755
expected_result = []
920
756
# put_bytes records the bytes, not the content to avoid memory
922
758
expected_result.append(('put_bytes', 'foo', 6, None))
923
759
# get records the file name only.
924
760
expected_result.append(('get', 'foo'))
925
self.assertEqual(expected_result, t._activity)
761
self.assertEqual(expected_result, transport._activity)
927
763
def test_readv(self):
928
t = transport.get_transport_from_url('trace+memory:///')
929
t.put_bytes('foo', 'barish')
930
list(t.readv('foo', [(0, 1), (3, 2)],
931
adjust_for_latency=True, upper_limit=6))
764
transport = get_transport('trace+memory:///')
765
transport.put_bytes('foo', 'barish')
766
list(transport.readv('foo', [(0, 1), (3, 2)], adjust_for_latency=True,
932
768
expected_result = []
933
769
# put_bytes records the bytes, not the content to avoid memory
935
771
expected_result.append(('put_bytes', 'foo', 6, None))
936
772
# readv records the supplied offset request
937
773
expected_result.append(('readv', 'foo', [(0, 1), (3, 2)], True, 6))
938
self.assertEqual(expected_result, t._activity)
941
class TestSSHConnections(tests.TestCaseWithTransport):
943
def test_bzr_connect_to_bzr_ssh(self):
944
"""get_transport of a bzr+ssh:// behaves correctly.
946
bzr+ssh:// should cause bzr to run a remote bzr smart server over SSH.
948
# This test actually causes a bzr instance to be invoked, which is very
949
# expensive: it should be the only such test in the test suite.
950
# A reasonable evolution for this would be to simply check inside
951
# check_channel_exec_request that the command is appropriate, and then
952
# satisfy requests in-process.
953
self.requireFeature(features.paramiko)
954
# SFTPFullAbsoluteServer has a get_url method, and doesn't
955
# override the interface (doesn't change self._vendor).
956
# Note that this does encryption, so can be slow.
957
from bzrlib.tests import stub_sftp
959
# Start an SSH server
960
self.command_executed = []
961
# XXX: This is horrible -- we define a really dumb SSH server that
962
# executes commands, and manage the hooking up of stdin/out/err to the
963
# SSH channel ourselves. Surely this has already been implemented
967
class StubSSHServer(stub_sftp.StubServer):
971
def check_channel_exec_request(self, channel, command):
972
self.test.command_executed.append(command)
973
proc = subprocess.Popen(
974
command, shell=True, stdin=subprocess.PIPE,
975
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
977
# XXX: horribly inefficient, not to mention ugly.
978
# Start a thread for each of stdin/out/err, and relay bytes
979
# from the subprocess to channel and vice versa.
980
def ferry_bytes(read, write, close):
989
(channel.recv, proc.stdin.write, proc.stdin.close),
990
(proc.stdout.read, channel.sendall, channel.close),
991
(proc.stderr.read, channel.sendall_stderr, channel.close)]
993
for read, write, close in file_functions:
994
t = threading.Thread(
995
target=ferry_bytes, args=(read, write, close))
1001
ssh_server = stub_sftp.SFTPFullAbsoluteServer(StubSSHServer)
1002
# We *don't* want to override the default SSH vendor: the detected one
1003
# is the one to use.
1005
# FIXME: I don't understand the above comment, SFTPFullAbsoluteServer
1006
# inherits from SFTPServer which forces the SSH vendor to
1007
# ssh.ParamikoVendor(). So it's forced, not detected. --vila 20100623
1008
self.start_server(ssh_server)
1009
port = ssh_server.port
1011
if sys.platform == 'win32':
1012
bzr_remote_path = sys.executable + ' ' + self.get_bzr_path()
1014
bzr_remote_path = self.get_bzr_path()
1015
self.overrideEnv('BZR_REMOTE_PATH', bzr_remote_path)
1017
# Access the branch via a bzr+ssh URL. The BZR_REMOTE_PATH environment
1018
# variable is used to tell bzr what command to run on the remote end.
1019
path_to_branch = osutils.abspath('.')
1020
if sys.platform == 'win32':
1021
# On Windows, we export all drives as '/C:/, etc. So we need to
1022
# prefix a '/' to get the right path.
1023
path_to_branch = '/' + path_to_branch
1024
url = 'bzr+ssh://fred:secret@localhost:%d%s' % (port, path_to_branch)
1025
t = transport.get_transport(url)
1026
self.permit_url(t.base)
1030
['%s serve --inet --directory=/ --allow-writes' % bzr_remote_path],
1031
self.command_executed)
1032
# Make sure to disconnect, so that the remote process can stop, and we
1033
# can cleanup. Then pause the test until everything is shutdown
1034
t._client._medium.disconnect()
1037
# First wait for the subprocess
1039
# And the rest are threads
1040
for t in started[1:]:
1044
class TestUnhtml(tests.TestCase):
1046
"""Tests for unhtml_roughly"""
1048
def test_truncation(self):
1049
fake_html = "<p>something!\n" * 1000
1050
result = http.unhtml_roughly(fake_html)
1051
self.assertEquals(len(result), 1000)
1052
self.assertStartsWith(result, " something!")
1055
class SomeDirectory(object):
1057
def look_up(self, name, url):
1061
class TestLocationToUrl(tests.TestCase):
1063
def get_base_location(self):
1064
path = osutils.abspath('/foo/bar')
1065
if path.startswith('/'):
1066
url = 'file://%s' % (path,)
1068
# On Windows, abspaths start with the drive letter, so we have to
1069
# add in the extra '/'
1070
url = 'file:///%s' % (path,)
1073
def test_regular_url(self):
1074
self.assertEquals("file://foo", location_to_url("file://foo"))
1076
def test_directory(self):
1077
directories.register("bar:", SomeDirectory, "Dummy directory")
1078
self.addCleanup(directories.remove, "bar:")
1079
self.assertEquals("http://bar", location_to_url("bar:"))
1081
def test_unicode_url(self):
1082
self.assertRaises(errors.InvalidURL, location_to_url,
1083
"http://fo/\xc3\xaf".decode("utf-8"))
1085
def test_unicode_path(self):
1086
path, url = self.get_base_location()
1087
location = path + "\xc3\xaf".decode("utf-8")
1089
self.assertEquals(url, location_to_url(location))
1091
def test_path(self):
1092
path, url = self.get_base_location()
1093
self.assertEquals(url, location_to_url(path))
1095
def test_relative_file_url(self):
1096
self.assertEquals(urlutils.local_path_to_url(".") + "/bar",
1097
location_to_url("file:bar"))
1099
def test_absolute_file_url(self):
1100
self.assertEquals("file:///bar", location_to_url("file:/bar"))
774
self.assertEqual(expected_result, transport._activity)