/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
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
1530.1.3 by Robert Collins
transport implementations now tested consistently.
2
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.
7
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.
12
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
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
28
from bzrlib.errors import (DirectoryNotEmpty, NoSuchFile, FileExists,
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
29
                           LockError,
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
30
                           PathError,
1530.1.3 by Robert Collins
transport implementations now tested consistently.
31
                           TransportNotPossible, ConnectionError)
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
32
from bzrlib.tests import TestCaseInTempDir, TestSkipped
1530.1.3 by Robert Collins
transport implementations now tested consistently.
33
from bzrlib.transport import memory, urlescape
34
import bzrlib.transport
35
36
37
def _append(fn, txt):
38
    """Append the given text (file-like object) to the supplied filename."""
39
    f = open(fn, 'ab')
1530.1.21 by Robert Collins
Review feedback fixes.
40
    try:
41
        f.write(txt.read())
42
    finally:
43
        f.close()
1530.1.3 by Robert Collins
transport implementations now tested consistently.
44
45
46
class TestTransportImplementation(TestCaseInTempDir):
47
    """Implementation verification for transports.
48
    
49
    To verify a transport we need a server factory, which is a callable
50
    that accepts no parameters and returns an implementation of
51
    bzrlib.transport.Server.
52
    
53
    That Server is then used to construct transport instances and test
54
    the transport via loopback activity.
55
56
    Currently this assumes that the Transport object is connected to the 
57
    current working directory.  So that whatever is done 
58
    through the transport, should show up in the working 
59
    directory, and vice-versa. This is a bug, because its possible to have
60
    URL schemes which provide access to something that may not be 
61
    result in storage on the local disk, i.e. due to file system limits, or 
62
    due to it being a database or some other non-filesystem tool.
63
64
    This also tests to make sure that the functions work with both
65
    generators and lists (assuming iter(list) is effectively a generator)
66
    """
67
    
68
    def setUp(self):
69
        super(TestTransportImplementation, self).setUp()
70
        self._server = self.transport_server()
71
        self._server.setUp()
72
73
    def tearDown(self):
74
        super(TestTransportImplementation, self).tearDown()
75
        self._server.tearDown()
76
        
77
    def check_transport_contents(self, content, transport, relpath):
78
        """Check that transport.get(relpath).read() == content."""
1530.1.21 by Robert Collins
Review feedback fixes.
79
        self.assertEqualDiff(content, transport.get(relpath).read())
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
80
1530.1.3 by Robert Collins
transport implementations now tested consistently.
81
    def get_transport(self):
82
        """Return a connected transport to the local directory."""
1540.3.19 by Martin Pool
Transport tests should always construct the precise transport to be tested
83
        base_url = self._server.get_url()
84
        t = bzrlib.transport.get_transport(base_url)
85
        if not isinstance(t, self.transport_class):
86
            # we want to make sure to construct one particular class, even if
87
            # there are several available implementations of this transport;
88
            # therefore construct it by hand rather than through the regular
89
            # get_transport method
90
            t = self.transport_class(base_url)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
91
        return t
92
93
    def assertListRaises(self, excClass, func, *args, **kwargs):
1530.1.21 by Robert Collins
Review feedback fixes.
94
        """Fail unless excClass is raised when the iterator from func is used.
95
        
96
        Many transport functions can return generators this makes sure
1530.1.3 by Robert Collins
transport implementations now tested consistently.
97
        to wrap them in a list() call to make sure the whole generator
98
        is run, and that the proper exception is raised.
99
        """
100
        try:
101
            list(func(*args, **kwargs))
102
        except excClass:
103
            return
104
        else:
105
            if hasattr(excClass,'__name__'): excName = excClass.__name__
106
            else: excName = str(excClass)
107
            raise self.failureException, "%s not raised" % excName
108
109
    def test_has(self):
110
        t = self.get_transport()
111
112
        files = ['a', 'b', 'e', 'g', '%']
113
        self.build_tree(files, transport=t)
114
        self.assertEqual(True, t.has('a'))
115
        self.assertEqual(False, t.has('c'))
116
        self.assertEqual(True, t.has(urlescape('%')))
117
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
118
                [True, True, False, False, True, False, True, False])
119
        self.assertEqual(True, t.has_any(['a', 'b', 'c']))
120
        self.assertEqual(False, t.has_any(['c', 'd', 'f', urlescape('%%')]))
121
        self.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
122
                [True, True, False, False, True, False, True, False])
123
        self.assertEqual(False, t.has_any(['c', 'c', 'c']))
124
        self.assertEqual(True, t.has_any(['b', 'b', 'b']))
125
126
    def test_get(self):
127
        t = self.get_transport()
128
129
        files = ['a', 'b', 'e', 'g']
130
        contents = ['contents of a\n',
131
                    'contents of b\n',
132
                    'contents of e\n',
133
                    'contents of g\n',
134
                    ]
1551.2.39 by abentley
Fix line endings in tests
135
        self.build_tree(files, transport=t, line_endings='binary')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
136
        self.check_transport_contents('contents of a\n', t, 'a')
137
        content_f = t.get_multi(files)
138
        for content, f in zip(contents, content_f):
139
            self.assertEqual(content, f.read())
140
141
        content_f = t.get_multi(iter(files))
142
        for content, f in zip(contents, content_f):
143
            self.assertEqual(content, f.read())
144
145
        self.assertRaises(NoSuchFile, t.get, 'c')
146
        self.assertListRaises(NoSuchFile, t.get_multi, ['a', 'b', 'c'])
147
        self.assertListRaises(NoSuchFile, t.get_multi, iter(['a', 'b', 'c']))
148
149
    def test_put(self):
150
        t = self.get_transport()
151
152
        if t.is_readonly():
153
            self.assertRaises(TransportNotPossible,
154
                    t.put, 'a', 'some text for a\n')
155
            return
156
157
        t.put('a', StringIO('some text for a\n'))
158
        self.failUnless(t.has('a'))
159
        self.check_transport_contents('some text for a\n', t, 'a')
160
        # Make sure 'has' is updated
161
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
162
                [True, False, False, False, False])
163
        # Put also replaces contents
164
        self.assertEqual(t.put_multi([('a', StringIO('new\ncontents for\na\n')),
165
                                      ('d', StringIO('contents\nfor d\n'))]),
166
                         2)
167
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e'])),
168
                [True, False, False, True, False])
169
        self.check_transport_contents('new\ncontents for\na\n', t, 'a')
170
        self.check_transport_contents('contents\nfor d\n', t, 'd')
171
172
        self.assertEqual(
173
            t.put_multi(iter([('a', StringIO('diff\ncontents for\na\n')),
174
                              ('d', StringIO('another contents\nfor d\n'))])),
175
                        2)
176
        self.check_transport_contents('diff\ncontents for\na\n', t, 'a')
177
        self.check_transport_contents('another contents\nfor d\n', t, 'd')
178
179
        self.assertRaises(NoSuchFile,
180
                          t.put, 'path/doesnt/exist/c', 'contents')
181
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
182
    def test_put_permissions(self):
183
        t = self.get_transport()
184
185
        if t.is_readonly():
186
            return
187
        t.put('mode644', StringIO('test text\n'), mode=0644)
1530.1.21 by Robert Collins
Review feedback fixes.
188
        self.assertTransportMode(t, 'mode644', 0644)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
189
        t.put('mode666', StringIO('test text\n'), mode=0666)
1530.1.21 by Robert Collins
Review feedback fixes.
190
        self.assertTransportMode(t, 'mode666', 0666)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
191
        t.put('mode600', StringIO('test text\n'), mode=0600)
1530.1.21 by Robert Collins
Review feedback fixes.
192
        self.assertTransportMode(t, 'mode600', 0600)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
193
        # Yes, you can put a file such that it becomes readonly
194
        t.put('mode400', StringIO('test text\n'), mode=0400)
1530.1.21 by Robert Collins
Review feedback fixes.
195
        self.assertTransportMode(t, 'mode400', 0400)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
196
        t.put_multi([('mmode644', StringIO('text\n'))], mode=0644)
1530.1.21 by Robert Collins
Review feedback fixes.
197
        self.assertTransportMode(t, 'mmode644', 0644)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
198
        
1530.1.3 by Robert Collins
transport implementations now tested consistently.
199
    def test_mkdir(self):
200
        t = self.get_transport()
201
202
        if t.is_readonly():
203
            # cannot mkdir on readonly transports. We're not testing for 
204
            # cache coherency because cache behaviour is not currently
205
            # defined for the transport interface.
206
            self.assertRaises(TransportNotPossible, t.mkdir, '.')
207
            self.assertRaises(TransportNotPossible, t.mkdir, 'new_dir')
208
            self.assertRaises(TransportNotPossible, t.mkdir_multi, ['new_dir'])
209
            self.assertRaises(TransportNotPossible, t.mkdir, 'path/doesnt/exist')
210
            return
211
        # Test mkdir
212
        t.mkdir('dir_a')
213
        self.assertEqual(t.has('dir_a'), True)
214
        self.assertEqual(t.has('dir_b'), False)
215
216
        t.mkdir('dir_b')
217
        self.assertEqual(t.has('dir_b'), True)
218
219
        t.mkdir_multi(['dir_c', 'dir_d'])
220
221
        t.mkdir_multi(iter(['dir_e', 'dir_f']))
222
        self.assertEqual(list(t.has_multi(
223
            ['dir_a', 'dir_b', 'dir_c', 'dir_q',
224
             'dir_d', 'dir_e', 'dir_f', 'dir_b'])),
225
            [True, True, True, False,
226
             True, True, True, True])
227
228
        # we were testing that a local mkdir followed by a transport
229
        # mkdir failed thusly, but given that we * in one process * do not
230
        # concurrently fiddle with disk dirs and then use transport to do 
231
        # things, the win here seems marginal compared to the constraint on
232
        # the interface. RBC 20051227
