/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
                    ]
135
        self.build_tree(files, transport=t)
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
250
        # Test mkdir with a mode
251
        t.mkdir('dmode755', mode=0755)
1530.1.21 by Robert Collins
Review feedback fixes.
252
        self.assertTransportMode(t, 'dmode755', 0755)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
253
        t.mkdir('dmode555', mode=0555)
1530.1.21 by Robert Collins
Review feedback fixes.
254
        self.assertTransportMode(t, 'dmode555', 0555)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
255
        t.mkdir('dmode777', mode=0777)
1530.1.21 by Robert Collins
Review feedback fixes.
256
        self.assertTransportMode(t, 'dmode777', 0777)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
257
        t.mkdir('dmode700', mode=0700)
1530.1.21 by Robert Collins
Review feedback fixes.
258
        self.assertTransportMode(t, 'dmode700', 0700)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
259
        # TODO: jam 20051215 test mkdir_multi with a mode
260
        t.mkdir_multi(['mdmode755'], mode=0755)
1530.1.21 by Robert Collins
Review feedback fixes.
261
        self.assertTransportMode(t, 'mdmode755', 0755)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
262
1530.1.3 by Robert Collins
transport implementations now tested consistently.
263
    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.
264
        # FIXME: test:   same server to same server (partly done)
265
        # same protocol two servers
266
        # and    different protocols (done for now except for MemoryTransport.
267
        # - RBC 20060122
1530.1.3 by Robert Collins
transport implementations now tested consistently.
268
        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.
269
270
        def simple_copy_files(transport_from, transport_to):
271
            files = ['a', 'b', 'c', 'd']
272
            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.
273
            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.
274
            for f in files:
275
                self.check_transport_contents(transport_to.get(f).read(),
276
                                              transport_from, f)
277
1530.1.3 by Robert Collins
transport implementations now tested consistently.
278
        t = self.get_transport()
279
        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.
280
        simple_copy_files(t, temp_transport)
281
        if not t.is_readonly():
282
            t.mkdir('copy_to_simple')
283
            t2 = t.clone('copy_to_simple')
284
            simple_copy_files(t, t2)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
285
286
287
        # Test that copying into a missing directory raises
288
        # NoSuchFile
289
        if t.is_readonly():
1530.1.21 by Robert Collins
Review feedback fixes.
290
            self.build_tree(['e/', 'e/f'])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
291
        else:
292
            t.mkdir('e')
293
            t.put('e/f', StringIO('contents of e'))
294
        self.assertRaises(NoSuchFile, t.copy_to, ['e/f'], temp_transport)
295
        temp_transport.mkdir('e')
296
        t.copy_to(['e/f'], temp_transport)
297
298
        del temp_transport
299
        temp_transport = MemoryTransport('memory:/')
300
301
        files = ['a', 'b', 'c', 'd']
302
        t.copy_to(iter(files), temp_transport)
303
        for f in files:
304
            self.check_transport_contents(temp_transport.get(f).read(),
305
                                          t, f)
306
        del temp_transport
307
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
308
        for mode in (0666, 0644, 0600, 0400):
309
            temp_transport = MemoryTransport("memory:/")
310
            t.copy_to(files, temp_transport, mode=mode)
311
            for f in files:
1530.1.21 by Robert Collins
Review feedback fixes.
312
                self.assertTransportMode(temp_transport, f, mode)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
313
1530.1.3 by Robert Collins
transport implementations now tested consistently.
314
    def test_append(self):
315
        t = self.get_transport()
316
317
        if t.is_readonly():
318
            open('a', 'wb').write('diff\ncontents for\na\n')
319
            open('b', 'wb').write('contents\nfor b\n')
320
        else:
321
            t.put_multi([
322
                    ('a', StringIO('diff\ncontents for\na\n')),
323
                    ('b', StringIO('contents\nfor b\n'))
324
                    ])
325
326
        if t.is_readonly():
327
            self.assertRaises(TransportNotPossible,
328
                    t.append, 'a', 'add\nsome\nmore\ncontents\n')
329
            _append('a', StringIO('add\nsome\nmore\ncontents\n'))
330
        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.
331
            self.assertEqual(20,
332
                t.append('a', StringIO('add\nsome\nmore\ncontents\n')))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
333
334
        self.check_transport_contents(
335
            'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
336
            t, 'a')
337
338
        if t.is_readonly():
