~bzr-pqm/bzr/bzr.dev

1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
1
# Copyright (C) 2004, 2005, 2006 by Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1530.1.3 by Robert Collins
transport implementations now tested consistently.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1530.1.3 by Robert Collins
transport implementations now tested consistently.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1530.1.3 by Robert Collins
transport implementations now tested consistently.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for Transport implementations.
18
19
Transport implementations tested here are supplied by
20
TransportTestProviderAdapter.
21
"""
22
23
import os
24
from cStringIO import StringIO
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
25
import stat
26
import sys
1530.1.3 by Robert Collins
transport implementations now tested consistently.
27
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
28
from bzrlib import (
29
    osutils,
30
    urlutils,
31
    )
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
32
from bzrlib.errors import (DirectoryNotEmpty, NoSuchFile, FileExists,
1185.85.84 by John Arbash Meinel
[merge] bzr.dev 1573, lots of updates
33
                           LockError, PathError,
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
34
                           TransportNotPossible, ConnectionError,
35
                           InvalidURL)
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
36
from bzrlib.osutils import getcwd
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
37
from bzrlib.tests import TestCaseInTempDir, TestSkipped
1871.1.2 by Robert Collins
Reduce code duplication in transport-parameterised tests.
38
from bzrlib.tests.test_transport import TestTransportImplementation
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
39
from bzrlib.transport import memory
1530.1.3 by Robert Collins
transport implementations now tested consistently.
40
import bzrlib.transport
41
42
43
def _append(fn, txt):
44
    """Append the given text (file-like object) to the supplied filename."""
45
    f = open(fn, 'ab')
1530.1.21 by Robert Collins
Review feedback fixes.
46
    try:
47
        f.write(txt.read())
48
    finally:
49
        f.close()
1530.1.3 by Robert Collins
transport implementations now tested consistently.
50
51
1871.1.2 by Robert Collins
Reduce code duplication in transport-parameterised tests.
52
class TransportTests(TestTransportImplementation):
53
1530.1.3 by Robert Collins
transport implementations now tested consistently.
54
    def check_transport_contents(self, content, transport, relpath):
55
        """Check that transport.get(relpath).read() == content."""
1530.1.21 by Robert Collins
Review feedback fixes.
56
        self.assertEqualDiff(content, transport.get(relpath).read())
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
57
1530.1.3 by Robert Collins
transport implementations now tested consistently.
58
    def assertListRaises(self, excClass, func, *args, **kwargs):
1530.1.21 by Robert Collins
Review feedback fixes.
59
        """Fail unless excClass is raised when the iterator from func is used.
60
        
61
        Many transport functions can return generators this makes sure
1530.1.3 by Robert Collins
transport implementations now tested consistently.
62
        to wrap them in a list() call to make sure the whole generator
63
        is run, and that the proper exception is raised.
