342
352
if not t._can_roundtrip_unix_modebits():
343
353
# Can't roundtrip, so no need to run this test
345
t.put_bytes('mode644', 'test text\n', mode=0644)
346
self.assertTransportMode(t, 'mode644', 0644)
347
t.put_bytes('mode666', 'test text\n', mode=0666)
348
self.assertTransportMode(t, 'mode666', 0666)
349
t.put_bytes('mode600', 'test text\n', mode=0600)
350
self.assertTransportMode(t, 'mode600', 0600)
355
t.put_bytes('mode644', 'test text\n', mode=0o644)
356
self.assertTransportMode(t, 'mode644', 0o644)
357
t.put_bytes('mode666', 'test text\n', mode=0o666)
358
self.assertTransportMode(t, 'mode666', 0o666)
359
t.put_bytes('mode600', 'test text\n', mode=0o600)
360
self.assertTransportMode(t, 'mode600', 0o600)
351
361
# Yes, you can put_bytes a file such that it becomes readonly
352
t.put_bytes('mode400', 'test text\n', mode=0400)
353
self.assertTransportMode(t, 'mode400', 0400)
362
t.put_bytes('mode400', 'test text\n', mode=0o400)
363
self.assertTransportMode(t, 'mode400', 0o400)
355
365
# The default permissions should be based on the current umask
356
366
umask = osutils.get_umask()
357
367
t.put_bytes('nomode', 'test text\n', mode=None)
358
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
368
self.assertTransportMode(t, 'nomode', 0o666 & ~umask)
360
370
def test_put_bytes_non_atomic_permissions(self):
361
371
t = self.get_transport()
365
375
if not t._can_roundtrip_unix_modebits():
366
376
# Can't roundtrip, so no need to run this test
368
t.put_bytes_non_atomic('mode644', 'test text\n', mode=0644)
369
self.assertTransportMode(t, 'mode644', 0644)
370
t.put_bytes_non_atomic('mode666', 'test text\n', mode=0666)
371
self.assertTransportMode(t, 'mode666', 0666)
372
t.put_bytes_non_atomic('mode600', 'test text\n', mode=0600)
373
self.assertTransportMode(t, 'mode600', 0600)
374
t.put_bytes_non_atomic('mode400', 'test text\n', mode=0400)
375
self.assertTransportMode(t, 'mode400', 0400)
378
t.put_bytes_non_atomic('mode644', 'test text\n', mode=0o644)
379
self.assertTransportMode(t, 'mode644', 0o644)
380
t.put_bytes_non_atomic('mode666', 'test text\n', mode=0o666)
381
self.assertTransportMode(t, 'mode666', 0o666)
382
t.put_bytes_non_atomic('mode600', 'test text\n', mode=0o600)
383
self.assertTransportMode(t, 'mode600', 0o600)
384
t.put_bytes_non_atomic('mode400', 'test text\n', mode=0o400)
385
self.assertTransportMode(t, 'mode400', 0o400)
377
387
# The default permissions should be based on the current umask
378
388
umask = osutils.get_umask()
379
389
t.put_bytes_non_atomic('nomode', 'test text\n', mode=None)
380
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
390
self.assertTransportMode(t, 'nomode', 0o666 & ~umask)
382
392
# We should also be able to set the mode for a parent directory
383
393
# when it is created
384
t.put_bytes_non_atomic('dir700/mode664', 'test text\n', mode=0664,
385
dir_mode=0700, create_parent_dir=True)
386
self.assertTransportMode(t, 'dir700', 0700)
387
t.put_bytes_non_atomic('dir770/mode664', 'test text\n', mode=0664,
388
dir_mode=0770, create_parent_dir=True)
389
self.assertTransportMode(t, 'dir770', 0770)
390
t.put_bytes_non_atomic('dir777/mode664', 'test text\n', mode=0664,
391
dir_mode=0777, create_parent_dir=True)
392
self.assertTransportMode(t, 'dir777', 0777)
394
t.put_bytes_non_atomic('dir700/mode664', 'test text\n', mode=0o664,
395
dir_mode=0o700, create_parent_dir=True)
396
self.assertTransportMode(t, 'dir700', 0o700)
397
t.put_bytes_non_atomic('dir770/mode664', 'test text\n', mode=0o664,
398
dir_mode=0o770, create_parent_dir=True)
399
self.assertTransportMode(t, 'dir770', 0o770)
400
t.put_bytes_non_atomic('dir777/mode664', 'test text\n', mode=0o664,
401
dir_mode=0o777, create_parent_dir=True)
402
self.assertTransportMode(t, 'dir777', 0o777)
394
404
def test_put_file(self):
395
405
t = self.get_transport()
397
407
if t.is_readonly():
398
408
self.assertRaises(TransportNotPossible,
399
t.put_file, 'a', StringIO('some text for a\n'))
409
t.put_file, 'a', BytesIO(b'some text for a\n'))
402
result = t.put_file('a', StringIO('some text for a\n'))
412
result = t.put_file('a', BytesIO(b'some text for a\n'))
403
413
# put_file returns the length of the data written
404
414
self.assertEqual(16, result)
405
self.failUnless(t.has('a'))
415
self.assertTrue(t.has('a'))
406
416
self.check_transport_contents('some text for a\n', t, 'a')
407
417
# Put also replaces contents
408
result = t.put_file('a', StringIO('new\ncontents for\na\n'))
418
result = t.put_file('a', BytesIO(b'new\ncontents for\na\n'))
409
419
self.assertEqual(19, result)
410
420
self.check_transport_contents('new\ncontents for\na\n', t, 'a')
411
421
self.assertRaises(NoSuchFile,
412
422
t.put_file, 'path/doesnt/exist/c',
413
StringIO('contents'))
423
BytesIO(b'contents'))
415
425
def test_put_file_non_atomic(self):
416
426
t = self.get_transport()
418
428
if t.is_readonly():
419
429
self.assertRaises(TransportNotPossible,
420
t.put_file_non_atomic, 'a', StringIO('some text for a\n'))
430
t.put_file_non_atomic, 'a', BytesIO(b'some text for a\n'))
423
self.failIf(t.has('a'))
424
t.put_file_non_atomic('a', StringIO('some text for a\n'))
425
self.failUnless(t.has('a'))
433
self.assertFalse(t.has('a'))
434
t.put_file_non_atomic('a', BytesIO(b'some text for a\n'))
435
self.assertTrue(t.has('a'))
426
436
self.check_transport_contents('some text for a\n', t, 'a')
427
437
# Put also replaces contents
428
t.put_file_non_atomic('a', StringIO('new\ncontents for\na\n'))
438
t.put_file_non_atomic('a', BytesIO(b'new\ncontents for\na\n'))
429
439
self.check_transport_contents('new\ncontents for\na\n', t, 'a')
431
441
# Make sure we can create another file
432
t.put_file_non_atomic('d', StringIO('contents for\nd\n'))
442
t.put_file_non_atomic('d', BytesIO(b'contents for\nd\n'))
433
443
# And overwrite 'a' with empty contents
434
t.put_file_non_atomic('a', StringIO(''))
444
t.put_file_non_atomic('a', BytesIO(b''))
435
445
self.check_transport_contents('contents for\nd\n', t, 'd')
436
446
self.check_transport_contents('', t, 'a')
438
448
self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'no/such/path',
439
StringIO('contents\n'))
449
BytesIO(b'contents\n'))
440
450
# Now test the create_parent flag
441
451
self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'dir/a',
442
StringIO('contents\n'))
443
self.failIf(t.has('dir/a'))
444
t.put_file_non_atomic('dir/a', StringIO('contents for dir/a\n'),
452
BytesIO(b'contents\n'))
453
self.assertFalse(t.has('dir/a'))
454
t.put_file_non_atomic('dir/a', BytesIO(b'contents for dir/a\n'),
445
455
create_parent_dir=True)
446
456
self.check_transport_contents('contents for dir/a\n', t, 'dir/a')
448
458
# But we still get NoSuchFile if we can't make the parent dir
449
459
self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'not/there/a',
450
StringIO('contents\n'),
460
BytesIO(b'contents\n'),
451
461
create_parent_dir=True)
453
463
def test_put_file_permissions(self):
459
469
if not t._can_roundtrip_unix_modebits():
460
470
# Can't roundtrip, so no need to run this test
462
t.put_file('mode644', StringIO('test text\n'), mode=0644)
463
self.assertTransportMode(t, 'mode644', 0644)
464
t.put_file('mode666', StringIO('test text\n'), mode=0666)
465
self.assertTransportMode(t, 'mode666', 0666)
466
t.put_file('mode600', StringIO('test text\n'), mode=0600)
467
self.assertTransportMode(t, 'mode600', 0600)
472
t.put_file('mode644', BytesIO(b'test text\n'), mode=0o644)
473
self.assertTransportMode(t, 'mode644', 0o644)
474
t.put_file('mode666', BytesIO(b'test text\n'), mode=0o666)
475
self.assertTransportMode(t, 'mode666', 0o666)
476
t.put_file('mode600', BytesIO(b'test text\n'), mode=0o600)
477
self.assertTransportMode(t, 'mode600', 0o600)
468
478
# Yes, you can put a file such that it becomes readonly
469
t.put_file('mode400', StringIO('test text\n'), mode=0400)
470
self.assertTransportMode(t, 'mode400', 0400)
479
t.put_file('mode400', BytesIO(b'test text\n'), mode=0o400)
480
self.assertTransportMode(t, 'mode400', 0o400)
471
481
# The default permissions should be based on the current umask
472
482
umask = osutils.get_umask()
473
t.put_file('nomode', StringIO('test text\n'), mode=None)
474
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
483
t.put_file('nomode', BytesIO(b'test text\n'), mode=None)
484
self.assertTransportMode(t, 'nomode', 0o666 & ~umask)
476
486
def test_put_file_non_atomic_permissions(self):
477
487
t = self.get_transport()
481
491
if not t._can_roundtrip_unix_modebits():
482
492
# Can't roundtrip, so no need to run this test
484
t.put_file_non_atomic('mode644', StringIO('test text\n'), mode=0644)
485
self.assertTransportMode(t, 'mode644', 0644)
486
t.put_file_non_atomic('mode666', StringIO('test text\n'), mode=0666)
487
self.assertTransportMode(t, 'mode666', 0666)
488
t.put_file_non_atomic('mode600', StringIO('test text\n'), mode=0600)
489
self.assertTransportMode(t, 'mode600', 0600)
494
t.put_file_non_atomic('mode644', BytesIO(b'test text\n'), mode=0o644)
495
self.assertTransportMode(t, 'mode644', 0o644)
496
t.put_file_non_atomic('mode666', BytesIO(b'test text\n'), mode=0o666)
497
self.assertTransportMode(t, 'mode666', 0o666)
498
t.put_file_non_atomic('mode600', BytesIO(b'test text\n'), mode=0o600)
499
self.assertTransportMode(t, 'mode600', 0o600)
490
500
# Yes, you can put_file_non_atomic a file such that it becomes readonly
491
t.put_file_non_atomic('mode400', StringIO('test text\n'), mode=0400)
492
self.assertTransportMode(t, 'mode400', 0400)
501
t.put_file_non_atomic('mode400', BytesIO(b'test text\n'), mode=0o400)
502
self.assertTransportMode(t, 'mode400', 0o400)
494
504
# The default permissions should be based on the current umask
495
505
umask = osutils.get_umask()
496
t.put_file_non_atomic('nomode', StringIO('test text\n'), mode=None)
497
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
506
t.put_file_non_atomic('nomode', BytesIO(b'test text\n'), mode=None)
507
self.assertTransportMode(t, 'nomode', 0o666 & ~umask)
499
509
# We should also be able to set the mode for a parent directory
500
510
# when it is created
502
t.put_file_non_atomic('dir700/mode664', sio, mode=0664,
503
dir_mode=0700, create_parent_dir=True)
504
self.assertTransportMode(t, 'dir700', 0700)
505
t.put_file_non_atomic('dir770/mode664', sio, mode=0664,
506
dir_mode=0770, create_parent_dir=True)
507
self.assertTransportMode(t, 'dir770', 0770)
508
t.put_file_non_atomic('dir777/mode664', sio, mode=0664,
509
dir_mode=0777, create_parent_dir=True)
510
self.assertTransportMode(t, 'dir777', 0777)
512
t.put_file_non_atomic('dir700/mode664', sio, mode=0o664,
513
dir_mode=0o700, create_parent_dir=True)
514
self.assertTransportMode(t, 'dir700', 0o700)
515
t.put_file_non_atomic('dir770/mode664', sio, mode=0o664,
516
dir_mode=0o770, create_parent_dir=True)
517
self.assertTransportMode(t, 'dir770', 0o770)
518
t.put_file_non_atomic('dir777/mode664', sio, mode=0o664,
519
dir_mode=0o777, create_parent_dir=True)
520
self.assertTransportMode(t, 'dir777', 0o777)
512
522
def test_put_bytes_unicode(self):
513
# Expect put_bytes to raise AssertionError or UnicodeEncodeError if
514
# given unicode "bytes". UnicodeEncodeError doesn't really make sense
515
# (we don't want to encode unicode here at all, callers should be
516
# strictly passing bytes to put_bytes), but we allow it for backwards
517
# compatibility. At some point we should use a specific exception.
518
# See https://bugs.launchpad.net/bzr/+bug/106898.
519
523
t = self.get_transport()
520
524
if t.is_readonly():
522
526
unicode_string = u'\u1234'
524
(AssertionError, UnicodeEncodeError),
525
t.put_bytes, 'foo', unicode_string)
527
def test_put_file_unicode(self):
528
# Like put_bytes, except with a StringIO.StringIO of a unicode string.
529
# This situation can happen (and has) if code is careless about the type
530
# of "string" they initialise/write to a StringIO with. We cannot use
531
# cStringIO, because it never returns unicode from read.
532
# Like put_bytes, UnicodeEncodeError isn't quite the right exception to
533
# raise, but we raise it for hysterical raisins.
534
t = self.get_transport()
537
unicode_file = pyStringIO(u'\u1234')
538
self.assertRaises(UnicodeEncodeError, t.put_file, 'foo', unicode_file)
527
self.assertRaises(TypeError, t.put_bytes, 'foo', unicode_string)
540
529
def test_mkdir(self):
541
530
t = self.get_transport()
591
580
# no sense testing on this transport
593
582
# Test mkdir with a mode
594
t.mkdir('dmode755', mode=0755)
595
self.assertTransportMode(t, 'dmode755', 0755)
596
t.mkdir('dmode555', mode=0555)
597
self.assertTransportMode(t, 'dmode555', 0555)
598
t.mkdir('dmode777', mode=0777)
599
self.assertTransportMode(t, 'dmode777', 0777)
600
t.mkdir('dmode700', mode=0700)
601
self.assertTransportMode(t, 'dmode700', 0700)
602
t.mkdir_multi(['mdmode755'], mode=0755)
603
self.assertTransportMode(t, 'mdmode755', 0755)
583
t.mkdir('dmode755', mode=0o755)
584
self.assertTransportMode(t, 'dmode755', 0o755)
585
t.mkdir('dmode555', mode=0o555)
586
self.assertTransportMode(t, 'dmode555', 0o555)
587
t.mkdir('dmode777', mode=0o777)
588
self.assertTransportMode(t, 'dmode777', 0o777)
589
t.mkdir('dmode700', mode=0o700)
590
self.assertTransportMode(t, 'dmode700', 0o700)
591
t.mkdir_multi(['mdmode755'], mode=0o755)
592
self.assertTransportMode(t, 'mdmode755', 0o755)
605
594
# Default mode should be based on umask
606
595
umask = osutils.get_umask()
607
596
t.mkdir('dnomode', mode=None)
608
self.assertTransportMode(t, 'dnomode', 0777 & ~umask)
597
self.assertTransportMode(t, 'dnomode', 0o777 & ~umask)
610
599
def test_opening_a_file_stream_creates_file(self):
611
600
t = self.get_transport()
620
609
def test_opening_a_file_stream_can_set_mode(self):
621
610
t = self.get_transport()
622
611
if t.is_readonly():
612
self.assertRaises((TransportNotPossible, NotImplementedError),
613
t.open_write_stream, 'foo')
624
615
if not t._can_roundtrip_unix_modebits():
625
616
# Can't roundtrip, so no need to run this test
627
619
def check_mode(name, mode, expected):
628
620
handle = t.open_write_stream(name, mode=mode)
630
622
self.assertTransportMode(t, name, expected)
631
check_mode('mode644', 0644, 0644)
632
check_mode('mode666', 0666, 0666)
633
check_mode('mode600', 0600, 0600)
623
check_mode('mode644', 0o644, 0o644)
624
check_mode('mode666', 0o666, 0o666)
625
check_mode('mode600', 0o600, 0o600)
634
626
# The default permissions should be based on the current umask
635
check_mode('nomode', None, 0666 & ~osutils.get_umask())
627
check_mode('nomode', None, 0o666 & ~osutils.get_umask())
637
629
def test_copy_to(self):
638
630
# FIXME: test: same server to same server (partly done)
1294
1292
self.build_tree(['a', 'b/', 'b/c'], transport=t1)
1296
self.failUnless(t1.has('a'))
1297
self.failUnless(t1.has('b/c'))
1298
self.failIf(t1.has('c'))
1294
self.assertTrue(t1.has('a'))
1295
self.assertTrue(t1.has('b/c'))
1296
self.assertFalse(t1.has('c'))
1300
1298
t2 = t1.clone('b')
1301
1299
self.assertEqual(t1.base + 'b/', t2.base)
1303
self.failUnless(t2.has('c'))
1304
self.failIf(t2.has('a'))
1301
self.assertTrue(t2.has('c'))
1302
self.assertFalse(t2.has('a'))
1306
1304
t3 = t2.clone('..')
1307
self.failUnless(t3.has('a'))
1308
self.failIf(t3.has('c'))
1305
self.assertTrue(t3.has('a'))
1306
self.assertFalse(t3.has('c'))
1310
self.failIf(t1.has('b/d'))
1311
self.failIf(t2.has('d'))
1312
self.failIf(t3.has('b/d'))
1308
self.assertFalse(t1.has('b/d'))
1309
self.assertFalse(t2.has('d'))
1310
self.assertFalse(t3.has('b/d'))
1314
1312
if t1.is_readonly():
1315
1313
self.build_tree_contents([('b/d', 'newfile\n')])
1317
1315
t2.put_bytes('d', 'newfile\n')
1319
self.failUnless(t1.has('b/d'))
1320
self.failUnless(t2.has('d'))
1321
self.failUnless(t3.has('b/d'))
1317
self.assertTrue(t1.has('b/d'))
1318
self.assertTrue(t2.has('d'))
1319
self.assertTrue(t3.has('b/d'))
1323
1321
def test_clone_to_root(self):
1324
1322
orig_transport = self.get_transport()
1765
1764
# also raise a special error
1766
1765
self.assertListRaises((errors.ShortReadvError, errors.InvalidRange),
1767
1766
transport.readv, 'a', [(12,2)])
1768
def test_no_segment_parameters(self):
1769
"""Segment parameters should be stripped and stored in
1770
transport.segment_parameters."""
1771
transport = self.get_transport("foo")
1772
self.assertEqual({}, transport.get_segment_parameters())
1774
def test_segment_parameters(self):
1775
"""Segment parameters should be stripped and stored in
1776
transport.get_segment_parameters()."""
1777
base_url = self._server.get_url()
1778
parameters = {"key1": "val1", "key2": "val2"}
1779
url = urlutils.join_segment_parameters(base_url, parameters)
1780
transport = _mod_transport.get_transport_from_url(url)
1781
self.assertEqual(parameters, transport.get_segment_parameters())
1783
def test_set_segment_parameters(self):
1784
"""Segment parameters can be set and show up in base."""
1785
transport = self.get_transport("foo")
1786
orig_base = transport.base
1787
transport.set_segment_parameter("arm", "board")
1788
self.assertEqual("%s,arm=board" % orig_base, transport.base)
1789
self.assertEqual({"arm": "board"}, transport.get_segment_parameters())
1790
transport.set_segment_parameter("arm", None)
1791
transport.set_segment_parameter("nonexistant", None)
1792
self.assertEqual({}, transport.get_segment_parameters())
1793
self.assertEqual(orig_base, transport.base)
1795
def test_stat_symlink(self):
1796
# if a transport points directly to a symlink (and supports symlinks
1797
# at all) you can tell this. helps with bug 32669.
1798
t = self.get_transport()
1800
t.symlink('target', 'link')
1801
except TransportNotPossible:
1802
raise TestSkipped("symlinks not supported")
1803
t2 = t.clone('link')
1805
self.assertTrue(stat.S_ISLNK(st.st_mode))
1807
def test_abspath_url_unquote_unreserved(self):
1808
"""URLs from abspath should have unreserved characters unquoted
1810
Need consistent quoting notably for tildes, see lp:842223 for more.
1812
t = self.get_transport()
1813
needlessly_escaped_dir = "%2D%2E%30%39%41%5A%5F%61%7A%7E/"
1814
self.assertEqual(t.base + "-.09AZ_az~",
1815
t.abspath(needlessly_escaped_dir))
1817
def test_clone_url_unquote_unreserved(self):
1818
"""Base URL of a cloned branch needs unreserved characters unquoted
1820
Cloned transports should be prefix comparable for things like the
1821
isolation checking of tests, see lp:842223 for more.
1823
t1 = self.get_transport()
1824
needlessly_escaped_dir = "%2D%2E%30%39%41%5A%5F%61%7A%7E/"
1825
self.build_tree([needlessly_escaped_dir], transport=t1)
1826
t2 = t1.clone(needlessly_escaped_dir)
1827
self.assertEqual(t1.base + "-.09AZ_az~/", t2.base)
1829
def test_hook_post_connection_one(self):
1830
"""Fire post_connect hook after a ConnectedTransport is first used"""
1832
Transport.hooks.install_named_hook("post_connect", log.append, None)
1833
t = self.get_transport()
1834
self.assertEqual([], log)
1835
t.has("non-existant")
1836
if isinstance(t, RemoteTransport):
1837
self.assertEqual([t.get_smart_medium()], log)
1838
elif isinstance(t, ConnectedTransport):
1839
self.assertEqual([t], log)
1841
self.assertEqual([], log)
1843
def test_hook_post_connection_multi(self):
1844
"""Fire post_connect hook once per unshared underlying connection"""
1846
Transport.hooks.install_named_hook("post_connect", log.append, None)
1847
t1 = self.get_transport()
1849
t3 = self.get_transport()
1850
self.assertEqual([], log)
1854
if isinstance(t1, RemoteTransport):
1855
self.assertEqual([t.get_smart_medium() for t in [t1, t3]], log)
1856
elif isinstance(t1, ConnectedTransport):
1857
self.assertEqual([t1, t3], log)
1859
self.assertEqual([], log)