339
            self.assertRaises(TransportNotPossible,
340
                    t.append_multi,
341
                        [('a', 'and\nthen\nsome\nmore\n'),
342
                         ('b', 'some\nmore\nfor\nb\n')])
343
            _append('a', StringIO('and\nthen\nsome\nmore\n'))
344
            _append('b', StringIO('some\nmore\nfor\nb\n'))
345
        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.
346
            self.assertEqual((43, 15), 
347
                t.append_multi([('a', StringIO('and\nthen\nsome\nmore\n')),
348
                                ('b', StringIO('some\nmore\nfor\nb\n'))]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
349
        self.check_transport_contents(
350
            'diff\ncontents for\na\n'
351
            'add\nsome\nmore\ncontents\n'
352
            'and\nthen\nsome\nmore\n',
353
            t, 'a')
354
        self.check_transport_contents(
355
                'contents\nfor b\n'
356
                'some\nmore\nfor\nb\n',
357
                t, 'b')
358
359
        if t.is_readonly():
360
            _append('a', StringIO('a little bit more\n'))
361
            _append('b', StringIO('from an iterator\n'))
362
        else:
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
363
            self.assertEqual((62, 31),
364
                t.append_multi(iter([('a', StringIO('a little bit more\n')),
365
                                     ('b', StringIO('from an iterator\n'))])))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
366
        self.check_transport_contents(
367
            'diff\ncontents for\na\n'
368
            'add\nsome\nmore\ncontents\n'
369
            'and\nthen\nsome\nmore\n'
370
            'a little bit more\n',
371
            t, 'a')
372
        self.check_transport_contents(
373
                'contents\nfor b\n'
374
                'some\nmore\nfor\nb\n'
375
                'from an iterator\n',
376
                t, 'b')
377
378
        if t.is_readonly():
379
            _append('c', StringIO('some text\nfor a missing file\n'))
380
            _append('a', StringIO('some text in a\n'))
381
            _append('d', StringIO('missing file r\n'))
382
        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.
383
            self.assertEqual(0,
384
                t.append('c', StringIO('some text\nfor a missing file\n')))
385
            self.assertEqual((80, 0),
386
                t.append_multi([('a', StringIO('some text in a\n')),
387
                                ('d', StringIO('missing file r\n'))]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
388
        self.check_transport_contents(
389
            'diff\ncontents for\na\n'
390
            'add\nsome\nmore\ncontents\n'
391
            'and\nthen\nsome\nmore\n'
392
            'a little bit more\n'
393
            'some text in a\n',
394
            t, 'a')
395
        self.check_transport_contents('some text\nfor a missing file\n',
396
                                      t, 'c')
397
        self.check_transport_contents('missing file r\n', t, 'd')
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
398
        
399
        # a file with no parent should fail..
400
        if not t.is_readonly():
401
            self.assertRaises(NoSuchFile,
402
                              t.append, 'missing/path', 
403
                              StringIO('content'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
404
405
    def test_append_file(self):
406
        t = self.get_transport()
407
408
        contents = [
409
            ('f1', StringIO('this is a string\nand some more stuff\n')),
410
            ('f2', StringIO('here is some text\nand a bit more\n')),
411
            ('f3', StringIO('some text for the\nthird file created\n')),
412
            ('f4', StringIO('this is a string\nand some more stuff\n')),
413
            ('f5', StringIO('here is some text\nand a bit more\n')),
414
            ('f6', StringIO('some text for the\nthird file created\n'))
415
        ]
416
        
417
        if t.is_readonly():
418
            for f, val in contents:
419
                open(f, 'wb').write(val.read())
420
        else:
421
            t.put_multi(contents)
422
423
        a1 = StringIO('appending to\none\n')
424
        if t.is_readonly():
425
            _append('f1', a1)
426
        else:
427
            t.append('f1', a1)
428
429
        del a1
430
431
        self.check_transport_contents(
432
                'this is a string\nand some more stuff\n'
433
                'appending to\none\n',
434
                t, 'f1')
435
436
        a2 = StringIO('adding more\ntext to two\n')
437
        a3 = StringIO('some garbage\nto put in three\n')
438
439
        if t.is_readonly():
440
            _append('f2', a2)
441
            _append('f3', a3)
442
        else:
443
            t.append_multi([('f2', a2), ('f3', a3)])
444
445
        del a2, a3
446
447
        self.check_transport_contents(
448
                'here is some text\nand a bit more\n'
449
                'adding more\ntext to two\n',
450
                t, 'f2')
451
        self.check_transport_contents( 
452
                'some text for the\nthird file created\n'
453
                'some garbage\nto put in three\n',
454
                t, 'f3')
455
456
        # Test that an actual file object can be used with put
457
        a4 = t.get('f1')
458
        if t.is_readonly():
459
            _append('f4', a4)
460
        else:
461
            t.append('f4', a4)
462
463
        del a4
464
465
        self.check_transport_contents(
466
                'this is a string\nand some more stuff\n'
467
                'this is a string\nand some more stuff\n'
468
                'appending to\none\n',
469
                t, 'f4')
470
471
        a5 = t.get('f2')
472
        a6 = t.get('f3')
473
        if t.is_readonly():
474
            _append('f5', a5)
475
            _append('f6', a6)
476
        else:
477
            t.append_multi([('f5', a5), ('f6', a6)])
478
479
        del a5, a6
480
481
        self.check_transport_contents(
482
                'here is some text\nand a bit more\n'
483
                'here is some text\nand a bit more\n'
484
                'adding more\ntext to two\n',
485
                t, 'f5')
486
        self.check_transport_contents(
487
                'some text for the\nthird file created\n'
488
                'some text for the\nthird file created\n'
489
                'some garbage\nto put in three\n',
490
                t, 'f6')
491
492
        a5 = t.get('f2')
493
        a6 = t.get('f2')
494
        a7 = t.get('f3')
495
        if t.is_readonly():
496
            _append('c', a5)
497
            _append('a', a6)
498
            _append('d', a7)
499
        else:
500
            t.append('c', a5)
501
            t.append_multi([('a', a6), ('d', a7)])
502
        del a5, a6, a7
503
        self.check_transport_contents(t.get('f2').read(), t, 'c')
504
        self.check_transport_contents(t.get('f3').read(), t, 'd')
505
506
    def test_delete(self):
507
        # TODO: Test Transport.delete
508
        t = self.get_transport()
509
510
        # Not much to do with a readonly transport
511
        if t.is_readonly():
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
512
            self.assertRaises(TransportNotPossible, t.delete, 'missing')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
513
            return
514
515
        t.put('a', StringIO('a little bit of text\n'))
516
        self.failUnless(t.has('a'))
517
        t.delete('a')
518
        self.failIf(t.has('a'))
519
520
        self.assertRaises(NoSuchFile, t.delete, 'a')
521
522
        t.put('a', StringIO('a text\n'))
523
        t.put('b', StringIO('b text\n'))
524
        t.put('c', StringIO('c text\n'))
525
        self.assertEqual([True, True, True],
526
                list(t.has_multi(['a', 'b', 'c'])))
527
        t.delete_multi(['a', 'c'])
528
        self.assertEqual([False, True, False],
529
                list(t.has_multi(['a', 'b', 'c'])))
530
        self.failIf(t.has('a'))
531
        self.failUnless(t.has('b'))
532
        self.failIf(t.has('c'))
533
534
        self.assertRaises(NoSuchFile,
535
                t.delete_multi, ['a', 'b', 'c'])
536
537
        self.assertRaises(NoSuchFile,
538
                t.delete_multi, iter(['a', 'b', 'c']))
539
540
        t.put('a', StringIO('another a text\n'))
541
        t.put('c', StringIO('another c text\n'))
542
        t.delete_multi(iter(['a', 'b', 'c']))
543
544
        # We should have deleted everything
545
        # SftpServer creates control files in the
546
        # working directory, so we can just do a
547
        # plain "listdir".
548
        # self.assertEqual([], os.listdir('.'))
549
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
550
    def test_rmdir(self):
551
        t = self.get_transport()
552
        # Not much to do with a readonly transport
553
        if t.is_readonly():
554
            self.assertRaises(TransportNotPossible, t.rmdir, 'missing')
555
            return
556
        t.mkdir('adir')
557
        t.mkdir('adir/bdir')
558
        t.rmdir('adir/bdir')
559
        self.assertRaises(NoSuchFile, t.stat, 'adir/bdir')
560
        t.rmdir('adir')
561
        self.assertRaises(NoSuchFile, t.stat, 'adir')
562
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
563
    def test_rmdir_not_empty(self):
564
        """Deleting a non-empty directory raises an exception
565
        
566
        sftp (and possibly others) don't give us a specific "directory not
567
        empty" exception -- we can just see that the operation failed.
568
        """
569
        t = self.get_transport()
570
        if t.is_readonly():
571
            return
572
        t.mkdir('adir')
573
        t.mkdir('adir/bdir')
574
        self.assertRaises(PathError, t.rmdir, 'adir')
575
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
576
    def test_rename_dir_succeeds(self):
577
        t = self.get_transport()
578
        if t.is_readonly():
579
            raise TestSkipped("transport is readonly")
580
        t.mkdir('adir')
581
        t.mkdir('adir/asubdir')
582
        t.rename('adir', 'bdir')
583
        self.assertTrue(t.has('bdir/asubdir'))
584
        self.assertFalse(t.has('adir'))
585
586
    def test_rename_dir_nonempty(self):
587
        """Attempting to replace a nonemtpy directory should fail"""
588
        t = self.get_transport()
589
        if t.is_readonly():
590
            raise TestSkipped("transport is readonly")
591
        t.mkdir('adir')
592
        t.mkdir('adir/asubdir')
593
        t.mkdir('bdir')
594
        t.mkdir('bdir/bsubdir')
595
        self.assertRaises(PathError, t.rename, 'bdir', 'adir')
596
        # nothing was changed so it should still be as before
597
        self.assertTrue(t.has('bdir/bsubdir'))
598
        self.assertFalse(t.has('adir/bdir'))
599
        self.assertFalse(t.has('adir/bsubdir'))
600
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
601
    def test_delete_tree(self):
602
        t = self.get_transport()
603
604
        # Not much to do with a readonly transport
605
        if t.is_readonly():
606
            self.assertRaises(TransportNotPossible, t.delete_tree, 'missing')
607
            return
608
609
        # and does it like listing ?
610
        t.mkdir('adir')
611
        try:
612
            t.delete_tree('adir')
613
        except TransportNotPossible:
614
            # ok, this transport does not support delete_tree
615
            return
616
        
617
        # did it delete that trivial case?
618
        self.assertRaises(NoSuchFile, t.stat, 'adir')
619
620
        self.build_tree(['adir/',
621
                         'adir/file', 
622
                         'adir/subdir/', 
623
                         'adir/subdir/file', 
624
                         'adir/subdir2/',
625
                         'adir/subdir2/file',
626
                         ], transport=t)
627
628
        t.delete_tree('adir')
629
        # adir should be gone now.
630
        self.assertRaises(NoSuchFile, t.stat, 'adir')
631
1530.1.3 by Robert Collins
transport implementations now tested consistently.
632
    def test_move(self):
633
        t = self.get_transport()
634
635
        if t.is_readonly():
636
            return
637
638
        # TODO: I would like to use os.listdir() to
639
        # make sure there are no extra files, but SftpServer
640
        # creates control files in the working directory
641
        # perhaps all of this could be done in a subdirectory
642
643
        t.put('a', StringIO('a first file\n'))
644
        self.assertEquals([True, False], list(t.has_multi(['a', 'b'])))
645
646
        t.move('a', 'b')
647
        self.failUnless(t.has('b'))
648
        self.failIf(t.has('a'))
649
650
        self.check_transport_contents('a first file\n', t, 'b')
651
        self.assertEquals([False, True], list(t.has_multi(['a', 'b'])))
652
653
        # Overwrite a file
654
        t.put('c', StringIO('c this file\n'))
655
        t.move('c', 'b')
656
        self.failIf(t.has('c'))
657
        self.check_transport_contents('c this file\n', t, 'b')
658
659
        # TODO: Try to write a test for atomicity
660
        # TODO: Test moving into a non-existant subdirectory
661
        # TODO: Test Transport.move_multi
662
663
    def test_copy(self):
664
        t = self.get_transport()
665
666
        if t.is_readonly():
667
            return
668
669
        t.put('a', StringIO('a file\n'))
670
        t.copy('a', 'b')
671
        self.check_transport_contents('a file\n', t, 'b')
672
673
        self.assertRaises(NoSuchFile, t.copy, 'c', 'd')
674
        os.mkdir('c')
675
        # What should the assert be if you try to copy a
676
        # file over a directory?
677
        #self.assertRaises(Something, t.copy, 'a', 'c')
678
        t.put('d', StringIO('text in d\n'))
679
        t.copy('d', 'b')
680
        self.check_transport_contents('text in d\n', t, 'b')
681
682
        # TODO: test copy_multi
683
684
    def test_connection_error(self):
685
        """ConnectionError is raised when connection is impossible"""
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
686
        try:
687
            url = self._server.get_bogus_url()
688
        except NotImplementedError:
689
            raise TestSkipped("Transport %s has no bogus URL support." %
690
                              self._server.__class__)
691
        t = bzrlib.transport.get_transport(url)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
692
        try:
693
            t.get('.bzr/branch')
694
        except (ConnectionError, NoSuchFile), e:
695
            pass
696
        except (Exception), e:
697
            self.failIf(True, 'Wrong exception thrown: %s' % e)
698
        else:
699
            self.failIf(True, 'Did not get the expected exception.')
700
701
    def test_stat(self):
702
        # TODO: Test stat, just try once, and if it throws, stop testing
703
        from stat import S_ISDIR, S_ISREG
704
705
        t = self.get_transport()
706
707
        try:
708
            st = t.stat('.')
709
        except TransportNotPossible, e:
710
            # This transport cannot stat
711
            return
712
713
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e']
714
        sizes = [14, 0, 16, 0, 18] 
715
        self.build_tree(paths, transport=t)
716
717
        for path, size in zip(paths, sizes):
718
            st = t.stat(path)
719
            if path.endswith('/'):
720
                self.failUnless(S_ISDIR(st.st_mode))
721
                # directory sizes are meaningless
722
            else:
723
                self.failUnless(S_ISREG(st.st_mode))
724
                self.assertEqual(size, st.st_size)
725
726
        remote_stats = list(t.stat_multi(paths))
727
        remote_iter_stats = list(t.stat_multi(iter(paths)))
728
729
        self.assertRaises(NoSuchFile, t.stat, 'q')
730
        self.assertRaises(NoSuchFile, t.stat, 'b/a')
731
732
        self.assertListRaises(NoSuchFile, t.stat_multi, ['a', 'c', 'd'])
733
        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.
734
        self.build_tree(['subdir/', 'subdir/file'], transport=t)
735
        subdir = t.clone('subdir')
736
        subdir.stat('./file')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
737
        subdir.stat('.')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
738
739
    def test_list_dir(self):
740
        # TODO: Test list_dir, just try once, and if it throws, stop testing
741
        t = self.get_transport()
742
        
743
        if not t.listable():
744
            self.assertRaises(TransportNotPossible, t.list_dir, '.')
745
            return
746
747
        def sorted_list(d):
748
            l = list(t.list_dir(d))
749
            l.sort()
750
            return l
751
752
        # SftpServer creates control files in the working directory
753
        # so lets move down a directory to avoid those.
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
754
        if not t.is_readonly():
755
            t.mkdir('wd')
756
        else:
757
            os.mkdir('wd')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
758
        t = t.clone('wd')
759
760
        self.assertEqual([], sorted_list(u'.'))
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
761
        # c2 is precisely one letter longer than c here to test that
762
        # suffixing is not confused.
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
763
        if not t.is_readonly():
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
764
            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.
765
        else:
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
766
            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.
767
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
768
        self.assertEqual([u'a', u'b', u'c', u'c2'], sorted_list(u'.'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
769
        self.assertEqual([u'd', u'e'], sorted_list(u'c'))
770
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
771
        if not t.is_readonly():
772
            t.delete('c/d')
773
            t.delete('b')
774
        else:
775
            os.unlink('wd/c/d')
776
            os.unlink('wd/b')
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'c', u'c2'], sorted_list('.'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
779
        self.assertEqual([u'e'], sorted_list(u'c'))
780
781
        self.assertListRaises(NoSuchFile, t.list_dir, 'q')
782
        self.assertListRaises(NoSuchFile, t.list_dir, 'c/f')
783
        self.assertListRaises(NoSuchFile, t.list_dir, 'a')
784
785
    def test_clone(self):
786
        # TODO: Test that clone moves up and down the filesystem
787
        t1 = self.get_transport()
788
789
        self.build_tree(['a', 'b/', 'b/c'], transport=t1)
790
791
        self.failUnless(t1.has('a'))
792
        self.failUnless(t1.has('b/c'))
793
        self.failIf(t1.has('c'))
794
795
        t2 = t1.clone('b')
796
        self.assertEqual(t1.base + 'b/', t2.base)
797
798
        self.failUnless(t2.has('c'))
799
        self.failIf(t2.has('a'))
800
801
        t3 = t2.clone('..')
802
        self.failUnless(t3.has('a'))
803
        self.failIf(t3.has('c'))
804
805
        self.failIf(t1.has('b/d'))
806
        self.failIf(t2.has('d'))
807
        self.failIf(t3.has('b/d'))
808
809
        if t1.is_readonly():
810
            open('b/d', 'wb').write('newfile\n')
811
        else:
812
            t2.put('d', StringIO('newfile\n'))
813
814
        self.failUnless(t1.has('b/d'))
815
        self.failUnless(t2.has('d'))
816
        self.failUnless(t3.has('b/d'))
817
818
    def test_relpath(self):
819
        t = self.get_transport()
820
        self.assertEqual('', t.relpath(t.base))
821
        # base ends with /
822
        self.assertEqual('', t.relpath(t.base[:-1]))
823
        # subdirs which dont exist should still give relpaths.
824
        self.assertEqual('foo', t.relpath(t.base + 'foo'))
825
        # trailing slash should be the same.
826
        self.assertEqual('foo', t.relpath(t.base + 'foo/'))
827
828
    def test_abspath(self):
829
        # smoke test for abspath. Corner cases for backends like unix fs's
830
        # that have aliasing problems like symlinks should go in backend
831
        # specific test cases.
832
        transport = self.get_transport()
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
833
        
834
        # disabled because some transports might normalize urls in generating
835
        # the abspath - eg http+pycurl-> just http -- mbp 20060308 
1530.1.3 by Robert Collins
transport implementations now tested consistently.
836
        self.assertEqual(transport.base + 'relpath',
837
                         transport.abspath('relpath'))
838
839
    def test_iter_files_recursive(self):
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
840
        transport = self.get_transport()
841
        if not transport.listable():
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
842
            self.assertRaises(TransportNotPossible,
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
843
                              transport.iter_files_recursive)
844
            return
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
845
        self.build_tree(['isolated/',
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
846
                         'isolated/dir/',
847
                         'isolated/dir/foo',
848
                         'isolated/dir/bar',
849
                         'isolated/bar'],
850
                        transport=transport)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
851
        paths = set(transport.iter_files_recursive())
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
852
        # nb the directories are not converted
853
        self.assertEqual(paths,
854
                    set(['isolated/dir/foo',
855
                         'isolated/dir/bar',
856
                         'isolated/bar']))
857
        sub_transport = transport.clone('isolated')
858
        paths = set(sub_transport.iter_files_recursive())
1530.1.3 by Robert Collins
transport implementations now tested consistently.
859
        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.
860
861
    def test_connect_twice_is_same_content(self):
862
        # check that our server (whatever it is) is accessable reliably
863
        # via get_transport and multiple connections share content.
864
        transport = self.get_transport()
865
        if transport.is_readonly():
866
            return
867
        transport.put('foo', StringIO('bar'))
868
        transport2 = self.get_transport()
869
        self.check_transport_contents('bar', transport2, 'foo')
870
        # its base should be usable.
871
        transport2 = bzrlib.transport.get_transport(transport.base)
872
        self.check_transport_contents('bar', transport2, 'foo')
873
874
        # now opening at a relative url should give use a sane result:
875
        transport.mkdir('newdir')
876
        transport2 = bzrlib.transport.get_transport(transport.base + "newdir")
877
        transport2 = transport2.clone('..')
878
        self.check_transport_contents('bar', transport2, 'foo')
879
880
    def test_lock_write(self):
881
        transport = self.get_transport()
882
        if transport.is_readonly():
883
            self.assertRaises(TransportNotPossible, transport.lock_write, 'foo')
884
            return
885
        transport.put('lock', StringIO())
886
        lock = transport.lock_write('lock')
887
        # TODO make this consistent on all platforms:
888
        # self.assertRaises(LockError, transport.lock_write, 'lock')
889
        lock.unlock()
890
891
    def test_lock_read(self):
892
        transport = self.get_transport()
893
        if transport.is_readonly():
894
            file('lock', 'w').close()
895
        else:
896
            transport.put('lock', StringIO())
897
        lock = transport.lock_read('lock')
898
        # TODO make this consistent on all platforms:
899
        # self.assertRaises(LockError, transport.lock_read, 'lock')
900
        lock.unlock()
1594.2.5 by Robert Collins
Readv patch from Johan Rydberg giving knits partial download support.
901
902
    def test_readv(self):
903
        transport = self.get_transport()
904
        if transport.is_readonly():
905
            file('a', 'w').write('0123456789')
906
        else:
907
            transport.put('a', StringIO('01234567890'))
908
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
909
        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.
910
        self.assertEqual(d[0], (0, '0'))
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
911
        self.assertEqual(d[1], (1, '1'))
912
        self.assertEqual(d[2], (3, '34'))
913
        self.assertEqual(d[3], (9, '9'))