64
        """
65
        try:
66
            list(func(*args, **kwargs))
67
        except excClass:
68
            return
69
        else:
70
            if hasattr(excClass,'__name__'): excName = excClass.__name__
71
            else: excName = str(excClass)
72
            raise self.failureException, "%s not raised" % excName
73
74
    def test_has(self):
75
        t = self.get_transport()
76
77
        files = ['a', 'b', 'e', 'g', '%']
78
        self.build_tree(files, transport=t)
79
        self.assertEqual(True, t.has('a'))
80
        self.assertEqual(False, t.has('c'))
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
81
        self.assertEqual(True, t.has(urlutils.escape('%')))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
82
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
83
                [True, True, False, False, True, False, True, False])
84
        self.assertEqual(True, t.has_any(['a', 'b', 'c']))
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
85
        self.assertEqual(False, t.has_any(['c', 'd', 'f', urlutils.escape('%%')]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
86
        self.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
87
                [True, True, False, False, True, False, True, False])
88
        self.assertEqual(False, t.has_any(['c', 'c', 'c']))
89
        self.assertEqual(True, t.has_any(['b', 'b', 'b']))
90
91
    def test_get(self):
92
        t = self.get_transport()
93
94
        files = ['a', 'b', 'e', 'g']
95
        contents = ['contents of a\n',
96
                    'contents of b\n',
97
                    'contents of e\n',
98
                    'contents of g\n',
99
                    ]
1551.2.39 by abentley
Fix line endings in tests
100
        self.build_tree(files, transport=t, line_endings='binary')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
101
        self.check_transport_contents('contents of a\n', t, 'a')
102
        content_f = t.get_multi(files)
103
        for content, f in zip(contents, content_f):
104
            self.assertEqual(content, f.read())
105
106
        content_f = t.get_multi(iter(files))
107
        for content, f in zip(contents, content_f):
108
            self.assertEqual(content, f.read())
109
110
        self.assertRaises(NoSuchFile, t.get, 'c')
111
        self.assertListRaises(NoSuchFile, t.get_multi, ['a', 'b', 'c'])
112
        self.assertListRaises(NoSuchFile, t.get_multi, iter(['a', 'b', 'c']))
113
114
    def test_put(self):
115
        t = self.get_transport()
116
117
        if t.is_readonly():
118
            self.assertRaises(TransportNotPossible,
119
                    t.put, 'a', 'some text for a\n')
120
            return
121
122
        t.put('a', StringIO('some text for a\n'))
123
        self.failUnless(t.has('a'))
124
        self.check_transport_contents('some text for a\n', t, 'a')
125
        # Make sure 'has' is updated
126
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
127
                [True, False, False, False, False])
128
        # Put also replaces contents
129
        self.assertEqual(t.put_multi([('a', StringIO('new\ncontents for\na\n')),
130
                                      ('d', StringIO('contents\nfor d\n'))]),
131
                         2)
132
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
133
                [True, False, False, True, False])
134
        self.check_transport_contents('new\ncontents for\na\n', t, 'a')
135
        self.check_transport_contents('contents\nfor d\n', t, 'd')
136
137
        self.assertEqual(
138
            t.put_multi(iter([('a', StringIO('diff\ncontents for\na\n')),
139
                              ('d', StringIO('another contents\nfor d\n'))])),
140
                        2)
141
        self.check_transport_contents('diff\ncontents for\na\n', t, 'a')
142
        self.check_transport_contents('another contents\nfor d\n', t, 'd')
143
144
        self.assertRaises(NoSuchFile,
1960.2.1 by vila
Enable writable http transports.
145
                          t.put, 'path/doesnt/exist/c', StringIO('contents'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
146
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
147
    def test_put_permissions(self):
148
        t = self.get_transport()
149
150
        if t.is_readonly():
151
            return
1711.4.32 by John Arbash Meinel
Skip permission tests on win32 no modebits
152
        if not t._can_roundtrip_unix_modebits():
153
            # Can't roundtrip, so no need to run this test
154
            return
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
155
        t.put('mode644', StringIO('test text\n'), mode=0644)
1530.1.21 by Robert Collins
Review feedback fixes.
156
        self.assertTransportMode(t, 'mode644', 0644)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
157
        t.put('mode666', StringIO('test text\n'), mode=0666)
1530.1.21 by Robert Collins
Review feedback fixes.
158
        self.assertTransportMode(t, 'mode666', 0666)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
159
        t.put('mode600', StringIO('test text\n'), mode=0600)
1530.1.21 by Robert Collins
Review feedback fixes.
160
        self.assertTransportMode(t, 'mode600', 0600)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
161
        # Yes, you can put a file such that it becomes readonly
162
        t.put('mode400', StringIO('test text\n'), mode=0400)
1530.1.21 by Robert Collins
Review feedback fixes.
163
        self.assertTransportMode(t, 'mode400', 0400)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
164
        t.put_multi([('mmode644', StringIO('text\n'))], mode=0644)
1530.1.21 by Robert Collins
Review feedback fixes.
165
        self.assertTransportMode(t, 'mmode644', 0644)
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
166
167
        # The default permissions should be based on the current umask
168
        umask = osutils.get_umask()
169
        t.put('nomode', StringIO('test text\n'), mode=None)
170
        self.assertTransportMode(t, 'nomode', 0666 & ~umask)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
171
        
1530.1.3 by Robert Collins
transport implementations now tested consistently.
172
    def test_mkdir(self):
173
        t = self.get_transport()
174
175
        if t.is_readonly():
176
            # cannot mkdir on readonly transports. We're not testing for 
177
            # cache coherency because cache behaviour is not currently
178
            # defined for the transport interface.
179
            self.assertRaises(TransportNotPossible, t.mkdir, '.')
180
            self.assertRaises(TransportNotPossible, t.mkdir, 'new_dir')
181
            self.assertRaises(TransportNotPossible, t.mkdir_multi, ['new_dir'])
182
            self.assertRaises(TransportNotPossible, t.mkdir, 'path/doesnt/exist')
183
            return
184
        # Test mkdir
185
        t.mkdir('dir_a')
186
        self.assertEqual(t.has('dir_a'), True)
187
        self.assertEqual(t.has('dir_b'), False)
188
189
        t.mkdir('dir_b')
190
        self.assertEqual(t.has('dir_b'), True)
191
192
        t.mkdir_multi(['dir_c', 'dir_d'])
193
194
        t.mkdir_multi(iter(['dir_e', 'dir_f']))
195
        self.assertEqual(list(t.has_multi(
196
            ['dir_a', 'dir_b', 'dir_c', 'dir_q',
197
             'dir_d', 'dir_e', 'dir_f', 'dir_b'])),
198
            [True, True, True, False,
199
             True, True, True, True])
200
201
        # we were testing that a local mkdir followed by a transport
202
        # mkdir failed thusly, but given that we * in one process * do not
203
        # concurrently fiddle with disk dirs and then use transport to do 
204
        # things, the win here seems marginal compared to the constraint on
205
        # the interface. RBC 20051227
206
        t.mkdir('dir_g')
207
        self.assertRaises(FileExists, t.mkdir, 'dir_g')
208
209
        # Test get/put in sub-directories
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
210
        self.assertEqual(2, 
1530.1.3 by Robert Collins
transport implementations now tested consistently.
211
            t.put_multi([('dir_a/a', StringIO('contents of dir_a/a')),
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
212
                         ('dir_b/b', StringIO('contents of dir_b/b'))]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
213
        self.check_transport_contents('contents of dir_a/a', t, 'dir_a/a')
214
        self.check_transport_contents('contents of dir_b/b', t, 'dir_b/b')
215
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
216
        # mkdir of a dir with an absent parent
217
        self.assertRaises(NoSuchFile, t.mkdir, 'missing/dir')
218
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
219
    def test_mkdir_permissions(self):
220
        t = self.get_transport()
221
        if t.is_readonly():
222
            return
1608.2.7 by Martin Pool
Rename supports_unix_modebits to _can_roundtrip_unix_modebits for clarity
223
        if not t._can_roundtrip_unix_modebits():
1608.2.5 by Martin Pool
Add Transport.supports_unix_modebits, so tests can
224
            # no sense testing on this transport
225
            return
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
226
        # Test mkdir with a mode
227
        t.mkdir('dmode755', mode=0755)
1530.1.21 by Robert Collins
Review feedback fixes.
228
        self.assertTransportMode(t, 'dmode755', 0755)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
229
        t.mkdir('dmode555', mode=0555)
1530.1.21 by Robert Collins
Review feedback fixes.
230
        self.assertTransportMode(t, 'dmode555', 0555)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
231
        t.mkdir('dmode777', mode=0777)
1530.1.21 by Robert Collins
Review feedback fixes.
232
        self.assertTransportMode(t, 'dmode777', 0777)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
233
        t.mkdir('dmode700', mode=0700)
1530.1.21 by Robert Collins
Review feedback fixes.
234
        self.assertTransportMode(t, 'dmode700', 0700)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
235
        t.mkdir_multi(['mdmode755'], mode=0755)
1530.1.21 by Robert Collins
Review feedback fixes.
236
        self.assertTransportMode(t, 'mdmode755', 0755)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
237
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
238
        # Default mode should be based on umask
239
        umask = osutils.get_umask()
240
        t.mkdir('dnomode', mode=None)
241
        self.assertTransportMode(t, 'dnomode', 0777 & ~umask)
242
1530.1.3 by Robert Collins
transport implementations now tested consistently.
243
    def test_copy_to(self):
1534.4.21 by Robert Collins
Extend the copy_to tests to smoke test server-to-same-server copies to catch optimised code paths, and fix sftps optimised code path by removing dead code.
244
        # FIXME: test:   same server to same server (partly done)
245
        # same protocol two servers
246
        # and    different protocols (done for now except for MemoryTransport.
247
        # - RBC 20060122
1530.1.3 by Robert Collins
transport implementations now tested consistently.
248
        from bzrlib.transport.memory import MemoryTransport
1534.4.21 by Robert Collins
Extend the copy_to tests to smoke test server-to-same-server copies to catch optimised code paths, and fix sftps optimised code path by removing dead code.
249
250
        def simple_copy_files(transport_from, transport_to):
251
            files = ['a', 'b', 'c', 'd']
252
            self.build_tree(files, transport=transport_from)
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
253
            self.assertEqual(4, transport_from.copy_to(files, transport_to))
1534.4.21 by Robert Collins
Extend the copy_to tests to smoke test server-to-same-server copies to catch optimised code paths, and fix sftps optimised code path by removing dead code.
254
            for f in files:
255
                self.check_transport_contents(transport_to.get(f).read(),
256
                                              transport_from, f)
257
1530.1.3 by Robert Collins
transport implementations now tested consistently.
258
        t = self.get_transport()
1685.1.42 by John Arbash Meinel
A couple more fixes to make sure memory:/// works correctly.
259
        temp_transport = MemoryTransport('memory:///')
1534.4.21 by Robert Collins
Extend the copy_to tests to smoke test server-to-same-server copies to catch optimised code paths, and fix sftps optimised code path by removing dead code.
260
        simple_copy_files(t, temp_transport)
261
        if not t.is_readonly():
262
            t.mkdir('copy_to_simple')
263
            t2 = t.clone('copy_to_simple')
264
            simple_copy_files(t, t2)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
265
266
267
        # Test that copying into a missing directory raises
268
        # NoSuchFile
269
        if t.is_readonly():
1530.1.21 by Robert Collins
Review feedback fixes.
270
            self.build_tree(['e/', 'e/f'])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
271
        else:
272
            t.mkdir('e')
273
            t.put('e/f', StringIO('contents of e'))
274
        self.assertRaises(NoSuchFile, t.copy_to, ['e/f'], temp_transport)
275
        temp_transport.mkdir('e')
276
        t.copy_to(['e/f'], temp_transport)
277
278
        del temp_transport
1685.1.42 by John Arbash Meinel
A couple more fixes to make sure memory:/// works correctly.
279
        temp_transport = MemoryTransport('memory:///')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
280
281
        files = ['a', 'b', 'c', 'd']
282
        t.copy_to(iter(files), temp_transport)
283
        for f in files:
284
            self.check_transport_contents(temp_transport.get(f).read(),
285
                                          t, f)
286
        del temp_transport
287
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
288
        for mode in (0666, 0644, 0600, 0400):
1685.1.42 by John Arbash Meinel
A couple more fixes to make sure memory:/// works correctly.
289
            temp_transport = MemoryTransport("memory:///")
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
290
            t.copy_to(files, temp_transport, mode=mode)
291
            for f in files:
1530.1.21 by Robert Collins
Review feedback fixes.
292
                self.assertTransportMode(temp_transport, f, mode)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
293
1530.1.3 by Robert Collins
transport implementations now tested consistently.
294
    def test_append(self):
295
        t = self.get_transport()
296
297
        if t.is_readonly():
298
            open('a', 'wb').write('diff\ncontents for\na\n')
299
            open('b', 'wb').write('contents\nfor b\n')
300
        else:
301
            t.put_multi([
302
                    ('a', StringIO('diff\ncontents for\na\n')),
303
                    ('b', StringIO('contents\nfor b\n'))
304
                    ])
305
306
        if t.is_readonly():
307
            self.assertRaises(TransportNotPossible,
308
                    t.append, 'a', 'add\nsome\nmore\ncontents\n')
309
            _append('a', StringIO('add\nsome\nmore\ncontents\n'))
310
        else:
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
311
            self.assertEqual(20,
312
                t.append('a', StringIO('add\nsome\nmore\ncontents\n')))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
313
314
        self.check_transport_contents(
315
            'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
316
            t, 'a')
317
318
        if t.is_readonly():
319
            self.assertRaises(TransportNotPossible,
320
                    t.append_multi,
321
                        [('a', 'and\nthen\nsome\nmore\n'),
322
                         ('b', 'some\nmore\nfor\nb\n')])
323
            _append('a', StringIO('and\nthen\nsome\nmore\n'))
324
            _append('b', StringIO('some\nmore\nfor\nb\n'))
325
        else:
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
326
            self.assertEqual((43, 15), 
327
                t.append_multi([('a', StringIO('and\nthen\nsome\nmore\n')),
328
                                ('b', StringIO('some\nmore\nfor\nb\n'))]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
329
        self.check_transport_contents(
330
            'diff\ncontents for\na\n'
331
            'add\nsome\nmore\ncontents\n'
332
            'and\nthen\nsome\nmore\n',
333
            t, 'a')
334
        self.check_transport_contents(
335
                'contents\nfor b\n'
336
                'some\nmore\nfor\nb\n',
337
                t, 'b')
338
339
        if t.is_readonly():
340
            _append('a', StringIO('a little bit more\n'))
341
            _append('b', StringIO('from an iterator\n'))
342
        else:
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
343
            self.assertEqual((62, 31),
344
                t.append_multi(iter([('a', StringIO('a little bit more\n')),
345
                                     ('b', StringIO('from an iterator\n'))])))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
346
        self.check_transport_contents(
347
            'diff\ncontents for\na\n'
348
            'add\nsome\nmore\ncontents\n'
349
            'and\nthen\nsome\nmore\n'
350
            'a little bit more\n',
351
            t, 'a')
352
        self.check_transport_contents(
353
                'contents\nfor b\n'
354
                'some\nmore\nfor\nb\n'
355
                'from an iterator\n',
356
                t, 'b')
357
358
        if t.is_readonly():
359
            _append('c', StringIO('some text\nfor a missing file\n'))
360
            _append('a', StringIO('some text in a\n'))
361
            _append('d', StringIO('missing file r\n'))
362
        else:
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
363
            self.assertEqual(0,
364
                t.append('c', StringIO('some text\nfor a missing file\n')))
365
            self.assertEqual((80, 0),
366
                t.append_multi([('a', StringIO('some text in a\n')),
367
                                ('d', StringIO('missing file r\n'))]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
368
        self.check_transport_contents(
369
            'diff\ncontents for\na\n'
370
            'add\nsome\nmore\ncontents\n'
371
            'and\nthen\nsome\nmore\n'
372
            'a little bit more\n'
373
            'some text in a\n',
374
            t, 'a')
375
        self.check_transport_contents('some text\nfor a missing file\n',
376
                                      t, 'c')
377
        self.check_transport_contents('missing file r\n', t, 'd')
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
378
        
379
        # a file with no parent should fail..
380
        if not t.is_readonly():
381
            self.assertRaises(NoSuchFile,
382
                              t.append, 'missing/path', 
383
                              StringIO('content'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
384
385
    def test_append_file(self):
386
        t = self.get_transport()
387
388
        contents = [
389
            ('f1', StringIO('this is a string\nand some more stuff\n')),
390
            ('f2', StringIO('here is some text\nand a bit more\n')),
391
            ('f3', StringIO('some text for the\nthird file created\n')),
392
            ('f4', StringIO('this is a string\nand some more stuff\n')),
393
            ('f5', StringIO('here is some text\nand a bit more\n')),
394
            ('f6', StringIO('some text for the\nthird file created\n'))
395
        ]
396
        
397
        if t.is_readonly():
398
            for f, val in contents:
399
                open(f, 'wb').write(val.read())
400
        else:
401
            t.put_multi(contents)
402
403
        a1 = StringIO('appending to\none\n')
404
        if t.is_readonly():
405
            _append('f1', a1)
406
        else:
407
            t.append('f1', a1)
408
409
        del a1
410
411
        self.check_transport_contents(
412
                'this is a string\nand some more stuff\n'
413
                'appending to\none\n',
414
                t, 'f1')
415
416
        a2 = StringIO('adding more\ntext to two\n')
417
        a3 = StringIO('some garbage\nto put in three\n')
418
419
        if t.is_readonly():
420
            _append('f2', a2)
421
            _append('f3', a3)
422
        else:
423
            t.append_multi([('f2', a2), ('f3', a3)])
424
425
        del a2, a3
426
427
        self.check_transport_contents(
428
                'here is some text\nand a bit more\n'
429
                'adding more\ntext to two\n',
430
                t, 'f2')
431
        self.check_transport_contents( 
432
                'some text for the\nthird file created\n'
433
                'some garbage\nto put in three\n',
434
                t, 'f3')
435
436
        # Test that an actual file object can be used with put
437
        a4 = t.get('f1')
438
        if t.is_readonly():
439
            _append('f4', a4)
440
        else:
441
            t.append('f4', a4)
442
443
        del a4
444
445
        self.check_transport_contents(
446
                'this is a string\nand some more stuff\n'
447
                'this is a string\nand some more stuff\n'
448
                'appending to\none\n',
449
                t, 'f4')
450
451
        a5 = t.get('f2')
452
        a6 = t.get('f3')
453
        if t.is_readonly():
454
            _append('f5', a5)
455
            _append('f6', a6)
456
        else:
457
            t.append_multi([('f5', a5), ('f6', a6)])
458
459
        del a5, a6
460
461
        self.check_transport_contents(
462
                'here is some text\nand a bit more\n'
463
                'here is some text\nand a bit more\n'
464
                'adding more\ntext to two\n',
465
                t, 'f5')
466
        self.check_transport_contents(
467
                'some text for the\nthird file created\n'
468
                'some text for the\nthird file created\n'
469
                'some garbage\nto put in three\n',
470
                t, 'f6')
471
472
        a5 = t.get('f2')
473
        a6 = t.get('f2')
474
        a7 = t.get('f3')
475
        if t.is_readonly():
476
            _append('c', a5)
477
            _append('a', a6)
478
            _append('d', a7)
479
        else:
480
            t.append('c', a5)
481
            t.append_multi([('a', a6), ('d', a7)])
482
        del a5, a6, a7
483
        self.check_transport_contents(t.get('f2').read(), t, 'c')
484
        self.check_transport_contents(t.get('f3').read(), t, 'd')
485
1666.1.6 by Robert Collins
Make knit the default format.
486
    def test_append_mode(self):
487
        # check append accepts a mode
488
        t = self.get_transport()
489
        if t.is_readonly():
490
            return
491
        t.append('f', StringIO('f'), mode=None)
492
        
1530.1.3 by Robert Collins
transport implementations now tested consistently.
493
    def test_delete(self):
494
        # TODO: Test Transport.delete
495
        t = self.get_transport()
496
497
        # Not much to do with a readonly transport
498
        if t.is_readonly():
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
499
            self.assertRaises(TransportNotPossible, t.delete, 'missing')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
500
            return
501
502
        t.put('a', StringIO('a little bit of text\n'))
503
        self.failUnless(t.has('a'))
504
        t.delete('a')
505
        self.failIf(t.has('a'))
506
507
        self.assertRaises(NoSuchFile, t.delete, 'a')
508
509
        t.put('a', StringIO('a text\n'))
510
        t.put('b', StringIO('b text\n'))
511
        t.put('c', StringIO('c text\n'))
512
        self.assertEqual([True, True, True],
513
                list(t.has_multi(['a', 'b', 'c'])))
514
        t.delete_multi(['a', 'c'])
515
        self.assertEqual([False, True, False],
516
                list(t.has_multi(['a', 'b', 'c'])))
517
        self.failIf(t.has('a'))
518
        self.failUnless(t.has('b'))
519
        self.failIf(t.has('c'))
520
521
        self.assertRaises(NoSuchFile,
522
                t.delete_multi, ['a', 'b', 'c'])
523
524
        self.assertRaises(NoSuchFile,
525
                t.delete_multi, iter(['a', 'b', 'c']))
526
527
        t.put('a', StringIO('another a text\n'))
528
        t.put('c', StringIO('another c text\n'))
529
        t.delete_multi(iter(['a', 'b', 'c']))
530
531
        # We should have deleted everything
532
        # SftpServer creates control files in the
533
        # working directory, so we can just do a
534
        # plain "listdir".
535
        # self.assertEqual([], os.listdir('.'))
536
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
537
    def test_rmdir(self):
538
        t = self.get_transport()
539
        # Not much to do with a readonly transport
540
        if t.is_readonly():
541
            self.assertRaises(TransportNotPossible, t.rmdir, 'missing')
542
            return
543
        t.mkdir('adir')
544
        t.mkdir('adir/bdir')
545
        t.rmdir('adir/bdir')
1948.3.12 by Vincent LADEUIL
Fix Aaron's third review remarks.
546
        # ftp may not be able to raise NoSuchFile for lack of
547
        # details when failing
548
        self.assertRaises((NoSuchFile, PathError), t.rmdir, 'adir/bdir')
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
549
        t.rmdir('adir')
1948.3.12 by Vincent LADEUIL
Fix Aaron's third review remarks.
550
        self.assertRaises((NoSuchFile, PathError), t.rmdir, 'adir')
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
551
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
552
    def test_rmdir_not_empty(self):
553
        """Deleting a non-empty directory raises an exception
554
        
555
        sftp (and possibly others) don't give us a specific "directory not
556
        empty" exception -- we can just see that the operation failed.
557
        """
558
        t = self.get_transport()
559
        if t.is_readonly():
560
            return
561
        t.mkdir('adir')
562
        t.mkdir('adir/bdir')
563
        self.assertRaises(PathError, t.rmdir, 'adir')
564
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
565
    def test_rename_dir_succeeds(self):
566
        t = self.get_transport()
567
        if t.is_readonly():
568
            raise TestSkipped("transport is readonly")
569
        t.mkdir('adir')
570
        t.mkdir('adir/asubdir')
571
        t.rename('adir', 'bdir')
572
        self.assertTrue(t.has('bdir/asubdir'))
573
        self.assertFalse(t.has('adir'))
574
575
    def test_rename_dir_nonempty(self):
576
        """Attempting to replace a nonemtpy directory should fail"""
577
        t = self.get_transport()
578
        if t.is_readonly():
579
            raise TestSkipped("transport is readonly")
580
        t.mkdir('adir')
581
        t.mkdir('adir/asubdir')
582
        t.mkdir('bdir')
583
        t.mkdir('bdir/bsubdir')
584
        self.assertRaises(PathError, t.rename, 'bdir', 'adir')
585
        # nothing was changed so it should still be as before
586
        self.assertTrue(t.has('bdir/bsubdir'))
587
        self.assertFalse(t.has('adir/bdir'))
588
        self.assertFalse(t.has('adir/bsubdir'))
589
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
590
    def test_delete_tree(self):
591
        t = self.get_transport()
592
593
        # Not much to do with a readonly transport
594
        if t.is_readonly():
595
            self.assertRaises(TransportNotPossible, t.delete_tree, 'missing')
596
            return
597
598
        # and does it like listing ?
599
        t.mkdir('adir')
600
        try:
601
            t.delete_tree('adir')
602
        except TransportNotPossible:
603
            # ok, this transport does not support delete_tree
604
            return
605
        
606
        # did it delete that trivial case?
607
        self.assertRaises(NoSuchFile, t.stat, 'adir')
608
609
        self.build_tree(['adir/',
610
                         'adir/file', 
611
                         'adir/subdir/', 
612
                         'adir/subdir/file', 
613
                         'adir/subdir2/',
614
                         'adir/subdir2/file',
615
                         ], transport=t)
616
617
        t.delete_tree('adir')
618
        # adir should be gone now.
619
        self.assertRaises(NoSuchFile, t.stat, 'adir')
620
1530.1.3 by Robert Collins
transport implementations now tested consistently.
621
    def test_move(self):
622
        t = self.get_transport()
623
624
        if t.is_readonly():
625
            return
626
627
        # TODO: I would like to use os.listdir() to
628
        # make sure there are no extra files, but SftpServer
629
        # creates control files in the working directory
630
        # perhaps all of this could be done in a subdirectory
631
632
        t.put('a', StringIO('a first file\n'))
633
        self.assertEquals([True, False], list(t.has_multi(['a', 'b'])))
634
635
        t.move('a', 'b')
636
        self.failUnless(t.has('b'))
637
        self.failIf(t.has('a'))
638
639
        self.check_transport_contents('a first file\n', t, 'b')
640
        self.assertEquals([False, True], list(t.has_multi(['a', 'b'])))
641
642
        # Overwrite a file
643
        t.put('c', StringIO('c this file\n'))
644
        t.move('c', 'b')
645
        self.failIf(t.has('c'))
646
        self.check_transport_contents('c this file\n', t, 'b')
647
648
        # TODO: Try to write a test for atomicity
649
        # TODO: Test moving into a non-existant subdirectory
650
        # TODO: Test Transport.move_multi
651
652
    def test_copy(self):
653
        t = self.get_transport()
654
655
        if t.is_readonly():
656
            return
657
658
        t.put('a', StringIO('a file\n'))
659
        t.copy('a', 'b')
660
        self.check_transport_contents('a file\n', t, 'b')
661
662
        self.assertRaises(NoSuchFile, t.copy, 'c', 'd')
663
        os.mkdir('c')
664
        # What should the assert be if you try to copy a
665
        # file over a directory?
666
        #self.assertRaises(Something, t.copy, 'a', 'c')
667
        t.put('d', StringIO('text in d\n'))
668
        t.copy('d', 'b')
669
        self.check_transport_contents('text in d\n', t, 'b')
670
671
        # TODO: test copy_multi
672
673
    def test_connection_error(self):
674
        """ConnectionError is raised when connection is impossible"""
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
675
        try:
676
            url = self._server.get_bogus_url()
677
        except NotImplementedError:
678
            raise TestSkipped("Transport %s has no bogus URL support." %
679
                              self._server.__class__)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
680
        try:
1711.2.42 by John Arbash Meinel
enable bogus_url support for SFTP tests
681
            t = bzrlib.transport.get_transport(url)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
682
            t.get('.bzr/branch')
683
        except (ConnectionError, NoSuchFile), e:
684
            pass
685
        except (Exception), e:
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
686
            self.fail('Wrong exception thrown (%s.%s): %s' 
687
                        % (e.__class__.__module__, e.__class__.__name__, e))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
688
        else:
1707.3.11 by John Arbash Meinel
fixing more tests.
689
            self.fail('Did not get the expected ConnectionError or NoSuchFile.')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
690
691
    def test_stat(self):
692
        # TODO: Test stat, just try once, and if it throws, stop testing
693
        from stat import S_ISDIR, S_ISREG
694
695
        t = self.get_transport()
696
697
        try:
698
            st = t.stat('.')
699
        except TransportNotPossible, e:
700
            # This transport cannot stat
701
            return
702
703
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e']
704
        sizes = [14, 0, 16, 0, 18] 
1551.2.39 by abentley
Fix line endings in tests
705
        self.build_tree(paths, transport=t, line_endings='binary')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
706
707
        for path, size in zip(paths, sizes):
708
            st = t.stat(path)
709
            if path.endswith('/'):
710
                self.failUnless(S_ISDIR(st.st_mode))
711
                # directory sizes are meaningless
712
            else:
713
                self.failUnless(S_ISREG(st.st_mode))
714
                self.assertEqual(size, st.st_size)
715
716
        remote_stats = list(t.stat_multi(paths))
717
        remote_iter_stats = list(t.stat_multi(iter(paths)))
718
719
        self.assertRaises(NoSuchFile, t.stat, 'q')
720
        self.assertRaises(NoSuchFile, t.stat, 'b/a')
721
722
        self.assertListRaises(NoSuchFile, t.stat_multi, ['a', 'c', 'd'])
723
        self.assertListRaises(NoSuchFile, t.stat_multi, iter(['a', 'c', 'd']))
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
724
        self.build_tree(['subdir/', 'subdir/file'], transport=t)
725
        subdir = t.clone('subdir')
726
        subdir.stat('./file')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
727
        subdir.stat('.')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
728
729
    def test_list_dir(self):
730
        # TODO: Test list_dir, just try once, and if it throws, stop testing
731
        t = self.get_transport()
732
        
733
        if not t.listable():
734
            self.assertRaises(TransportNotPossible, t.list_dir, '.')
735
            return
736
737
        def sorted_list(d):
738
            l = list(t.list_dir(d))
739
            l.sort()
740
            return l
741
742
        # SftpServer creates control files in the working directory
743
        # so lets move down a directory to avoid those.
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
744
        if not t.is_readonly():
745
            t.mkdir('wd')
746
        else:
747
            os.mkdir('wd')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
748
        t = t.clone('wd')
749
1910.7.1 by Andrew Bennetts
Make sure list_dir always returns url-escaped names.
750
        self.assertEqual([], sorted_list('.'))
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
751
        # c2 is precisely one letter longer than c here to test that
752
        # suffixing is not confused.
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
753
        # a%25b checks that quoting is done consistently across transports
754
        tree_names = ['a', 'a%25b', 'b', 'c/', 'c/d', 'c/e', 'c2/']
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
755
        if not t.is_readonly():
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
756
            self.build_tree(tree_names, transport=t)
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
757
        else:
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
758
            self.build_tree(['wd/' + name for name in tree_names])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
759
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
760
        self.assertEqual(
1959.2.3 by John Arbash Meinel
Remove some unicode string notations
761
            ['a', 'a%2525b', 'b', 'c', 'c2'], sorted_list('.'))
1910.7.1 by Andrew Bennetts
Make sure list_dir always returns url-escaped names.
762
        self.assertEqual(['d', 'e'], sorted_list('c'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
763
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
764
        if not t.is_readonly():
765
            t.delete('c/d')
766
            t.delete('b')
767
        else:
768
            os.unlink('wd/c/d')
769
            os.unlink('wd/b')
770
            
1959.2.3 by John Arbash Meinel
Remove some unicode string notations
771
        self.assertEqual(['a', 'a%2525b', 'c', 'c2'], sorted_list('.'))
1910.7.1 by Andrew Bennetts
Make sure list_dir always returns url-escaped names.
772
        self.assertEqual(['e'], sorted_list('c'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
773
1662.1.12 by Martin Pool
Translate unknown sftp errors to PathError, no NoSuchFile
774
        self.assertListRaises(PathError, t.list_dir, 'q')
775
        self.assertListRaises(PathError, t.list_dir, 'c/f')
776
        self.assertListRaises(PathError, t.list_dir, 'a')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
777
1910.7.1 by Andrew Bennetts
Make sure list_dir always returns url-escaped names.
778
    def test_list_dir_result_is_url_escaped(self):
779
        t = self.get_transport()
780
        if not t.listable():
781
            raise TestSkipped("transport not listable")
782
783
        if not t.is_readonly():
784
            self.build_tree(['a/', 'a/%'], transport=t)
785
        else:
786
            self.build_tree(['a/', 'a/%'])
787
        
1910.7.2 by Andrew Bennetts
Also assert that list_dir returns plain str objects.
788
        names = list(t.list_dir('a'))
789
        self.assertEqual(['%25'], names)
790
        self.assertIsInstance(names[0], str)
1910.7.1 by Andrew Bennetts
Make sure list_dir always returns url-escaped names.
791
1530.1.3 by Robert Collins
transport implementations now tested consistently.
792
    def test_clone(self):
793
        # TODO: Test that clone moves up and down the filesystem
794
        t1 = self.get_transport()
795
796
        self.build_tree(['a', 'b/', 'b/c'], transport=t1)
797
798
        self.failUnless(t1.has('a'))
799
        self.failUnless(t1.has('b/c'))
800
        self.failIf(t1.has('c'))
801
802
        t2 = t1.clone('b')
803
        self.assertEqual(t1.base + 'b/', t2.base)
804
805
        self.failUnless(t2.has('c'))
806
        self.failIf(t2.has('a'))
807
808
        t3 = t2.clone('..')
809
        self.failUnless(t3.has('a'))
810
        self.failIf(t3.has('c'))
811
812
        self.failIf(t1.has('b/d'))
813
        self.failIf(t2.has('d'))
814
        self.failIf(t3.has('b/d'))
815
816
        if t1.is_readonly():
817
            open('b/d', 'wb').write('newfile\n')
818
        else:
819
            t2.put('d', StringIO('newfile\n'))
820
821
        self.failUnless(t1.has('b/d'))
822
        self.failUnless(t2.has('d'))
823
        self.failUnless(t3.has('b/d'))
824
825
    def test_relpath(self):
826
        t = self.get_transport()
827
        self.assertEqual('', t.relpath(t.base))
828
        # base ends with /
829
        self.assertEqual('', t.relpath(t.base[:-1]))
830
        # subdirs which dont exist should still give relpaths.
831
        self.assertEqual('foo', t.relpath(t.base + 'foo'))
832
        # trailing slash should be the same.
833
        self.assertEqual('foo', t.relpath(t.base + 'foo/'))
834
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
835
    def test_relpath_at_root(self):
836
        t = self.get_transport()
837
        # clone all the way to the top
838
        new_transport = t.clone('..')
839
        while new_transport.base != t.base:
840
            t = new_transport
841
            new_transport = t.clone('..')
842
        # we must be able to get a relpath below the root
843
        self.assertEqual('', t.relpath(t.base))
844
        # and a deeper one should work too
845
        self.assertEqual('foo/bar', t.relpath(t.base + 'foo/bar'))
846
1530.1.3 by Robert Collins
transport implementations now tested consistently.
847
    def test_abspath(self):
848
        # smoke test for abspath. Corner cases for backends like unix fs's
849
        # that have aliasing problems like symlinks should go in backend
850
        # specific test cases.
851
        transport = self.get_transport()
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
852
        
853
        # disabled because some transports might normalize urls in generating
854
        # the abspath - eg http+pycurl-> just http -- mbp 20060308 
1530.1.3 by Robert Collins
transport implementations now tested consistently.
855
        self.assertEqual(transport.base + 'relpath',
856
                         transport.abspath('relpath'))
857
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
858
    def test_local_abspath(self):
859
        transport = self.get_transport()
860
        try:
861
            p = transport.local_abspath('.')
862
        except TransportNotPossible:
863
            pass # This is not a local transport
864
        else:
865
            self.assertEqual(getcwd(), p)
866
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
867
    def test_abspath_at_root(self):
868
        t = self.get_transport()
869
        # clone all the way to the top
870
        new_transport = t.clone('..')
871
        while new_transport.base != t.base:
872
            t = new_transport
873
            new_transport = t.clone('..')
874
        # we must be able to get a abspath of the root when we ask for
875
        # t.abspath('..') - this due to our choice that clone('..')
876
        # should return the root from the root, combined with the desire that
877
        # the url from clone('..') and from abspath('..') should be the same.
878
        self.assertEqual(t.base, t.abspath('..'))
879
        # '' should give us the root
880
        self.assertEqual(t.base, t.abspath(''))
881
        # and a path should append to the url
882
        self.assertEqual(t.base + 'foo', t.abspath('foo'))
883
1530.1.3 by Robert Collins
transport implementations now tested consistently.
884
    def test_iter_files_recursive(self):
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
885
        transport = self.get_transport()
886
        if not transport.listable():
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
887
            self.assertRaises(TransportNotPossible,
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
888
                              transport.iter_files_recursive)
889
            return
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
890
        self.build_tree(['isolated/',
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
891
                         'isolated/dir/',
892
                         'isolated/dir/foo',
893
                         'isolated/dir/bar',
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
894
                         'isolated/dir/b%25z', # make sure quoting is correct
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
895
                         'isolated/bar'],
896
                        transport=transport)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
897
        paths = set(transport.iter_files_recursive())
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
898
        # nb the directories are not converted
899
        self.assertEqual(paths,
900
                    set(['isolated/dir/foo',
901
                         'isolated/dir/bar',
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
902
                         'isolated/dir/b%2525z',
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
903
                         'isolated/bar']))
904
        sub_transport = transport.clone('isolated')
905
        paths = set(sub_transport.iter_files_recursive())
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
906
        self.assertEqual(paths,
907
            set(['dir/foo', 'dir/bar', 'dir/b%2525z', 'bar']))
908
909
    def test_copy_tree(self):
910
        # TODO: test file contents and permissions are preserved. This test was
911
        # added just to ensure that quoting was handled correctly.
912
        # -- David Allouche 2006-08-11
913
        transport = self.get_transport()
914
        if not transport.listable():
915
            self.assertRaises(TransportNotPossible,
916
                              transport.iter_files_recursive)
917
            return
918
        if transport.is_readonly():
919
            self.assertRaises(TransportNotPossible,
920
                              transport.put, 'a', 'some text for a\n')
921
            return
922
        self.build_tree(['from/',
923
                         'from/dir/',
924
                         'from/dir/foo',
925
                         'from/dir/bar',
926
                         'from/dir/b%25z', # make sure quoting is correct
927
                         'from/bar'],
928
                        transport=transport)
929
        transport.copy_tree('from', 'to')
930
        paths = set(transport.iter_files_recursive())
931
        self.assertEqual(paths,
932
                    set(['from/dir/foo',
933
                         'from/dir/bar',
934
                         'from/dir/b%2525z',
935
                         'from/bar',
936
                         'to/dir/foo',
937
                         'to/dir/bar',
938
                         'to/dir/b%2525z',
939
                         'to/bar',]))
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
940
941
    def test_unicode_paths(self):
1685.1.57 by Martin Pool
[broken] Skip unicode blackbox tests if not supported by filesystem
942
        """Test that we can read/write files with Unicode names."""
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
943
        t = self.get_transport()
944
1711.7.36 by John Arbash Meinel
Use different filenames to avoid path collisions on win32 w/ FAT32
945
        # With FAT32 and certain encodings on win32
946
        # '\xe5' and '\xe4' actually map to the same file
947
        # adding a suffix kicks in the 'preserving but insensitive'
948
        # route, and maintains the right files
949
        files = [u'\xe5.1', # a w/ circle iso-8859-1
950
                 u'\xe4.2', # a w/ dots iso-8859-1
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
951
                 u'\u017d', # Z with umlat iso-8859-2
952
                 u'\u062c', # Arabic j
953
                 u'\u0410', # Russian A
954
                 u'\u65e5', # Kanji person
955
                ]
956
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
957
        try:
1711.4.13 by John Arbash Meinel
Use line_endings='binary' for win32
958
            self.build_tree(files, transport=t, line_endings='binary')
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
959
        except UnicodeError:
960
            raise TestSkipped("cannot handle unicode paths in current encoding")
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
961
962
        # A plain unicode string is not a valid url
963
        for fname in files:
964
            self.assertRaises(InvalidURL, t.get, fname)
965
966
        for fname in files:
967
            fname_utf8 = fname.encode('utf-8')
968
            contents = 'contents of %s\n' % (fname_utf8,)
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
969
            self.check_transport_contents(contents, t, urlutils.escape(fname))
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
970
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
971
    def test_connect_twice_is_same_content(self):
972
        # check that our server (whatever it is) is accessable reliably
973
        # via get_transport and multiple connections share content.
974
        transport = self.get_transport()
975
        if transport.is_readonly():
976
            return
977
        transport.put('foo', StringIO('bar'))
978
        transport2 = self.get_transport()
979
        self.check_transport_contents('bar', transport2, 'foo')
980
        # its base should be usable.
981
        transport2 = bzrlib.transport.get_transport(transport.base)
982
        self.check_transport_contents('bar', transport2, 'foo')
983
984
        # now opening at a relative url should give use a sane result:
985
        transport.mkdir('newdir')
986
        transport2 = bzrlib.transport.get_transport(transport.base + "newdir")
987
        transport2 = transport2.clone('..')
988
        self.check_transport_contents('bar', transport2, 'foo')
989
990
    def test_lock_write(self):
991
        transport = self.get_transport()
992
        if transport.is_readonly():
993
            self.assertRaises(TransportNotPossible, transport.lock_write, 'foo')
994
            return
995
        transport.put('lock', StringIO())
996
        lock = transport.lock_write('lock')
997
        # TODO make this consistent on all platforms:
998
        # self.assertRaises(LockError, transport.lock_write, 'lock')
999
        lock.unlock()
1000
1001
    def test_lock_read(self):
1002
        transport = self.get_transport()
1003
        if transport.is_readonly():
1004
            file('lock', 'w').close()
1005
        else:
1006
            transport.put('lock', StringIO())
1007
        lock = transport.lock_read('lock')
1008
        # TODO make this consistent on all platforms:
1009
        # self.assertRaises(LockError, transport.lock_read, 'lock')
1010
        lock.unlock()
1185.85.80 by John Arbash Meinel
[merge] jam-integration 1527, including branch-formats, help text, misc bug fixes.
1011
1594.2.5 by Robert Collins
Readv patch from Johan Rydberg giving knits partial download support.
1012
    def test_readv(self):
1013
        transport = self.get_transport()
1014
        if transport.is_readonly():
1015
            file('a', 'w').write('0123456789')
1016
        else:
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
1017
            transport.put('a', StringIO('0123456789'))
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
1018
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
1019
        d = list(transport.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
1594.2.5 by Robert Collins
Readv patch from Johan Rydberg giving knits partial download support.
1020
        self.assertEqual(d[0], (0, '0'))
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
1021
        self.assertEqual(d[1], (1, '1'))
1022
        self.assertEqual(d[2], (3, '34'))
1023
        self.assertEqual(d[3], (9, '9'))
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
1024
1864.5.2 by John Arbash Meinel
always read in sorted order, and return in requested order, but only cache what is currently out of order
1025
    def test_readv_out_of_order(self):
1026
        transport = self.get_transport()
1027
        if transport.is_readonly():
1028
            file('a', 'w').write('0123456789')
1029
        else:
1030
            transport.put('a', StringIO('01234567890'))
1031
1032
        d = list(transport.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
1033
        self.assertEqual(d[0], (1, '1'))
1034
        self.assertEqual(d[1], (9, '9'))
1035
        self.assertEqual(d[2], (0, '0'))
1036
        self.assertEqual(d[3], (3, '34'))