1
# Copyright (C) 2004, 2005 by Canonical Ltd
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.
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.
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
17
"""Tests for Transport implementations.
19
Transport implementations tested here are supplied by
20
TransportTestProviderAdapter.
24
from cStringIO import StringIO
28
from bzrlib.errors import (NoSuchFile, FileExists,
30
TransportNotPossible, ConnectionError)
31
from bzrlib.tests import TestCaseInTempDir, TestSkipped
32
from bzrlib.transport import memory, urlescape
33
import bzrlib.transport
37
"""Append the given text (file-like object) to the supplied filename."""
45
class TestTransportImplementation(TestCaseInTempDir):
46
"""Implementation verification for transports.
48
To verify a transport we need a server factory, which is a callable
49
that accepts no parameters and returns an implementation of
50
bzrlib.transport.Server.
52
That Server is then used to construct transport instances and test
53
the transport via loopback activity.
55
Currently this assumes that the Transport object is connected to the
56
current working directory. So that whatever is done
57
through the transport, should show up in the working
58
directory, and vice-versa. This is a bug, because its possible to have
59
URL schemes which provide access to something that may not be
60
result in storage on the local disk, i.e. due to file system limits, or
61
due to it being a database or some other non-filesystem tool.
63
This also tests to make sure that the functions work with both
64
generators and lists (assuming iter(list) is effectively a generator)
68
super(TestTransportImplementation, self).setUp()
69
self._server = self.transport_server()
73
super(TestTransportImplementation, self).tearDown()
74
self._server.tearDown()
76
def check_transport_contents(self, content, transport, relpath):
77
"""Check that transport.get(relpath).read() == content."""
78
self.assertEqualDiff(content, transport.get(relpath).read())
80
def get_transport(self):
81
"""Return a connected transport to the local directory."""
82
t = bzrlib.transport.get_transport(self._server.get_url())
83
self.failUnless(isinstance(t, self.transport_class),
84
"Got the wrong class from get_transport"
85
"(%r, expected %r)" % (t.__class__,
86
self.transport_class))
89
def assertListRaises(self, excClass, func, *args, **kwargs):
90
"""Fail unless excClass is raised when the iterator from func is used.
92
Many transport functions can return generators this makes sure
93
to wrap them in a list() call to make sure the whole generator
94
is run, and that the proper exception is raised.
97
list(func(*args, **kwargs))
101
if hasattr(excClass,'__name__'): excName = excClass.__name__
102
else: excName = str(excClass)
103
raise self.failureException, "%s not raised" % excName
106
t = self.get_transport()
108
files = ['a', 'b', 'e', 'g', '%']
109
self.build_tree(files, transport=t)
110
self.assertEqual(True, t.has('a'))
111
self.assertEqual(False, t.has('c'))
112
self.assertEqual(True, t.has(urlescape('%')))
113
self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
114
[True, True, False, False, True, False, True, False])
115
self.assertEqual(True, t.has_any(['a', 'b', 'c']))
116
self.assertEqual(False, t.has_any(['c', 'd', 'f', urlescape('%%')]))
117
self.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
118
[True, True, False, False, True, False, True, False])
119
self.assertEqual(False, t.has_any(['c', 'c', 'c']))
120
self.assertEqual(True, t.has_any(['b', 'b', 'b']))
123
t = self.get_transport()
125
files = ['a', 'b', 'e', 'g']
126
contents = ['contents of a\n',
131
self.build_tree(files, transport=t)
132
self.check_transport_contents('contents of a\n', t, 'a')
133
content_f = t.get_multi(files)
134
for content, f in zip(contents, content_f):
135
self.assertEqual(content, f.read())
137
content_f = t.get_multi(iter(files))
138
for content, f in zip(contents, content_f):
139
self.assertEqual(content, f.read())
141
self.assertRaises(NoSuchFile, t.get, 'c')
142
self.assertListRaises(NoSuchFile, t.get_multi, ['a', 'b', 'c'])
143
self.assertListRaises(NoSuchFile, t.get_multi, iter(['a', 'b', 'c']))
146
t = self.get_transport()
149
self.assertRaises(TransportNotPossible,
150
t.put, 'a', 'some text for a\n')
153
t.put('a', StringIO('some text for a\n'))
154
self.failUnless(t.has('a'))
155
self.check_transport_contents('some text for a\n', t, 'a')
156
# Make sure 'has' is updated
157
self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
158
[True, False, False, False, False])
159
# Put also replaces contents
160
self.assertEqual(t.put_multi([('a', StringIO('new\ncontents for\na\n')),
161
('d', StringIO('contents\nfor d\n'))]),
163
self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
164
[True, False, False, True, False])
165
self.check_transport_contents('new\ncontents for\na\n', t, 'a')
166
self.check_transport_contents('contents\nfor d\n', t, 'd')
169
t.put_multi(iter([('a', StringIO('diff\ncontents for\na\n')),
170
('d', StringIO('another contents\nfor d\n'))])),
172
self.check_transport_contents('diff\ncontents for\na\n', t, 'a')
173
self.check_transport_contents('another contents\nfor d\n', t, 'd')
175
self.assertRaises(NoSuchFile,
176
t.put, 'path/doesnt/exist/c', 'contents')
178
def test_put_permissions(self):
179
t = self.get_transport()
183
t.put('mode644', StringIO('test text\n'), mode=0644)
184
self.assertTransportMode(t, 'mode644', 0644)
185
t.put('mode666', StringIO('test text\n'), mode=0666)
186
self.assertTransportMode(t, 'mode666', 0666)
187
t.put('mode600', StringIO('test text\n'), mode=0600)
188
self.assertTransportMode(t, 'mode600', 0600)
189
# Yes, you can put a file such that it becomes readonly
190
t.put('mode400', StringIO('test text\n'), mode=0400)
191
self.assertTransportMode(t, 'mode400', 0400)
192
t.put_multi([('mmode644', StringIO('text\n'))], mode=0644)
193
self.assertTransportMode(t, 'mmode644', 0644)
195
def test_mkdir(self):
196
t = self.get_transport()
199
# cannot mkdir on readonly transports. We're not testing for
200
# cache coherency because cache behaviour is not currently
201
# defined for the transport interface.
202
self.assertRaises(TransportNotPossible, t.mkdir, '.')
203
self.assertRaises(TransportNotPossible, t.mkdir, 'new_dir')
204
self.assertRaises(TransportNotPossible, t.mkdir_multi, ['new_dir'])
205
self.assertRaises(TransportNotPossible, t.mkdir, 'path/doesnt/exist')
209
self.assertEqual(t.has('dir_a'), True)
210
self.assertEqual(t.has('dir_b'), False)
213
self.assertEqual(t.has('dir_b'), True)
215
t.mkdir_multi(['dir_c', 'dir_d'])
217
t.mkdir_multi(iter(['dir_e', 'dir_f']))
218
self.assertEqual(list(t.has_multi(
219
['dir_a', 'dir_b', 'dir_c', 'dir_q',
220
'dir_d', 'dir_e', 'dir_f', 'dir_b'])),
221
[True, True, True, False,
222
True, True, True, True])
224
# we were testing that a local mkdir followed by a transport
225
# mkdir failed thusly, but given that we * in one process * do not
226
# concurrently fiddle with disk dirs and then use transport to do
227
# things, the win here seems marginal compared to the constraint on
228
# the interface. RBC 20051227
230
self.assertRaises(FileExists, t.mkdir, 'dir_g')
232
# Test get/put in sub-directories
234
t.put_multi([('dir_a/a', StringIO('contents of dir_a/a')),
235
('dir_b/b', StringIO('contents of dir_b/b'))])
237
self.check_transport_contents('contents of dir_a/a', t, 'dir_a/a')
238
self.check_transport_contents('contents of dir_b/b', t, 'dir_b/b')
240
# mkdir of a dir with an absent parent
241
self.assertRaises(NoSuchFile, t.mkdir, 'missing/dir')
243
def test_mkdir_permissions(self):
244
t = self.get_transport()
247
# Test mkdir with a mode
248
t.mkdir('dmode755', mode=0755)
249
self.assertTransportMode(t, 'dmode755', 0755)
250
t.mkdir('dmode555', mode=0555)
251
self.assertTransportMode(t, 'dmode555', 0555)
252
t.mkdir('dmode777', mode=0777)
253
self.assertTransportMode(t, 'dmode777', 0777)
254
t.mkdir('dmode700', mode=0700)
255
self.assertTransportMode(t, 'dmode700', 0700)
256
# TODO: jam 20051215 test mkdir_multi with a mode
257
t.mkdir_multi(['mdmode755'], mode=0755)
258
self.assertTransportMode(t, 'mdmode755', 0755)
260
def test_copy_to(self):
261
# FIXME: test: same server to same server (partly done)
262
# same protocol two servers
263
# and different protocols (done for now except for MemoryTransport.
265
from bzrlib.transport.memory import MemoryTransport
267
def simple_copy_files(transport_from, transport_to):
268
files = ['a', 'b', 'c', 'd']
269
self.build_tree(files, transport=transport_from)
270
transport_from.copy_to(files, transport_to)
272
self.check_transport_contents(transport_to.get(f).read(),
275
t = self.get_transport()
276
temp_transport = MemoryTransport('memory:/')
277
simple_copy_files(t, temp_transport)
278
if not t.is_readonly():
279
t.mkdir('copy_to_simple')
280
t2 = t.clone('copy_to_simple')
281
simple_copy_files(t, t2)
284
# Test that copying into a missing directory raises
287
self.build_tree(['e/', 'e/f'])
290
t.put('e/f', StringIO('contents of e'))
291
self.assertRaises(NoSuchFile, t.copy_to, ['e/f'], temp_transport)
292
temp_transport.mkdir('e')
293
t.copy_to(['e/f'], temp_transport)
296
temp_transport = MemoryTransport('memory:/')
298
files = ['a', 'b', 'c', 'd']
299
t.copy_to(iter(files), temp_transport)
301
self.check_transport_contents(temp_transport.get(f).read(),
305
for mode in (0666, 0644, 0600, 0400):
306
temp_transport = MemoryTransport("memory:/")
307
t.copy_to(files, temp_transport, mode=mode)
309
self.assertTransportMode(temp_transport, f, mode)
311
def test_append(self):
312
t = self.get_transport()
315
open('a', 'wb').write('diff\ncontents for\na\n')
316
open('b', 'wb').write('contents\nfor b\n')
319
('a', StringIO('diff\ncontents for\na\n')),
320
('b', StringIO('contents\nfor b\n'))
324
self.assertRaises(TransportNotPossible,
325
t.append, 'a', 'add\nsome\nmore\ncontents\n')
326
_append('a', StringIO('add\nsome\nmore\ncontents\n'))
328
t.append('a', StringIO('add\nsome\nmore\ncontents\n'))
330
self.check_transport_contents(
331
'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
335
self.assertRaises(TransportNotPossible,
337
[('a', 'and\nthen\nsome\nmore\n'),
338
('b', 'some\nmore\nfor\nb\n')])
339
_append('a', StringIO('and\nthen\nsome\nmore\n'))
340
_append('b', StringIO('some\nmore\nfor\nb\n'))
342
t.append_multi([('a', StringIO('and\nthen\nsome\nmore\n')),
343
('b', StringIO('some\nmore\nfor\nb\n'))])
344
self.check_transport_contents(
345
'diff\ncontents for\na\n'
346
'add\nsome\nmore\ncontents\n'
347
'and\nthen\nsome\nmore\n',
349
self.check_transport_contents(
351
'some\nmore\nfor\nb\n',
355
_append('a', StringIO('a little bit more\n'))
356
_append('b', StringIO('from an iterator\n'))
358
t.append_multi(iter([('a', StringIO('a little bit more\n')),
359
('b', StringIO('from an iterator\n'))]))
360
self.check_transport_contents(
361
'diff\ncontents for\na\n'
362
'add\nsome\nmore\ncontents\n'
363
'and\nthen\nsome\nmore\n'
364
'a little bit more\n',
366
self.check_transport_contents(
368
'some\nmore\nfor\nb\n'
369
'from an iterator\n',
373
_append('c', StringIO('some text\nfor a missing file\n'))
374
_append('a', StringIO('some text in a\n'))
375
_append('d', StringIO('missing file r\n'))
377
t.append('c', StringIO('some text\nfor a missing file\n'))
378
t.append_multi([('a', StringIO('some text in a\n')),
379
('d', StringIO('missing file r\n'))])
380
self.check_transport_contents(
381
'diff\ncontents for\na\n'
382
'add\nsome\nmore\ncontents\n'
383
'and\nthen\nsome\nmore\n'
384
'a little bit more\n'
387
self.check_transport_contents('some text\nfor a missing file\n',
389
self.check_transport_contents('missing file r\n', t, 'd')
391
# a file with no parent should fail..
392
if not t.is_readonly():
393
self.assertRaises(NoSuchFile,
394
t.append, 'missing/path',
397
def test_append_file(self):
398
t = self.get_transport()
401
('f1', StringIO('this is a string\nand some more stuff\n')),
402
('f2', StringIO('here is some text\nand a bit more\n')),
403
('f3', StringIO('some text for the\nthird file created\n')),
404
('f4', StringIO('this is a string\nand some more stuff\n')),
405
('f5', StringIO('here is some text\nand a bit more\n')),
406
('f6', StringIO('some text for the\nthird file created\n'))
410
for f, val in contents:
411
open(f, 'wb').write(val.read())
413
t.put_multi(contents)
415
a1 = StringIO('appending to\none\n')
423
self.check_transport_contents(
424
'this is a string\nand some more stuff\n'
425
'appending to\none\n',
428
a2 = StringIO('adding more\ntext to two\n')
429
a3 = StringIO('some garbage\nto put in three\n')
435
t.append_multi([('f2', a2), ('f3', a3)])
439
self.check_transport_contents(
440
'here is some text\nand a bit more\n'
441
'adding more\ntext to two\n',
443
self.check_transport_contents(
444
'some text for the\nthird file created\n'
445
'some garbage\nto put in three\n',
448
# Test that an actual file object can be used with put
457
self.check_transport_contents(
458
'this is a string\nand some more stuff\n'
459
'this is a string\nand some more stuff\n'
460
'appending to\none\n',
469
t.append_multi([('f5', a5), ('f6', a6)])
473
self.check_transport_contents(
474
'here is some text\nand a bit more\n'
475
'here is some text\nand a bit more\n'
476
'adding more\ntext to two\n',
478
self.check_transport_contents(
479
'some text for the\nthird file created\n'
480
'some text for the\nthird file created\n'
481
'some garbage\nto put in three\n',
493
t.append_multi([('a', a6), ('d', a7)])
495
self.check_transport_contents(t.get('f2').read(), t, 'c')
496
self.check_transport_contents(t.get('f3').read(), t, 'd')
498
def test_delete(self):
499
# TODO: Test Transport.delete
500
t = self.get_transport()
502
# Not much to do with a readonly transport
504
self.assertRaises(TransportNotPossible, t.delete, 'missing')
507
t.put('a', StringIO('a little bit of text\n'))
508
self.failUnless(t.has('a'))
510
self.failIf(t.has('a'))
512
self.assertRaises(NoSuchFile, t.delete, 'a')
514
t.put('a', StringIO('a text\n'))
515
t.put('b', StringIO('b text\n'))
516
t.put('c', StringIO('c text\n'))
517
self.assertEqual([True, True, True],
518
list(t.has_multi(['a', 'b', 'c'])))
519
t.delete_multi(['a', 'c'])
520
self.assertEqual([False, True, False],
521
list(t.has_multi(['a', 'b', 'c'])))
522
self.failIf(t.has('a'))
523
self.failUnless(t.has('b'))
524
self.failIf(t.has('c'))
526
self.assertRaises(NoSuchFile,
527
t.delete_multi, ['a', 'b', 'c'])
529
self.assertRaises(NoSuchFile,
530
t.delete_multi, iter(['a', 'b', 'c']))
532
t.put('a', StringIO('another a text\n'))
533
t.put('c', StringIO('another c text\n'))
534
t.delete_multi(iter(['a', 'b', 'c']))
536
# We should have deleted everything
537
# SftpServer creates control files in the
538
# working directory, so we can just do a
540
# self.assertEqual([], os.listdir('.'))
542
def test_rmdir(self):
543
t = self.get_transport()
544
# Not much to do with a readonly transport
546
self.assertRaises(TransportNotPossible, t.rmdir, 'missing')
551
self.assertRaises(NoSuchFile, t.stat, 'adir/bdir')
553
self.assertRaises(NoSuchFile, t.stat, 'adir')
555
def test_delete_tree(self):
556
t = self.get_transport()
558
# Not much to do with a readonly transport
560
self.assertRaises(TransportNotPossible, t.delete_tree, 'missing')
563
# and does it like listing ?
566
t.delete_tree('adir')
567
except TransportNotPossible:
568
# ok, this transport does not support delete_tree
571
# did it delete that trivial case?
572
self.assertRaises(NoSuchFile, t.stat, 'adir')
574
self.build_tree(['adir/',
582
t.delete_tree('adir')
583
# adir should be gone now.
584
self.assertRaises(NoSuchFile, t.stat, 'adir')
587
t = self.get_transport()
592
# TODO: I would like to use os.listdir() to
593
# make sure there are no extra files, but SftpServer
594
# creates control files in the working directory
595
# perhaps all of this could be done in a subdirectory
597
t.put('a', StringIO('a first file\n'))
598
self.assertEquals([True, False], list(t.has_multi(['a', 'b'])))
601
self.failUnless(t.has('b'))
602
self.failIf(t.has('a'))
604
self.check_transport_contents('a first file\n', t, 'b')
605
self.assertEquals([False, True], list(t.has_multi(['a', 'b'])))
608
t.put('c', StringIO('c this file\n'))
610
self.failIf(t.has('c'))
611
self.check_transport_contents('c this file\n', t, 'b')
613
# TODO: Try to write a test for atomicity
614
# TODO: Test moving into a non-existant subdirectory
615
# TODO: Test Transport.move_multi
618
t = self.get_transport()
623
t.put('a', StringIO('a file\n'))
625
self.check_transport_contents('a file\n', t, 'b')
627
self.assertRaises(NoSuchFile, t.copy, 'c', 'd')
629
# What should the assert be if you try to copy a
630
# file over a directory?
631
#self.assertRaises(Something, t.copy, 'a', 'c')
632
t.put('d', StringIO('text in d\n'))
634
self.check_transport_contents('text in d\n', t, 'b')
636
# TODO: test copy_multi
638
def test_connection_error(self):
639
"""ConnectionError is raised when connection is impossible"""
641
url = self._server.get_bogus_url()
642
except NotImplementedError:
643
raise TestSkipped("Transport %s has no bogus URL support." %
644
self._server.__class__)
645
t = bzrlib.transport.get_transport(url)
648
except (ConnectionError, NoSuchFile), e:
650
except (Exception), e:
651
self.failIf(True, 'Wrong exception thrown: %s' % e)
653
self.failIf(True, 'Did not get the expected exception.')
656
# TODO: Test stat, just try once, and if it throws, stop testing
657
from stat import S_ISDIR, S_ISREG
659
t = self.get_transport()
663
except TransportNotPossible, e:
664
# This transport cannot stat
667
paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e']
668
sizes = [14, 0, 16, 0, 18]
669
self.build_tree(paths, transport=t)
671
for path, size in zip(paths, sizes):
673
if path.endswith('/'):
674
self.failUnless(S_ISDIR(st.st_mode))
675
# directory sizes are meaningless
677
self.failUnless(S_ISREG(st.st_mode))
678
self.assertEqual(size, st.st_size)
680
remote_stats = list(t.stat_multi(paths))
681
remote_iter_stats = list(t.stat_multi(iter(paths)))
683
self.assertRaises(NoSuchFile, t.stat, 'q')
684
self.assertRaises(NoSuchFile, t.stat, 'b/a')
686
self.assertListRaises(NoSuchFile, t.stat_multi, ['a', 'c', 'd'])
687
self.assertListRaises(NoSuchFile, t.stat_multi, iter(['a', 'c', 'd']))
688
self.build_tree(['subdir/', 'subdir/file'], transport=t)
689
subdir = t.clone('subdir')
690
subdir.stat('./file')
693
def test_list_dir(self):
694
# TODO: Test list_dir, just try once, and if it throws, stop testing
695
t = self.get_transport()
698
self.assertRaises(TransportNotPossible, t.list_dir, '.')
702
l = list(t.list_dir(d))
706
# SftpServer creates control files in the working directory
707
# so lets move down a directory to avoid those.
708
if not t.is_readonly():
714
self.assertEqual([], sorted_list(u'.'))
715
# c2 is precisely one letter longer than c here to test that
716
# suffixing is not confused.
717
if not t.is_readonly():
718
self.build_tree(['a', 'b', 'c/', 'c/d', 'c/e', 'c2/'], transport=t)
720
self.build_tree(['wd/a', 'wd/b', 'wd/c/', 'wd/c/d', 'wd/c/e', 'wd/c2/'])
722
self.assertEqual([u'a', u'b', u'c', u'c2'], sorted_list(u'.'))
723
self.assertEqual([u'd', u'e'], sorted_list(u'c'))
725
if not t.is_readonly():
732
self.assertEqual([u'a', u'c', u'c2'], sorted_list('.'))
733
self.assertEqual([u'e'], sorted_list(u'c'))
735
self.assertListRaises(NoSuchFile, t.list_dir, 'q')
736
self.assertListRaises(NoSuchFile, t.list_dir, 'c/f')
737
self.assertListRaises(NoSuchFile, t.list_dir, 'a')
739
def test_clone(self):
740
# TODO: Test that clone moves up and down the filesystem
741
t1 = self.get_transport()
743
self.build_tree(['a', 'b/', 'b/c'], transport=t1)
745
self.failUnless(t1.has('a'))
746
self.failUnless(t1.has('b/c'))
747
self.failIf(t1.has('c'))
750
self.assertEqual(t1.base + 'b/', t2.base)
752
self.failUnless(t2.has('c'))
753
self.failIf(t2.has('a'))
756
self.failUnless(t3.has('a'))
757
self.failIf(t3.has('c'))
759
self.failIf(t1.has('b/d'))
760
self.failIf(t2.has('d'))
761
self.failIf(t3.has('b/d'))
764
open('b/d', 'wb').write('newfile\n')
766
t2.put('d', StringIO('newfile\n'))
768
self.failUnless(t1.has('b/d'))
769
self.failUnless(t2.has('d'))
770
self.failUnless(t3.has('b/d'))
772
def test_relpath(self):
773
t = self.get_transport()
774
self.assertEqual('', t.relpath(t.base))
776
self.assertEqual('', t.relpath(t.base[:-1]))
777
# subdirs which dont exist should still give relpaths.
778
self.assertEqual('foo', t.relpath(t.base + 'foo'))
779
# trailing slash should be the same.
780
self.assertEqual('foo', t.relpath(t.base + 'foo/'))
782
def test_abspath(self):
783
# smoke test for abspath. Corner cases for backends like unix fs's
784
# that have aliasing problems like symlinks should go in backend
785
# specific test cases.
786
transport = self.get_transport()
787
self.assertEqual(transport.base + 'relpath',
788
transport.abspath('relpath'))
790
def test_iter_files_recursive(self):
791
transport = self.get_transport()
792
if not transport.listable():
793
self.assertRaises(TransportNotPossible,
794
transport.iter_files_recursive)
796
self.build_tree(['isolated/',
802
transport = transport.clone('isolated')
803
paths = set(transport.iter_files_recursive())
804
self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
806
def test_connect_twice_is_same_content(self):
807
# check that our server (whatever it is) is accessable reliably
808
# via get_transport and multiple connections share content.
809
transport = self.get_transport()
810
if transport.is_readonly():
812
transport.put('foo', StringIO('bar'))
813
transport2 = self.get_transport()
814
self.check_transport_contents('bar', transport2, 'foo')
815
# its base should be usable.
816
transport2 = bzrlib.transport.get_transport(transport.base)
817
self.check_transport_contents('bar', transport2, 'foo')
819
# now opening at a relative url should give use a sane result:
820
transport.mkdir('newdir')
821
transport2 = bzrlib.transport.get_transport(transport.base + "newdir")
822
transport2 = transport2.clone('..')
823
self.check_transport_contents('bar', transport2, 'foo')
825
def test_lock_write(self):
826
transport = self.get_transport()
827
if transport.is_readonly():
828
self.assertRaises(TransportNotPossible, transport.lock_write, 'foo')
830
transport.put('lock', StringIO())
831
lock = transport.lock_write('lock')
832
# TODO make this consistent on all platforms:
833
# self.assertRaises(LockError, transport.lock_write, 'lock')
836
def test_lock_read(self):
837
transport = self.get_transport()
838
if transport.is_readonly():
839
file('lock', 'w').close()
841
transport.put('lock', StringIO())
842
lock = transport.lock_read('lock')
843
# TODO make this consistent on all platforms:
844
# self.assertRaises(LockError, transport.lock_read, 'lock')