233
        t.mkdir('dir_g')
234
        self.assertRaises(FileExists, t.mkdir, 'dir_g')
235
236
        # 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.
237
        self.assertEqual(2, 
1530.1.3 by Robert Collins
transport implementations now tested consistently.
238
            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.
239
                         ('dir_b/b', StringIO('contents of dir_b/b'))]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
240
        self.check_transport_contents('contents of dir_a/a', t, 'dir_a/a')
241
        self.check_transport_contents('contents of dir_b/b', t, 'dir_b/b')
242
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
243
        # mkdir of a dir with an absent parent
244
        self.assertRaises(NoSuchFile, t.mkdir, 'missing/dir')
245
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
246
    def test_mkdir_permissions(self):
247
        t = self.get_transport()
248
        if t.is_readonly():
249
            return
1608.2.7 by Martin Pool
Rename supports_unix_modebits to _can_roundtrip_unix_modebits for clarity
250
        if not t._can_roundtrip_unix_modebits():
1608.2.5 by Martin Pool
Add Transport.supports_unix_modebits, so tests can
251
            # no sense testing on this transport
252
            return
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
253
        # Test mkdir with a mode
254
        t.mkdir('dmode755', mode=0755)
1530.1.21 by Robert Collins
Review feedback fixes.
255
        self.assertTransportMode(t, 'dmode755', 0755)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
256
        t.mkdir('dmode555', mode=0555)
1530.1.21 by Robert Collins
Review feedback fixes.
257
        self.assertTransportMode(t, 'dmode555', 0555)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
258
        t.mkdir('dmode777', mode=0777)
1530.1.21 by Robert Collins
Review feedback fixes.
259
        self.assertTransportMode(t, 'dmode777', 0777)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
260
        t.mkdir('dmode700', mode=0700)
1530.1.21 by Robert Collins
Review feedback fixes.
261
        self.assertTransportMode(t, 'dmode700', 0700)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
262
        # TODO: jam 20051215 test mkdir_multi with a mode
263
        t.mkdir_multi(['mdmode755'], mode=0755)
1530.1.21 by Robert Collins
Review feedback fixes.
264
        self.assertTransportMode(t, 'mdmode755', 0755)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
265
1530.1.3 by Robert Collins
transport implementations now tested consistently.
266
    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.
267
        # FIXME: test:   same server to same server (partly done)
268
        # same protocol two servers
269
        # and    different protocols (done for now except for MemoryTransport.
270
        # - RBC 20060122
1530.1.3 by Robert Collins
transport implementations now tested consistently.
271
        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.
272
273
        def simple_copy_files(transport_from, transport_to):
274
            files = ['a', 'b', 'c', 'd']
275
            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.
276
            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.
277
            for f in files:
278
                self.check_transport_contents(transport_to.get(f).read(),
279
                                              transport_from, f)
280
1530.1.3 by Robert Collins
transport implementations now tested consistently.
281
        t = self.get_transport()
282
        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.
283
        simple_copy_files(t, temp_transport)
284
        if not t.is_readonly():
285
            t.mkdir('copy_to_simple')
286
            t2 = t.clone('copy_to_simple')
287
            simple_copy_files(t, t2)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
288
289
290
        # Test that copying into a missing directory raises
291
        # NoSuchFile
292
        if t.is_readonly():
1530.1.21 by Robert Collins
Review feedback fixes.
293
            self.build_tree(['e/', 'e/f'])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
294
        else:
295
            t.mkdir('e')
296
            t.put('e/f', StringIO('contents of e'))
297
        self.assertRaises(NoSuchFile, t.copy_to, ['e/f'], temp_transport)
298
        temp_transport.mkdir('e')
299
        t.copy_to(['e/f'], temp_transport)
300
301
        del temp_transport
302
        temp_transport = MemoryTransport('memory:/')
303
304
        files = ['a', 'b', 'c', 'd']
305
        t.copy_to(iter(files), temp_transport)
306
        for f in files:
307
            self.check_transport_contents(temp_transport.get(f).read(),
308
                                          t, f)
309
        del temp_transport
310
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
311
        for mode in (0666, 0644, 0600, 0400):
312
            temp_transport = MemoryTransport("memory:/")
313
            t.copy_to(files, temp_transport, mode=mode)
314
            for f in files:
1530.1.21 by Robert Collins
Review feedback fixes.
315
                self.assertTransportMode(temp_transport, f, mode)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
316
1530.1.3 by Robert Collins
transport implementations now tested consistently.
317
    def test_append(self):
318
        t = self.get_transport()
319
320
        if t.is_readonly():
321
            open('a', 'wb').write('diff\ncontents for\na\n')
322
            open('b', 'wb').write('contents\nfor b\n')
323
        else:
324
            t.put_multi([
325
                    ('a', StringIO('diff\ncontents for\na\n')),
326
                    ('b', StringIO('contents\nfor b\n'))
327
                    ])
328
329
        if t.is_readonly():
