260
236
max_size=1*1024*1024*1024)
263
class TestMemoryServer(TestCase):
265
def test_create_server(self):
266
server = memory.MemoryServer()
267
server.start_server()
268
url = server.get_url()
269
self.assertTrue(url in _mod_transport.transport_list_registry)
270
t = _mod_transport.get_transport(url)
273
self.assertFalse(url in _mod_transport.transport_list_registry)
274
self.assertRaises(errors.UnsupportedProtocol,
275
_mod_transport.get_transport, url)
278
239
class TestMemoryTransport(TestCase):
280
241
def test_get_transport(self):
281
memory.MemoryTransport()
283
244
def test_clone(self):
284
transport = memory.MemoryTransport()
285
self.assertTrue(isinstance(transport, memory.MemoryTransport))
245
transport = MemoryTransport()
246
self.assertTrue(isinstance(transport, MemoryTransport))
286
247
self.assertEqual("memory:///", transport.clone("/").base)
288
249
def test_abspath(self):
289
transport = memory.MemoryTransport()
250
transport = MemoryTransport()
290
251
self.assertEqual("memory:///relpath", transport.abspath('relpath'))
292
253
def test_abspath_of_root(self):
293
transport = memory.MemoryTransport()
254
transport = MemoryTransport()
294
255
self.assertEqual("memory:///", transport.base)
295
256
self.assertEqual("memory:///", transport.abspath('/'))
297
258
def test_abspath_of_relpath_starting_at_root(self):
298
transport = memory.MemoryTransport()
259
transport = MemoryTransport()
299
260
self.assertEqual("memory:///foo", transport.abspath('/foo'))
301
262
def test_append_and_get(self):
302
transport = memory.MemoryTransport()
263
transport = MemoryTransport()
303
264
transport.append_bytes('path', 'content')
304
265
self.assertEqual(transport.get('path').read(), 'content')
305
266
transport.append_file('path', StringIO('content'))
306
267
self.assertEqual(transport.get('path').read(), 'contentcontent')
308
269
def test_put_and_get(self):
309
transport = memory.MemoryTransport()
270
transport = MemoryTransport()
310
271
transport.put_file('path', StringIO('content'))
311
272
self.assertEqual(transport.get('path').read(), 'content')
312
273
transport.put_bytes('path', 'content')
313
274
self.assertEqual(transport.get('path').read(), 'content')
315
276
def test_append_without_dir_fails(self):
316
transport = memory.MemoryTransport()
277
transport = MemoryTransport()
317
278
self.assertRaises(NoSuchFile,
318
279
transport.append_bytes, 'dir/path', 'content')
320
281
def test_put_without_dir_fails(self):
321
transport = memory.MemoryTransport()
282
transport = MemoryTransport()
322
283
self.assertRaises(NoSuchFile,
323
284
transport.put_file, 'dir/path', StringIO('content'))
325
286
def test_get_missing(self):
326
transport = memory.MemoryTransport()
287
transport = MemoryTransport()
327
288
self.assertRaises(NoSuchFile, transport.get, 'foo')
329
290
def test_has_missing(self):
330
transport = memory.MemoryTransport()
291
transport = MemoryTransport()
331
292
self.assertEquals(False, transport.has('foo'))
333
294
def test_has_present(self):
334
transport = memory.MemoryTransport()
295
transport = MemoryTransport()
335
296
transport.append_bytes('foo', 'content')
336
297
self.assertEquals(True, transport.has('foo'))
338
299
def test_list_dir(self):
339
transport = memory.MemoryTransport()
300
transport = MemoryTransport()
340
301
transport.put_bytes('foo', 'content')
341
302
transport.mkdir('dir')
342
303
transport.put_bytes('dir/subfoo', 'content')
346
307
self.assertEquals(['subfoo'], sorted(transport.list_dir('dir')))
348
309
def test_mkdir(self):
349
transport = memory.MemoryTransport()
310
transport = MemoryTransport()
350
311
transport.mkdir('dir')
351
312
transport.append_bytes('dir/path', 'content')
352
313
self.assertEqual(transport.get('dir/path').read(), 'content')
354
315
def test_mkdir_missing_parent(self):
355
transport = memory.MemoryTransport()
316
transport = MemoryTransport()
356
317
self.assertRaises(NoSuchFile,
357
318
transport.mkdir, 'dir/dir')
359
320
def test_mkdir_twice(self):
360
transport = memory.MemoryTransport()
321
transport = MemoryTransport()
361
322
transport.mkdir('dir')
362
323
self.assertRaises(FileExists, transport.mkdir, 'dir')
364
325
def test_parameters(self):
365
transport = memory.MemoryTransport()
326
transport = MemoryTransport()
366
327
self.assertEqual(True, transport.listable())
367
328
self.assertEqual(False, transport.is_readonly())
369
330
def test_iter_files_recursive(self):
370
transport = memory.MemoryTransport()
331
transport = MemoryTransport()
371
332
transport.mkdir('dir')
372
333
transport.put_bytes('dir/foo', 'content')
373
334
transport.put_bytes('dir/bar', 'content')
389
350
def test_abspath(self):
390
351
# The abspath is always relative to the chroot_url.
391
352
server = ChrootServer(get_transport('memory:///foo/bar/'))
392
self.start_server(server)
393
354
transport = get_transport(server.get_url())
394
355
self.assertEqual(server.get_url(), transport.abspath('/'))
396
357
subdir_transport = transport.clone('subdir')
397
358
self.assertEqual(server.get_url(), subdir_transport.abspath('/'))
399
361
def test_clone(self):
400
362
server = ChrootServer(get_transport('memory:///foo/bar/'))
401
self.start_server(server)
402
364
transport = get_transport(server.get_url())
403
365
# relpath from root and root path are the same
404
366
relpath_cloned = transport.clone('foo')
405
367
abspath_cloned = transport.clone('/foo')
406
368
self.assertEqual(server, relpath_cloned.server)
407
369
self.assertEqual(server, abspath_cloned.server)
409
372
def test_chroot_url_preserves_chroot(self):
410
373
"""Calling get_transport on a chroot transport's base should produce a
411
374
transport with exactly the same behaviour as the original chroot
433
397
new_transport = get_transport(parent_url)
435
399
server = ChrootServer(get_transport('memory:///path/'))
436
self.start_server(server)
437
401
transport = get_transport(server.get_url())
438
402
self.assertRaises(
439
403
InvalidURLJoin, urlutils.join, transport.base, '..')
442
407
class ChrootServerTest(TestCase):
444
409
def test_construct(self):
445
backing_transport = memory.MemoryTransport()
410
backing_transport = MemoryTransport()
446
411
server = ChrootServer(backing_transport)
447
412
self.assertEqual(backing_transport, server.backing_transport)
449
414
def test_setUp(self):
450
backing_transport = memory.MemoryTransport()
415
backing_transport = MemoryTransport()
451
416
server = ChrootServer(backing_transport)
452
server.start_server()
454
self.assertTrue(server.scheme in _get_protocol_handlers().keys())
418
self.assertTrue(server.scheme in _get_protocol_handlers().keys())
458
def test_stop_server(self):
459
backing_transport = memory.MemoryTransport()
420
def test_tearDown(self):
421
backing_transport = MemoryTransport()
460
422
server = ChrootServer(backing_transport)
461
server.start_server()
463
425
self.assertFalse(server.scheme in _get_protocol_handlers().keys())
465
427
def test_get_url(self):
466
backing_transport = memory.MemoryTransport()
428
backing_transport = MemoryTransport()
467
429
server = ChrootServer(backing_transport)
468
server.start_server()
470
self.assertEqual('chroot-%d:///' % id(server), server.get_url())
475
class PathFilteringDecoratorTransportTest(TestCase):
476
"""Pathfilter decoration specific tests."""
478
def test_abspath(self):
479
# The abspath is always relative to the base of the backing transport.
480
server = PathFilteringServer(get_transport('memory:///foo/bar/'),
482
server.start_server()
483
transport = get_transport(server.get_url())
484
self.assertEqual(server.get_url(), transport.abspath('/'))
486
subdir_transport = transport.clone('subdir')
487
self.assertEqual(server.get_url(), subdir_transport.abspath('/'))
490
def make_pf_transport(self, filter_func=None):
491
"""Make a PathFilteringTransport backed by a MemoryTransport.
493
:param filter_func: by default this will be a no-op function. Use this
494
parameter to override it."""
495
if filter_func is None:
496
filter_func = lambda x: x
497
server = PathFilteringServer(
498
get_transport('memory:///foo/bar/'), filter_func)
499
server.start_server()
500
self.addCleanup(server.stop_server)
501
return get_transport(server.get_url())
503
def test__filter(self):
504
# _filter (with an identity func as filter_func) always returns
505
# paths relative to the base of the backing transport.
506
transport = self.make_pf_transport()
507
self.assertEqual('foo', transport._filter('foo'))
508
self.assertEqual('foo/bar', transport._filter('foo/bar'))
509
self.assertEqual('', transport._filter('..'))
510
self.assertEqual('', transport._filter('/'))
511
# The base of the pathfiltering transport is taken into account too.
512
transport = transport.clone('subdir1/subdir2')
513
self.assertEqual('subdir1/subdir2/foo', transport._filter('foo'))
515
'subdir1/subdir2/foo/bar', transport._filter('foo/bar'))
516
self.assertEqual('subdir1', transport._filter('..'))
517
self.assertEqual('', transport._filter('/'))
519
def test_filter_invocation(self):
522
filter_log.append(path)
524
transport = self.make_pf_transport(filter)
526
self.assertEqual(['abc'], filter_log)
528
transport.clone('abc').has('xyz')
529
self.assertEqual(['abc/xyz'], filter_log)
531
transport.has('/abc')
532
self.assertEqual(['abc'], filter_log)
534
def test_clone(self):
535
transport = self.make_pf_transport()
536
# relpath from root and root path are the same
537
relpath_cloned = transport.clone('foo')
538
abspath_cloned = transport.clone('/foo')
539
self.assertEqual(transport.server, relpath_cloned.server)
540
self.assertEqual(transport.server, abspath_cloned.server)
542
def test_url_preserves_pathfiltering(self):
543
"""Calling get_transport on a pathfiltered transport's base should
544
produce a transport with exactly the same behaviour as the original
545
pathfiltered transport.
547
This is so that it is not possible to escape (accidentally or
548
otherwise) the filtering by doing::
549
url = filtered_transport.base
550
parent_url = urlutils.join(url, '..')
551
new_transport = get_transport(parent_url)
553
transport = self.make_pf_transport()
554
new_transport = get_transport(transport.base)
555
self.assertEqual(transport.server, new_transport.server)
556
self.assertEqual(transport.base, new_transport.base)
431
self.assertEqual('chroot-%d:///' % id(server), server.get_url())
559
435
class ReadonlyDecoratorTransportTest(TestCase):
560
436
"""Readonly decoration specific tests."""
562
438
def test_local_parameters(self):
439
import bzrlib.transport.readonly as readonly
563
440
# connect to . in readonly mode
564
441
transport = readonly.ReadonlyTransportDecorator('readonly+.')
565
442
self.assertEqual(True, transport.listable())
568
445
def test_http_parameters(self):
569
446
from bzrlib.tests.http_server import HttpServer
447
import bzrlib.transport.readonly as readonly
570
448
# connect to '.' via http which is not listable
571
449
server = HttpServer()
572
self.start_server(server)
573
transport = get_transport('readonly+' + server.get_url())
574
self.failUnless(isinstance(transport,
575
readonly.ReadonlyTransportDecorator))
576
self.assertEqual(False, transport.listable())
577
self.assertEqual(True, transport.is_readonly())
452
transport = get_transport('readonly+' + server.get_url())
453
self.failUnless(isinstance(transport,
454
readonly.ReadonlyTransportDecorator))
455
self.assertEqual(False, transport.listable())
456
self.assertEqual(True, transport.is_readonly())
580
461
class FakeNFSDecoratorTests(TestCaseInTempDir):
581
462
"""NFS decorator specific tests."""
583
464
def get_nfs_transport(self, url):
465
import bzrlib.transport.fakenfs as fakenfs
584
466
# connect to url with nfs decoration
585
467
return fakenfs.FakeNFSTransportDecorator('fakenfs+' + url)
597
479
from bzrlib.tests.http_server import HttpServer
598
480
# connect to '.' via http which is not listable
599
481
server = HttpServer()
600
self.start_server(server)
601
transport = self.get_nfs_transport(server.get_url())
602
self.assertIsInstance(
603
transport, fakenfs.FakeNFSTransportDecorator)
604
self.assertEqual(False, transport.listable())
605
self.assertEqual(True, transport.is_readonly())
484
transport = self.get_nfs_transport(server.get_url())
485
self.assertIsInstance(
486
transport, bzrlib.transport.fakenfs.FakeNFSTransportDecorator)
487
self.assertEqual(False, transport.listable())
488
self.assertEqual(True, transport.is_readonly())
607
492
def test_fakenfs_server_default(self):
608
493
# a FakeNFSServer() should bring up a local relpath server for itself
494
import bzrlib.transport.fakenfs as fakenfs
609
495
server = fakenfs.FakeNFSServer()
610
self.start_server(server)
611
# the url should be decorated appropriately
612
self.assertStartsWith(server.get_url(), 'fakenfs+')
613
# and we should be able to get a transport for it
614
transport = get_transport(server.get_url())
615
# which must be a FakeNFSTransportDecorator instance.
616
self.assertIsInstance(transport, fakenfs.FakeNFSTransportDecorator)
498
# the url should be decorated appropriately
499
self.assertStartsWith(server.get_url(), 'fakenfs+')
500
# and we should be able to get a transport for it
501
transport = get_transport(server.get_url())
502
# which must be a FakeNFSTransportDecorator instance.
503
self.assertIsInstance(
504
transport, fakenfs.FakeNFSTransportDecorator)
618
508
def test_fakenfs_rename_semantics(self):
619
509
# a FakeNFS transport must mangle the way rename errors occur to
662
552
class TestTransportImplementation(TestCaseInTempDir):
663
553
"""Implementation verification for transports.
665
555
To verify a transport we need a server factory, which is a callable
666
556
that accepts no parameters and returns an implementation of
667
557
bzrlib.transport.Server.
669
559
That Server is then used to construct transport instances and test
670
560
the transport via loopback activity.
672
Currently this assumes that the Transport object is connected to the
673
current working directory. So that whatever is done
674
through the transport, should show up in the working
562
Currently this assumes that the Transport object is connected to the
563
current working directory. So that whatever is done
564
through the transport, should show up in the working
675
565
directory, and vice-versa. This is a bug, because its possible to have
676
URL schemes which provide access to something that may not be
677
result in storage on the local disk, i.e. due to file system limits, or
566
URL schemes which provide access to something that may not be
567
result in storage on the local disk, i.e. due to file system limits, or
678
568
due to it being a database or some other non-filesystem tool.
680
570
This also tests to make sure that the functions work with both
681
571
generators and lists (assuming iter(list) is effectively a generator)
685
575
super(TestTransportImplementation, self).setUp()
686
576
self._server = self.transport_server()
687
self.start_server(self._server)
578
self.addCleanup(self._server.tearDown)
689
580
def get_transport(self, relpath=None):
690
581
"""Return a connected transport to the local directory.
896
787
# readv records the supplied offset request
897
788
expected_result.append(('readv', 'foo', [(0, 1), (3, 2)], True, 6))
898
789
self.assertEqual(expected_result, transport._activity)
901
class TestSSHConnections(tests.TestCaseWithTransport):
903
def test_bzr_connect_to_bzr_ssh(self):
904
"""User acceptance that get_transport of a bzr+ssh:// behaves correctly.
906
bzr+ssh:// should cause bzr to run a remote bzr smart server over SSH.
908
# This test actually causes a bzr instance to be invoked, which is very
909
# expensive: it should be the only such test in the test suite.
910
# A reasonable evolution for this would be to simply check inside
911
# check_channel_exec_request that the command is appropriate, and then
912
# satisfy requests in-process.
913
self.requireFeature(features.paramiko)
914
# SFTPFullAbsoluteServer has a get_url method, and doesn't
915
# override the interface (doesn't change self._vendor).
916
# Note that this does encryption, so can be slow.
917
from bzrlib.transport.sftp import SFTPFullAbsoluteServer
918
from bzrlib.tests.stub_sftp import StubServer
920
# Start an SSH server
921
self.command_executed = []
922
# XXX: This is horrible -- we define a really dumb SSH server that
923
# executes commands, and manage the hooking up of stdin/out/err to the
924
# SSH channel ourselves. Surely this has already been implemented
927
class StubSSHServer(StubServer):
931
def check_channel_exec_request(self, channel, command):
932
self.test.command_executed.append(command)
933
proc = subprocess.Popen(
934
command, shell=True, stdin=subprocess.PIPE,
935
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
937
# XXX: horribly inefficient, not to mention ugly.
938
# Start a thread for each of stdin/out/err, and relay bytes from
939
# the subprocess to channel and vice versa.
940
def ferry_bytes(read, write, close):
949
(channel.recv, proc.stdin.write, proc.stdin.close),
950
(proc.stdout.read, channel.sendall, channel.close),
951
(proc.stderr.read, channel.sendall_stderr, channel.close)]
953
for read, write, close in file_functions:
954
t = threading.Thread(
955
target=ferry_bytes, args=(read, write, close))
961
ssh_server = SFTPFullAbsoluteServer(StubSSHServer)
962
# We *don't* want to override the default SSH vendor: the detected one
964
self.start_server(ssh_server)
965
port = ssh_server._listener.port
967
if sys.platform == 'win32':
968
bzr_remote_path = sys.executable + ' ' + self.get_bzr_path()
970
bzr_remote_path = self.get_bzr_path()
971
os.environ['BZR_REMOTE_PATH'] = bzr_remote_path
973
# Access the branch via a bzr+ssh URL. The BZR_REMOTE_PATH environment
974
# variable is used to tell bzr what command to run on the remote end.
975
path_to_branch = osutils.abspath('.')
976
if sys.platform == 'win32':
977
# On Windows, we export all drives as '/C:/, etc. So we need to
978
# prefix a '/' to get the right path.
979
path_to_branch = '/' + path_to_branch
980
url = 'bzr+ssh://fred:secret@localhost:%d%s' % (port, path_to_branch)
981
t = get_transport(url)
982
self.permit_url(t.base)
986
['%s serve --inet --directory=/ --allow-writes' % bzr_remote_path],
987
self.command_executed)
988
# Make sure to disconnect, so that the remote process can stop, and we
989
# can cleanup. Then pause the test until everything is shutdown
990
t._client._medium.disconnect()
993
# First wait for the subprocess
995
# And the rest are threads
996
for t in started[1:]: