/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
509
    def test_delete(self):
510
        # TODO: Test Transport.delete
511
        t = self.get_transport()
512
513
        # Not much to do with a readonly transport
514
        if t.is_readonly():
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
515
            self.assertRaises(TransportNotPossible, t.delete, 'missing')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
516
            return
517
518
        t.put('a', StringIO('a little bit of text\n'))
519
        self.failUnless(t.has('a'))
520
        t.delete('a')
521
        self.failIf(t.has('a'))
522
523
        self.assertRaises(NoSuchFile, t.delete, 'a')
524
525
        t.put('a', StringIO('a text\n'))
526
        t.put('b', StringIO('b text\n'))
527
        t.put('c', StringIO('c text\n'))
528
        self.assertEqual([True, True, True],
529
                list(t.has_multi(['a', 'b', 'c'])))
530
        t.delete_multi(['a', 'c'])
531
        self.assertEqual([False, True, False],
532
                list(t.has_multi(['a', 'b', 'c'])))
533
        self.failIf(t.has('a'))
534
        self.failUnless(t.has('b'))
535
        self.failIf(t.has('c'))
536
537
        self.assertRaises(NoSuchFile,
538
                t.delete_multi, ['a', 'b', 'c'])
539
540
        self.assertRaises(NoSuchFile,
541
                t.delete_multi, iter(['a', 'b', 'c']))
542
543
        t.put('a', StringIO('another a text\n'))
544
        t.put('c', StringIO('another c text\n'))
545
        t.delete_multi(iter(['a', 'b', 'c']))
546
547
        # We should have deleted everything
548
        # SftpServer creates control files in the
549
        # working directory, so we can just do a
550
        # plain "listdir".
551
        # self.assertEqual([], os.listdir('.'))
552
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
553
    def test_rmdir(self):
554
        t = self.get_transport()
555
        # Not much to do with a readonly transport
556
        if t.is_readonly():
557
            self.assertRaises(TransportNotPossible, t.rmdir, 'missing')
558
            return
559
        t.mkdir('adir')
560
        t.mkdir('adir/bdir')
561
        t.rmdir('adir/bdir')
562
        self.assertRaises(NoSuchFile, t.stat, 'adir/bdir')
563
        t.rmdir('adir')
564
        self.assertRaises(NoSuchFile, t.stat, 'adir')
565
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
566
    def test_rmdir_not_empty(self):
567
        """Deleting a non-empty directory raises an exception
568
        
569
        sftp (and possibly others) don't give us a specific "directory not
570
        empty" exception -- we can just see that the operation failed.
571
        """
572
        t = self.get_transport()
573
        if t.is_readonly():
574
            return
575
        t.mkdir('adir')
576
        t.mkdir('adir/bdir')
577
        self.assertRaises(PathError, t.rmdir, 'adir')
578
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
579
    def test_rename_dir_succeeds(self):
580
        t = self.get_transport()
581
        if t.is_readonly():
582
            raise TestSkipped("transport is readonly")
583
        t.mkdir('adir')
584
        t.mkdir('adir/asubdir')
585
        t.rename('adir', 'bdir')
586
        self.assertTrue(t.has('bdir/asubdir'))
587
        self.assertFalse(t.has('adir'))
588
589
    def test_rename_dir_nonempty(self):
590
        """Attempting to replace a nonemtpy directory should fail"""
591
        t = self.get_transport()
592
        if t.is_readonly():
593
            raise TestSkipped("transport is readonly")
594
        t.mkdir('adir')
595
        t.mkdir('adir/asubdir')
596
        t.mkdir('bdir')
597
        t.mkdir('bdir/bsubdir')
598
        self.assertRaises(PathError, t.rename, 'bdir', 'adir')
599
        # nothing was changed so it should still be as before
600
        self.assertTrue(t.has('bdir/bsubdir'))
601
        self.assertFalse(t.has('adir/bdir'))
602
        self.assertFalse(t.has('adir/bsubdir'))
603
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
604
    def test_delete_tree(self):
605
        t = self.get_transport()
606
607
        # Not much to do with a readonly transport
608
        if t.is_readonly():
609
            self.assertRaises(TransportNotPossible, t.delete_tree, 'missing')
610
            return
611
612
        # and does it like listing ?
613
        t.mkdir('adir')
614
        try:
615
            t.delete_tree('adir')
616
        except TransportNotPossible:
617
            # ok, this transport does not support delete_tree
618
            return
619
        
620
        # did it delete that trivial case?
621
        self.assertRaises(NoSuchFile, t.stat, 'adir')
622
623
        self.build_tree(['adir/',
624
                         'adir/file', 
625
                         'adir/subdir/', 
626
                         'adir/subdir/file', 
627
                         'adir/subdir2/',
628
                         'adir/subdir2/file',
629
                         ], transport=t)
630
631
        t.delete_tree('adir')
632
        # adir should be gone now.
633
        self.assertRaises(NoSuchFile, t.stat, 'adir')
634
1530.1.3 by Robert Collins
transport implementations now tested consistently.
635
    def test_move(self):
636
        t = self.get_transport()
637
638
        if t.is_readonly():
639
            return
640
641
        # TODO: I would like to use os.listdir() to
642
        # make sure there are no extra files, but SftpServer
643
        # creates control files in the working directory
644
        # perhaps all of this could be done in a subdirectory
645
646
        t.put('a', StringIO('a first file\n'))
647
        self.assertEquals([True, False], list(t.has_multi(['a', 'b'])))
648
649
        t.move('a', 'b')
650
        self.failUnless(t.has('b'))
651
        self.failIf(t.has('a'))
652
653
        self.check_transport_contents('a first file\n', t, 'b')
654
        self.assertEquals([False, True], list(t.has_multi(['a', 'b'])))
655
656
        # Overwrite a file
657
        t.put('c', StringIO('c this file\n'))
658
        t.move('c', 'b')
659
        self.failIf(t.has('c'))
660
        self.check_transport_contents('c this file\n', t, 'b')
661
662
        # TODO: Try to write a test for atomicity
663
        # TODO: Test moving into a non-existant subdirectory
664
        # TODO: Test Transport.move_multi
665
666
    def test_copy(self):
667
        t = self.get_transport()
668
669
        if t.is_readonly():
670
            return
671
672
        t.put('a', StringIO('a file\n'))
673
        t.copy('a', 'b')
674
        self.check_transport_contents('a file\n', t, 'b')
675
676
        self.assertRaises(NoSuchFile, t.copy, 'c', 'd')
677
        os.mkdir('c')
678
        # What should the assert be if you try to copy a
679
        # file over a directory?
680
        #self.assertRaises(Something, t.copy, 'a', 'c')
681
        t.put('d', StringIO('text in d\n'))
682
        t.copy('d', 'b')
683
        self.check_transport_contents('text in d\n', t, 'b')
684
685
        # TODO: test copy_multi
686
687
    def test_connection_error(self):
688
        """ConnectionError is raised when connection is impossible"""
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
689
        try:
690
            url = self._server.get_bogus_url()
691
        except NotImplementedError:
692
            raise TestSkipped("Transport %s has no bogus URL support." %
693
                              self._server.__class__)
694
        t = bzrlib.transport.get_transport(url)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
695
        try:
696
            t.get('.bzr/branch')
697
        except (ConnectionError, NoSuchFile), e:
698
            pass
699
        except (Exception), e:
700
            self.failIf(True, 'Wrong exception thrown: %s' % e)
701
        else:
702
            self.failIf(True, 'Did not get the expected exception.')
703
704
    def test_stat(self):
705
        # TODO: Test stat, just try once, and if it throws, stop testing
706
        from stat import S_ISDIR, S_ISREG
707
708
        t = self.get_transport()
709
710
        try:
711
            st = t.stat('.')
712
        except TransportNotPossible, e:
713
            # This transport cannot stat
714
            return
715
716
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e']
717
        sizes = [14, 0, 16, 0, 18] 
1551.2.39 by abentley
Fix line endings in tests
718
        self.build_tree(paths, transport=t, line_endings='binary')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
719
720
        for path, size in zip(paths, sizes):
721
            st = t.stat(path)
722
            if path.endswith('/'):
723
                self.failUnless(S_ISDIR(st.st_mode))
724
                # directory sizes are meaningless
725
            else:
726
                self.failUnless(S_ISREG(st.st_mode))
727
                self.assertEqual(size, st.st_size)
728
729
        remote_stats = list(t.stat_multi(paths))
730
        remote_iter_stats = list(t.stat_multi(iter(paths)))
731
732
        self.assertRaises(NoSuchFile, t.stat, 'q')
733
        self.assertRaises(NoSuchFile, t.stat, 'b/a')
734
735
        self.assertListRaises(NoSuchFile, t.stat_multi, ['a', 'c', 'd'])