330
            self.assertRaises(TransportNotPossible,
331
                    t.append, 'a', 'add\nsome\nmore\ncontents\n')
332
            _append('a', StringIO('add\nsome\nmore\ncontents\n'))
333
        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.
334
            self.assertEqual(20,
335
                t.append('a', StringIO('add\nsome\nmore\ncontents\n')))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
336
337
        self.check_transport_contents(
338
            'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
339
            t, 'a')
340
341
        if t.is_readonly():
342
            self.assertRaises(TransportNotPossible,
343
                    t.append_multi,
344
                        [('a', 'and\nthen\nsome\nmore\n'),
345
                         ('b', 'some\nmore\nfor\nb\n')])
346
            _append('a', StringIO('and\nthen\nsome\nmore\n'))
347
            _append('b', StringIO('some\nmore\nfor\nb\n'))
348
        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.
349
            self.assertEqual((43, 15), 
350
                t.append_multi([('a', StringIO('and\nthen\nsome\nmore\n')),
351
                                ('b', StringIO('some\nmore\nfor\nb\n'))]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
352
        self.check_transport_contents(
353
            'diff\ncontents for\na\n'
354
            'add\nsome\nmore\ncontents\n'
355
            'and\nthen\nsome\nmore\n',
356
            t, 'a')
357
        self.check_transport_contents(
358
                'contents\nfor b\n'
359
                'some\nmore\nfor\nb\n',
360
                t, 'b')
361
362
        if t.is_readonly():
363
            _append('a', StringIO('a little bit more\n'))
364
            _append('b', StringIO('from an iterator\n'))
365
        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.
366
            self.assertEqual((62, 31),
367
                t.append_multi(iter([('a', StringIO('a little bit more\n')),
368
                                     ('b', StringIO('from an iterator\n'))])))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
369
        self.check_transport_contents(
370
            'diff\ncontents for\na\n'
371
            'add\nsome\nmore\ncontents\n'
372
            'and\nthen\nsome\nmore\n'
373
            'a little bit more\n',
374
            t, 'a')
375
        self.check_transport_contents(
376
                'contents\nfor b\n'
377
                'some\nmore\nfor\nb\n'
378
                'from an iterator\n',
379
                t, 'b')
380
381
        if t.is_readonly():
382
            _append('c', StringIO('some text\nfor a missing file\n'))
383
            _append('a', StringIO('some text in a\n'))
384
            _append('d', StringIO('missing file r\n'))
385
        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.
386
            self.assertEqual(0,
387
                t.append('c', StringIO('some text\nfor a missing file\n')))
388
            self.assertEqual((80, 0),
389
                t.append_multi([('a', StringIO('some text in a\n')),
390
                                ('d', StringIO('missing file r\n'))]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
391
        self.check_transport_contents(
392
            'diff\ncontents for\na\n'
393
            'add\nsome\nmore\ncontents\n'
394
            'and\nthen\nsome\nmore\n'
395
            'a little bit more\n'
396
            'some text in a\n',
397
            t, 'a')
398
        self.check_transport_contents('some text\nfor a missing file\n',
399
                                      t, 'c')
400
        self.check_transport_contents('missing file r\n', t, 'd')
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
401
        
402
        # a file with no parent should fail..
403
        if not t.is_readonly():
404
            self.assertRaises(NoSuchFile,
405
                              t.append, 'missing/path', 
406
                              StringIO('content'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
407
408
    def test_append_file(self):
409
        t = self.get_transport()
410
411
        contents = [
412
            ('f1', StringIO('this is a string\nand some more stuff\n')),
413
            ('f2', StringIO('here is some text\nand a bit more\n')),
414
            ('f3', StringIO('some text for the\nthird file created\n')),
415
            ('f4', StringIO('this is a string\nand some more stuff\n')),
416
            ('f5', StringIO('here is some text\nand a bit more\n')),
417
            ('f6', StringIO('some text for the\nthird file created\n'))
418
        ]
419
        
420
        if t.is_readonly():
421
            for f, val in contents:
422
                open(f, 'wb').write(val.read())
423
        else:
424
            t.put_multi(contents)
425
426
        a1 = StringIO('appending to\none\n')
427
        if t.is_readonly():
428
            _append('f1', a1)
429
        else:
430
            t.append('f1', a1)
431
432
        del a1
433
434
        self.check_transport_contents(
435
                'this is a string\nand some more stuff\n'
436
                'appending to\none\n',
437
                t, 'f1')
438
439
        a2 = StringIO('adding more\ntext to two\n')
440
        a3 = StringIO('some garbage\nto put in three\n')
441
442
        if t.is_readonly():
443
            _append('f2', a2)
444
            _append('f3', a3)
445
        else:
446
            t.append_multi([('f2', a2), ('f3', a3)])
447
448
        del a2, a3
449
450
        self.check_transport_contents(
451
                'here is some text\nand a bit more\n'
452
                'adding more\ntext to two\n',
453
                t, 'f2')
454
        self.check_transport_contents( 
455
                'some text for the\nthird file created\n'
456
                'some garbage\nto put in three\n',
457
                t, 'f3')
458
459
        # Test that an actual file object can be used with put
460
        a4 = t.get('f1')
461
        if t.is_readonly():
462
            _append('f4', a4)
463
        else:
464
            t.append('f4', a4)
465
466
        del a4
467
468
        self.check_transport_contents(
469
                'this is a string\nand some more stuff\n'
470
                'this is a string\nand some more stuff\n'
471
                'appending to\none\n',
472
                t, 'f4')
473
474
        a5 = t.get('f2')
475
        a6 = t.get('f3')
476
        if t.is_readonly():
477
            _append('f5', a5)
478
            _append('f6', a6)
479
        else:
480
            t.append_multi([('f5', a5), ('f6', a6)])
481
482
        del a5, a6
483
484
        self.check_transport_contents(
485
                'here is some text\nand a bit more\n'
486
                'here is some text\nand a bit more\n'
487
                'adding more\ntext to two\n',
488
                t, 'f5')
489
        self.check_transport_contents(
490
                'some text for the\nthird file created\n'
491
                'some text for the\nthird file created\n'
492
                'some garbage\nto put in three\n',
493
                t, 'f6')
494
495
        a5 = t.get('f2')
496
        a6 = t.get('f2')
497
        a7 = t.get('f3')
498
        if t.is_readonly():
499
            _append('c', a5)
500
            _append('a', a6)
501
            _append('d', a7)
502
        else:
503
            t.append('c', a5)
504
            t.append_multi([('a', a6), ('d', a7)])
505
        del a5, a6, a7
506
        self.check_transport_contents(t.get('f2').read(), t, 'c')
507
        self.check_transport_contents(t.get('f3').read(), t, 'd')
508
1666.1.6 by Robert Collins
Make knit the default format.
509
    def test_append_mode(self):
510
        # check append accepts a mode
511
        t = self.get_transport()
512
        if t.is_readonly():
513
            return
514
        t.append('f', StringIO('f'), mode=None)
515
        
1530.1.3 by Robert Collins
transport implementations now tested consistently.
516
    def test_delete(self):
517
        # TODO: Test Transport.delete
518
        t = self.get_transport()
519
520
        # Not much to do with a readonly transport
521
        if t.is_readonly():
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
522
            self.assertRaises(TransportNotPossible, t.delete, 'missing')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
523
            return
524
525
        t.put('a', StringIO('a little bit of text\n'))
526
        self.failUnless(t.has('a'))
527
        t.delete('a')
528
        self.failIf(t.has('a'))
529
530
        self.assertRaises(NoSuchFile, t.delete, 'a')
531
532
        t.put('a', StringIO('a text\n'))
533
        t.put('b', StringIO('b text\n'))
534
        t.put('c', StringIO('c text\n'))
535
        self.assertEqual([True, True, True],
536
                list(t.has_multi(['a', 'b', 'c'])))
537
        t.delete_multi(['a', 'c'])
538
        self.assertEqual([False, True, False],
539
                list(t.has_multi(['a', 'b', 'c'])))
540
        self.failIf(t.has('a'))
541
        self.failUnless(t.has('b'))
542
        self.failIf(t.has('c'))
543
544
        self.assertRaises(NoSuchFile,
545
                t.delete_multi, ['a', 'b', 'c'])
546
547
        self.assertRaises(NoSuchFile,
548
                t.delete_multi, iter(['a', 'b', 'c']))
549
550
        t.put('a', StringIO('another a text\n'))
551
        t.put('c', StringIO('another c text\n'))
552
        t.delete_multi(iter(['a', 'b', 'c']))
553
554
        # We should have deleted everything
555
        # SftpServer creates control files in the
556
        # working directory, so we can just do a
557
        # plain "listdir".
558
        # self.assertEqual([], os.listdir('.'))
559
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
560
    def test_rmdir(self):
561
        t = self.get_transport()
562
        # Not much to do with a readonly transport
563
        if t.is_readonly():
564
            self.assertRaises(TransportNotPossible, t.rmdir, 'missing')
565
            return
566
        t.mkdir('adir')
567
        t.mkdir('adir/bdir')
568
        t.rmdir('adir/bdir')
569
        self.assertRaises(NoSuchFile, t.stat, 'adir/bdir')
570
        t.rmdir('adir')
571
        self.assertRaises(NoSuchFile, t.stat, 'adir')
572
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
573
    def test_rmdir_not_empty(self):
574
        """Deleting a non-empty directory raises an exception
575
        
576
        sftp (and possibly others) don't give us a specific "directory not
577
        empty" exception -- we can just see that the operation failed.
578
        """
579
        t = self.get_transport()
580
        if t.is_readonly():
581
            return
582
        t.mkdir('adir')
583
        t.mkdir('adir/bdir')
584
        self.assertRaises(PathError, t.rmdir, 'adir')
585
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
586
    def test_rename_dir_succeeds(self):
587
        t = self.get_transport()
588
        if t.is_readonly():
589
            raise TestSkipped("transport is readonly")
590
        t.mkdir('adir')
591
        t.mkdir('adir/asubdir')
592
        t.rename('adir', 'bdir')
593
        self.assertTrue(t.has('bdir/asubdir'))
594
        self.assertFalse(t.has('adir'))
595
596
    def test_rename_dir_nonempty(self):
597
        """Attempting to replace a nonemtpy directory should fail"""
598
        t = self.get_transport()
599
        if t.is_readonly():
600
            raise TestSkipped("transport is readonly")
601
        t.mkdir('adir')
602
        t.mkdir('adir/asubdir')
603
        t.mkdir('bdir')
604
        t.mkdir('bdir/bsubdir')
605
        self.assertRaises(PathError, t.rename, 'bdir', 'adir')
606
        # nothing was changed so it should still be as before
607
        self.assertTrue(t.has('bdir/bsubdir'))
608
        self.assertFalse(t.has('adir/bdir'))
609
        self.assertFalse(t.has('adir/bsubdir'))
610
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
611
    def test_delete_tree(self):
612
        t = self.get_transport()
613
614
        # Not much to do with a readonly transport
615
        if t.is_readonly():
616
            self.assertRaises(TransportNotPossible, t.delete_tree, 'missing')
617
            return
618
619
        # and does it like listing ?
620
        t.mkdir('adir')
621
        try:
622
            t.delete_tree('adir')
623
        except TransportNotPossible:
624
            # ok, this transport does not support delete_tree
625
            return
626
        
627
        # did it delete that trivial case?
628
        self.assertRaises(NoSuchFile, t.stat, 'adir')
629
630
        self.build_tree(['adir/',
631
                         'adir/file', 
632
                         'adir/subdir/', 
633
                         'adir/subdir/file', 
634
                         'adir/subdir2/',
635
                         'adir/subdir2/file',
636
                         ], transport=t)
637
638
        t.delete_tree('adir')
639
        # adir should be gone now.
640
        self.assertRaises(NoSuchFile, t.stat, 'adir')
641
1530.1.3 by Robert Collins
transport implementations now tested consistently.
642
    def test_move(self):
643
        t = self.get_transport()
644
645
        if t.is_readonly():
646
            return
647
648
        # TODO: I would like to use os.listdir() to
649
        # make sure there are no extra files, but SftpServer
650
        # creates control files in the working directory
651
        # perhaps all of this could be done in a subdirectory
652
653
        t.put('a', StringIO('a first file\n'))
654
        self.assertEquals([True, False], list(t.has_multi(['a', 'b'])))
655
656
        t.move('a', 'b')
657
        self.failUnless(t.has('b'))
658
        self.failIf(t.has('a'))
659
660
        self.check_transport_contents('a first file\n', t, 'b')
661
        self.assertEquals([False, True], list(t.has_multi(['a', 'b'])))
662
663
        # Overwrite a file
664
        t.put('c', StringIO('c this file\n'))
665
        t.move('c', 'b')
666
        self.failIf(t.has('c'))
667
        self.check_transport_contents('c this file\n', t, 'b')
668
669
        # TODO: Try to write a test for atomicity
670
        # TODO: Test moving into a non-existant subdirectory
671
        # TODO: Test Transport.move_multi
672
673
    def test_copy(self):
674
        t = self.get_transport()
675
676
        if t.is_readonly():
677
            return
678
679
        t.put('a', StringIO('a file\n'))
680
        t.copy('a', 'b')
681
        self.check_transport_contents('a file\n', t, 'b')
682
683
        self.assertRaises(NoSuchFile, t.copy, 'c', 'd')
684
        os.mkdir('c')
685
        # What should the assert be if you try to copy a
686
        # file over a directory?
687
        #self.assertRaises(Something, t.copy, 'a', 'c')
688
        t.put('d', StringIO('text in d\n'))
689
        t.copy('d', 'b')
690
        self.check_transport_contents('text in d\n', t, 'b')
691
692
        # TODO: test copy_multi
693
694
    def test_connection_error(self):
695
        """ConnectionError is raised when connection is impossible"""
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
696
        try:
697
            url = self._server.get_bogus_url()
698
        except NotImplementedError:
699
            raise TestSkipped("Transport %s has no bogus URL support." %
700
                              self._server.__class__)
701
        t = bzrlib.transport.get_transport(url)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
702
        try:
703
            t.get('.bzr/branch')
704
        except (ConnectionError, NoSuchFile), e:
705
            pass
706
        except (Exception), e:
707
            self.failIf(True, 'Wrong exception thrown: %s' % e)
708
        else:
709
            self.failIf(True, 'Did not get the expected exception.')
710
711
    def test_stat(self):
712
        # TODO: Test stat, just try once, and if it throws, stop testing
713
        from stat import S_ISDIR, S_ISREG
714
715
        t = self.get_transport()
716
717
        try:
718
            st = t.stat('.')
719
        except TransportNotPossible, e:
720
            # This transport cannot stat
721
            return
722
723
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e']
724
        sizes = [14, 0, 16, 0, 18] 
1551.2.39 by abentley
Fix line endings in tests
725
        self.build_tree(paths, transport=t, line_endings='binary')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
726
727
        for path, size in zip(paths, sizes):
728
            st = t.stat(path)
729
            if path.endswith('/'):
730
                self.failUnless(S_ISDIR(st.st_mode))
731
                # directory sizes are meaningless
732
            else:
733
                self.failUnless(S_ISREG(st.st_mode))
734
                self.assertEqual(size, st.st_size)
735
736
        remote_stats = list(t.stat_multi(paths))
737
        remote_iter_stats = list(t.stat_multi(iter(paths)))
738
739
        self.assertRaises(NoSuchFile, t.stat, 'q')
740
        self.assertRaises(NoSuchFile, t.stat, 'b/a')
741
742
        self.assertListRaises(NoSuchFile, t.stat_multi, ['a', 'c', 'd'])
743
        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.
744
        self.build_tree(['subdir/', 'subdir/file'], transport=t)
745
        subdir = t.clone('subdir')
746
        subdir.stat('./file')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
747
        subdir.stat('.')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
748
749
    def test_list_dir(self):
750
        # TODO: Test list_dir, just try once, and if it throws, stop testing
751
        t = self.get_transport()
752
        
753
        if not t.listable():
754
            self.assertRaises(TransportNotPossible, t.list_dir, '.')
755
            return
756
757
        def sorted_list(d):
758
            l = list(t.list_dir(d))
759
            l.sort()
760
            return l
761
762
        # SftpServer creates control files in the working directory
763
        # so lets move down a directory to avoid those.
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
764
        if not t.is_readonly():
765
            t.mkdir('wd')
766
        else:
767
            os.mkdir('wd')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
768
        t = t.clone('wd')
769
770
        self.assertEqual([], sorted_list(u'.'))
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
771
        # c2 is precisely one letter longer than c here to test that
772
        # suffixing is not confused.
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
773
        if not t.is_readonly():
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
774
            self.build_tree(['a', 'b', 'c/', 'c/d', 'c/e', 'c2/'], transport=t)
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
775
        else:
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
776
            self.build_tree(['wd/a', 'wd/b', 'wd/c/', 'wd/c/d', 'wd/c/e', 'wd/c2/'])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
777
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
778
        self.assertEqual([u'a', u'b', u'c', u'c2'], sorted_list(u'.'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
779
        self.assertEqual([u'd', u'e'], sorted_list(u'c'))
780
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
781
        if not t.is_readonly():
782
            t.delete('c/d')
783
            t.delete('b')
784
        else:
785
            os.unlink('wd/c/d')
786
            os.unlink('wd/b')
787
            
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
788
        self.assertEqual([u'a', u'c', u'c2'], sorted_list('.'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
789
        self.assertEqual([u'e'], sorted_list(u'c'))
790
1662.1.12 by Martin Pool
Translate unknown sftp errors to PathError, no NoSuchFile
791
        self.assertListRaises(PathError, t.list_dir, 'q')
792
        self.assertListRaises(PathError, t.list_dir, 'c/f')
793
        self.assertListRaises(PathError, t.list_dir, 'a')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
794
795
    def test_clone(self):
796
        # TODO: Test that clone moves up and down the filesystem
797
        t1 = self.get_transport()
798
799
        self.build_tree(['a', 'b/', 'b/c'], transport=t1)
800
801
        self.failUnless(t1.has('a'))
802
        self.failUnless(t1.has('b/c'))
803
        self.failIf(t1.has('c'))
804
805
        t2 = t1.clone('b')
806
        self.assertEqual(t1.base + 'b/', t2.base)
807
808
        self.failUnless(t2.has('c'))
809
        self.failIf(t2.has('a'))
810
811
        t3 = t2.clone('..')
812
        self.failUnless(t3.has('a'))
813
        self.failIf(t3.has('c'))
814
815
        self.failIf(t1.has('b/d'))
816
        self.failIf(t2.has('d'))
817
        self.failIf(t3.has('b/d'))
818
819
        if t1.is_readonly():
820
            open('b/d', 'wb').write('newfile\n')
821
        else:
822
            t2.put('d', StringIO('newfile\n'))
823
824
        self.failUnless(t1.has('b/d'))
825
        self.failUnless(t2.has('d'))
826
        self.failUnless(t3.has('b/d'))
827
828
    def test_relpath(self):
829
        t = self.get_transport()
830
        self.assertEqual('', t.relpath(t.base))
831
        # base ends with /
832
        self.assertEqual('', t.relpath(t.base[:-1]))
833
        # subdirs which dont exist should still give relpaths.
834
        self.assertEqual('foo', t.relpath(t.base + 'foo'))
835
        # trailing slash should be the same.
836
        self.assertEqual('foo', t.relpath(t.base + 'foo/'))
837
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
838
    def test_relpath_at_root(self):
839
        t = self.get_transport()
840
        # clone all the way to the top
841
        new_transport = t.clone('..')
842
        while new_transport.base != t.base:
843
            t = new_transport
844
            new_transport = t.clone('..')
845
        # we must be able to get a relpath below the root
846
        self.assertEqual('', t.relpath(t.base))
847
        # and a deeper one should work too
848
        self.assertEqual('foo/bar', t.relpath(t.base + 'foo/bar'))
849
1530.1.3 by Robert Collins
transport implementations now tested consistently.
850
    def test_abspath(self):
851
        # smoke test for abspath. Corner cases for backends like unix fs's
852
        # that have aliasing problems like symlinks should go in backend
853
        # specific test cases.
854
        transport = self.get_transport()
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
855
        
856
        # disabled because some transports might normalize urls in generating
857
        # the abspath - eg http+pycurl-> just http -- mbp 20060308 
1530.1.3 by Robert Collins
transport implementations now tested consistently.
858
        self.assertEqual(transport.base + 'relpath',
859
                         transport.abspath('relpath'))
860
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
861
    def test_abspath_at_root(self):
862
        t = self.get_transport()
863
        # clone all the way to the top
864
        new_transport = t.clone('..')
865
        while new_transport.base != t.base:
866
            t = new_transport
867
            new_transport = t.clone('..')
868
        # we must be able to get a abspath of the root when we ask for
869
        # t.abspath('..') - this due to our choice that clone('..')
870
        # should return the root from the root, combined with the desire that
871
        # the url from clone('..') and from abspath('..') should be the same.
872
        self.assertEqual(t.base, t.abspath('..'))
873
        # '' should give us the root
874
        self.assertEqual(t.base, t.abspath(''))
875
        # and a path should append to the url
876
        self.assertEqual(t.base + 'foo', t.abspath('foo'))
877
1530.1.3 by Robert Collins
transport implementations now tested consistently.
878
    def test_iter_files_recursive(self):
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
879
        transport = self.get_transport()
880
        if not transport.listable():
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
881
            self.assertRaises(TransportNotPossible,
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
882
                              transport.iter_files_recursive)
883
            return
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
884
        self.build_tree(['isolated/',
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
885
                         'isolated/dir/',
886
                         'isolated/dir/foo',
887
                         'isolated/dir/bar',
888
                         'isolated/bar'],
889
                        transport=transport)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
890
        paths = set(transport.iter_files_recursive())
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
891
        # nb the directories are not converted
892
        self.assertEqual(paths,
893
                    set(['isolated/dir/foo',
894
                         'isolated/dir/bar',
895
                         'isolated/bar']))
896
        sub_transport = transport.clone('isolated')
897
        paths = set(sub_transport.iter_files_recursive())
1530.1.3 by Robert Collins
transport implementations now tested consistently.
898
        self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
899
900
    def test_connect_twice_is_same_content(self):
901
        # check that our server (whatever it is) is accessable reliably
902
        # via get_transport and multiple connections share content.
903
        transport = self.get_transport()
904
        if transport.is_readonly():
905
            return
906
        transport.put('foo', StringIO('bar'))
907
        transport2 = self.get_transport()
908
        self.check_transport_contents('bar', transport2, 'foo')
909
        # its base should be usable.
910
        transport2 = bzrlib.transport.get_transport(transport.base)
911
        self.check_transport_contents('bar', transport2, 'foo')
912
913
        # now opening at a relative url should give use a sane result:
914
        transport.mkdir('newdir')
915
        transport2 = bzrlib.transport.get_transport(transport.base + "newdir")
916
        transport2 = transport2.clone('..')
917
        self.check_transport_contents('bar', transport2, 'foo')
918
919
    def test_lock_write(self):
920
        transport = self.get_transport()
921
        if transport.is_readonly():
922
            self.assertRaises(TransportNotPossible, transport.lock_write, 'foo')
923
            return
924
        transport.put('lock', StringIO())
925
        lock = transport.lock_write('lock')
926
        # TODO make this consistent on all platforms:
927
        # self.assertRaises(LockError, transport.lock_write, 'lock')
928
        lock.unlock()
929
930
    def test_lock_read(self):
931
        transport = self.get_transport()
932
        if transport.is_readonly():
933
            file('lock', 'w').close()
934
        else:
935
            transport.put('lock', StringIO())
936
        lock = transport.lock_read('lock')
937
        # TODO make this consistent on all platforms:
938
        # self.assertRaises(LockError, transport.lock_read, 'lock')
939
        lock.unlock()
1594.2.5 by Robert Collins
Readv patch from Johan Rydberg giving knits partial download support.
940
941
    def test_readv(self):
942
        transport = self.get_transport()
943
        if transport.is_readonly():
944
            file('a', 'w').write('0123456789')
945
        else:
946
            transport.put('a', StringIO('01234567890'))
947
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
948
        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.
949
        self.assertEqual(d[0], (0, '0'))
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
950
        self.assertEqual(d[1], (1, '1'))
951
        self.assertEqual(d[2], (3, '34'))
952
        self.assertEqual(d[3], (9, '9'))