736
        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.
737
        self.build_tree(['subdir/', 'subdir/file'], transport=t)
738
        subdir = t.clone('subdir')
739
        subdir.stat('./file')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
740
        subdir.stat('.')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
741
742
    def test_list_dir(self):
743
        # TODO: Test list_dir, just try once, and if it throws, stop testing
744
        t = self.get_transport()
745
        
746
        if not t.listable():
747
            self.assertRaises(TransportNotPossible, t.list_dir, '.')
748
            return
749
750
        def sorted_list(d):
751
            l = list(t.list_dir(d))
752
            l.sort()
753
            return l
754
755
        # SftpServer creates control files in the working directory
756
        # so lets move down a directory to avoid those.
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
757
        if not t.is_readonly():
758
            t.mkdir('wd')
759
        else:
760
            os.mkdir('wd')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
761
        t = t.clone('wd')
762
763
        self.assertEqual([], sorted_list(u'.'))
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
764
        # c2 is precisely one letter longer than c here to test that
765
        # suffixing is not confused.
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
766
        if not t.is_readonly():
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
767
            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.
768
        else:
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
769
            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.
770
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
771
        self.assertEqual([u'a', u'b', u'c', u'c2'], sorted_list(u'.'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
772
        self.assertEqual([u'd', u'e'], sorted_list(u'c'))
773
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
774
        if not t.is_readonly():
775
            t.delete('c/d')
776
            t.delete('b')
777
        else:
778
            os.unlink('wd/c/d')
779
            os.unlink('wd/b')
780
            
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
781
        self.assertEqual([u'a', u'c', u'c2'], sorted_list('.'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
782
        self.assertEqual([u'e'], sorted_list(u'c'))
783
784
        self.assertListRaises(NoSuchFile, t.list_dir, 'q')
785
        self.assertListRaises(NoSuchFile, t.list_dir, 'c/f')
786
        self.assertListRaises(NoSuchFile, t.list_dir, 'a')
787
788
    def test_clone(self):
789
        # TODO: Test that clone moves up and down the filesystem
790
        t1 = self.get_transport()
791
792
        self.build_tree(['a', 'b/', 'b/c'], transport=t1)
793
794
        self.failUnless(t1.has('a'))
795
        self.failUnless(t1.has('b/c'))
796
        self.failIf(t1.has('c'))
797
798
        t2 = t1.clone('b')
799
        self.assertEqual(t1.base + 'b/', t2.base)
800
801
        self.failUnless(t2.has('c'))
802
        self.failIf(t2.has('a'))
803
804
        t3 = t2.clone('..')
805
        self.failUnless(t3.has('a'))
806
        self.failIf(t3.has('c'))
807
808
        self.failIf(t1.has('b/d'))
809
        self.failIf(t2.has('d'))
810
        self.failIf(t3.has('b/d'))
811
812
        if t1.is_readonly():
813
            open('b/d', 'wb').write('newfile\n')
814
        else:
815
            t2.put('d', StringIO('newfile\n'))
816
817
        self.failUnless(t1.has('b/d'))
818
        self.failUnless(t2.has('d'))
819
        self.failUnless(t3.has('b/d'))
820
821
    def test_relpath(self):
822
        t = self.get_transport()
823
        self.assertEqual('', t.relpath(t.base))
824
        # base ends with /
825
        self.assertEqual('', t.relpath(t.base[:-1]))
826
        # subdirs which dont exist should still give relpaths.
827
        self.assertEqual('foo', t.relpath(t.base + 'foo'))
828
        # trailing slash should be the same.
829
        self.assertEqual('foo', t.relpath(t.base + 'foo/'))
830
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
831
    def test_relpath_at_root(self):
832
        t = self.get_transport()
833
        # clone all the way to the top
834
        new_transport = t.clone('..')
835
        while new_transport.base != t.base:
836
            t = new_transport
837
            new_transport = t.clone('..')
838
        # we must be able to get a relpath below the root
839
        self.assertEqual('', t.relpath(t.base))
840
        # and a deeper one should work too
841
        self.assertEqual('foo/bar', t.relpath(t.base + 'foo/bar'))
842
1530.1.3 by Robert Collins
transport implementations now tested consistently.
843
    def test_abspath(self):
844
        # smoke test for abspath. Corner cases for backends like unix fs's
845
        # that have aliasing problems like symlinks should go in backend
846
        # specific test cases.
847
        transport = self.get_transport()
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
848
        
849
        # disabled because some transports might normalize urls in generating
850
        # the abspath - eg http+pycurl-> just http -- mbp 20060308 
1530.1.3 by Robert Collins
transport implementations now tested consistently.
851
        self.assertEqual(transport.base + 'relpath',
852
                         transport.abspath('relpath'))
853
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
854
    def test_abspath_at_root(self):
855
        t = self.get_transport()
856
        # clone all the way to the top
857
        new_transport = t.clone('..')
858
        while new_transport.base != t.base:
859
            t = new_transport
860
            new_transport = t.clone('..')
861
        # we must be able to get a abspath of the root when we ask for
862
        # t.abspath('..') - this due to our choice that clone('..')
863
        # should return the root from the root, combined with the desire that
864
        # the url from clone('..') and from abspath('..') should be the same.
865
        self.assertEqual(t.base, t.abspath('..'))
866
        # '' should give us the root
867
        self.assertEqual(t.base, t.abspath(''))
868
        # and a path should append to the url
869
        self.assertEqual(t.base + 'foo', t.abspath('foo'))
870
1530.1.3 by Robert Collins
transport implementations now tested consistently.
871
    def test_iter_files_recursive(self):
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
872
        transport = self.get_transport()
873
        if not transport.listable():
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
874
            self.assertRaises(TransportNotPossible,
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
875
                              transport.iter_files_recursive)
876
            return
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
877
        self.build_tree(['isolated/',
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
878
                         'isolated/dir/',
879
                         'isolated/dir/foo',
880
                         'isolated/dir/bar',
881
                         'isolated/bar'],
882
                        transport=transport)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
883
        paths = set(transport.iter_files_recursive())
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
884
        # nb the directories are not converted
885
        self.assertEqual(paths,
886
                    set(['isolated/dir/foo',
887
                         'isolated/dir/bar',
888
                         'isolated/bar']))
889
        sub_transport = transport.clone('isolated')
890
        paths = set(sub_transport.iter_files_recursive())
1530.1.3 by Robert Collins
transport implementations now tested consistently.
891
        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.
892
893
    def test_connect_twice_is_same_content(self):
894
        # check that our server (whatever it is) is accessable reliably
895
        # via get_transport and multiple connections share content.
896
        transport = self.get_transport()
897
        if transport.is_readonly():
898
            return
899
        transport.put('foo', StringIO('bar'))
900
        transport2 = self.get_transport()
901
        self.check_transport_contents('bar', transport2, 'foo')
902
        # its base should be usable.
903
        transport2 = bzrlib.transport.get_transport(transport.base)
904
        self.check_transport_contents('bar', transport2, 'foo')
905
906
        # now opening at a relative url should give use a sane result:
907
        transport.mkdir('newdir')
908
        transport2 = bzrlib.transport.get_transport(transport.base + "newdir")
909
        transport2 = transport2.clone('..')
910
        self.check_transport_contents('bar', transport2, 'foo')
911
912
    def test_lock_write(self):
913
        transport = self.get_transport()
914
        if transport.is_readonly():
915
            self.assertRaises(TransportNotPossible, transport.lock_write, 'foo')
916
            return
917
        transport.put('lock', StringIO())
918
        lock = transport.lock_write('lock')
919
        # TODO make this consistent on all platforms:
920
        # self.assertRaises(LockError, transport.lock_write, 'lock')
921
        lock.unlock()
922
923
    def test_lock_read(self):
924
        transport = self.get_transport()
925
        if transport.is_readonly():
926
            file('lock', 'w').close()
927
        else:
928
            transport.put('lock', StringIO())
929
        lock = transport.lock_read('lock')
930
        # TODO make this consistent on all platforms:
931
        # self.assertRaises(LockError, transport.lock_read, 'lock')
932
        lock.unlock()
1594.2.5 by Robert Collins
Readv patch from Johan Rydberg giving knits partial download support.
933
934
    def test_readv(self):
935
        transport = self.get_transport()
936
        if transport.is_readonly():
937
            file('a', 'w').write('0123456789')
938
        else:
939
            transport.put('a', StringIO('01234567890'))
940
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
941
        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.
942
        self.assertEqual(d[0], (0, '0'))
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
943
        self.assertEqual(d[1], (1, '1'))
944
        self.assertEqual(d[2], (3, '34'))
945
        self.assertEqual(d[3], (9, '9'))