/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.18.22 by Martin Pool
merge bzr.dev
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1530.1.3 by Robert Collins
transport implementations now tested consistently.
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1530.1.3 by Robert Collins
transport implementations now tested consistently.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1530.1.3 by Robert Collins
transport implementations now tested consistently.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for Transport implementations.
18
19
Transport implementations tested here are supplied by
20
TransportTestProviderAdapter.
21
"""
22
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
23
import itertools
1530.1.3 by Robert Collins
transport implementations now tested consistently.
24
import os
25
from cStringIO import StringIO
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
26
from StringIO import StringIO as pyStringIO
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
27
import stat
28
import sys
2553.2.5 by Robert Collins
And overhaul TransportTestProviderAdapter too.
29
import unittest
1530.1.3 by Robert Collins
transport implementations now tested consistently.
30
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
31
from bzrlib import (
2001.3.2 by John Arbash Meinel
Force all transports to raise ShortReadvError if they can
32
    errors,
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
33
    osutils,
34
    urlutils,
35
    )
2400.2.1 by Robert Collins
Split out the improvement to Transport.local_abspath to raise NotLocalURL from the hpss-faster-copy branch. (Martin Pool, Ian Clatworthy)
36
from bzrlib.errors import (ConnectionError,
37
                           DirectoryNotEmpty,
38
                           FileExists,
39
                           InvalidURL,
40
                           LockError,
41
                           NoSuchFile,
42
                           NotLocalUrl,
43
                           PathError,
44
                           TransportNotPossible,
45
                           )
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
46
from bzrlib.osutils import getcwd
2018.5.139 by Andrew Bennetts
Merge from bzr.dev, resolving conflicts.
47
from bzrlib.smart import medium
3010.2.2 by Martin Pool
Add missing import
48
from bzrlib.tests import (
49
    TestCaseInTempDir,
50
    TestScenarioApplier,
51
    TestSkipped,
52
    TestNotApplicable,
53
    )
1871.1.2 by Robert Collins
Reduce code duplication in transport-parameterised tests.
54
from bzrlib.tests.test_transport import TestTransportImplementation
2485.8.25 by Vincent Ladeuil
Separate abspath from _remote_path, the intents are different.
55
from bzrlib.transport import (
56
    ConnectedTransport,
57
    get_transport,
2485.8.50 by Vincent Ladeuil
merge bzr.dev @ 2584 resolving conflicts
58
    _get_transport_modules,
2485.8.25 by Vincent Ladeuil
Separate abspath from _remote_path, the intents are different.
59
    )
60
from bzrlib.transport.memory import MemoryTransport
1530.1.3 by Robert Collins
transport implementations now tested consistently.
61
62
2553.2.5 by Robert Collins
And overhaul TransportTestProviderAdapter too.
63
class TransportTestProviderAdapter(TestScenarioApplier):
64
    """A tool to generate a suite testing all transports for a single test.
65
66
    This is done by copying the test once for each transport and injecting
67
    the transport_class and transport_server classes into each copy. Each copy
68
    is also given a new id() to make it easy to identify.
69
    """
70
71
    def __init__(self):
72
        self.scenarios = self._test_permutations()
73
74
    def get_transport_test_permutations(self, module):
75
        """Get the permutations module wants to have tested."""
76
        if getattr(module, 'get_test_permutations', None) is None:
77
            raise AssertionError("transport module %s doesn't provide get_test_permutations()"
78
                    % module.__name__)
79
            ##warning("transport module %s doesn't provide get_test_permutations()"
80
            ##       % module.__name__)
81
            return []
82
        return module.get_test_permutations()
83
84
    def _test_permutations(self):
85
        """Return a list of the klass, server_factory pairs to test."""
86
        result = []
87
        for module in _get_transport_modules():
88
            try:
89
                permutations = self.get_transport_test_permutations(
90
                    reduce(getattr, (module).split('.')[1:], __import__(module)))
91
                for (klass, server_factory) in permutations:
92
                    scenario = (server_factory.__name__,
93
                        {"transport_class":klass,
94
                         "transport_server":server_factory})
95
                    result.append(scenario)
96
            except errors.DependencyNotPresent, e:
97
                # Continue even if a dependency prevents us 
3221.5.1 by Vincent Ladeuil
Fix bug #137823 by raising UnavailableFeature *after* the fake ftp server
98
                # from adding this test
2553.2.5 by Robert Collins
And overhaul TransportTestProviderAdapter too.
99
                pass
100
        return result
101
102
103
1871.1.2 by Robert Collins
Reduce code duplication in transport-parameterised tests.
104
class TransportTests(TestTransportImplementation):
105
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
106
    def setUp(self):
107
        super(TransportTests, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
108
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
109
1530.1.3 by Robert Collins
transport implementations now tested consistently.
110
    def check_transport_contents(self, content, transport, relpath):
111
        """Check that transport.get(relpath).read() == content."""
1530.1.21 by Robert Collins
Review feedback fixes.
112
        self.assertEqualDiff(content, transport.get(relpath).read())
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
113
2475.3.2 by John Arbash Meinel
Add Transport.ensure_base()
114
    def test_ensure_base_missing(self):
115
        """.ensure_base() should create the directory if it doesn't exist"""
116
        t = self.get_transport()
117
        t_a = t.clone('a')
118
        if t_a.is_readonly():
119
            self.assertRaises(TransportNotPossible,
120
                              t_a.ensure_base)
121
            return
122
        self.assertTrue(t_a.ensure_base())
123
        self.assertTrue(t.has('a'))
124
125
    def test_ensure_base_exists(self):
126
        """.ensure_base() should just be happy if it already exists"""
127
        t = self.get_transport()
128
        if t.is_readonly():
129
            return
130
131
        t.mkdir('a')
132
        t_a = t.clone('a')
133
        # ensure_base returns False if it didn't create the base
134
        self.assertFalse(t_a.ensure_base())
135
136
    def test_ensure_base_missing_parent(self):
137
        """.ensure_base() will fail if the parent dir doesn't exist"""
138
        t = self.get_transport()
139
        if t.is_readonly():
140
            return
141
142
        t_a = t.clone('a')
143
        t_b = t_a.clone('b')
144
        self.assertRaises(NoSuchFile, t_b.ensure_base)
145
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
146
    def test_external_url(self):
147
        """.external_url either works or raises InProcessTransport."""
148
        t = self.get_transport()
149
        try:
150
            t.external_url()
151
        except errors.InProcessTransport:
152
            pass
153
1530.1.3 by Robert Collins
transport implementations now tested consistently.
154
    def test_has(self):
155
        t = self.get_transport()
156
157
        files = ['a', 'b', 'e', 'g', '%']
158
        self.build_tree(files, transport=t)
159
        self.assertEqual(True, t.has('a'))
160
        self.assertEqual(False, t.has('c'))
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
161
        self.assertEqual(True, t.has(urlutils.escape('%')))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
162
        self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
163
                [True, True, False, False, True, False, True, False])
164
        self.assertEqual(True, t.has_any(['a', 'b', 'c']))
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
165
        self.assertEqual(False, t.has_any(['c', 'd', 'f', urlutils.escape('%%')]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
166
        self.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
167
                [True, True, False, False, True, False, True, False])
168
        self.assertEqual(False, t.has_any(['c', 'c', 'c']))
169
        self.assertEqual(True, t.has_any(['b', 'b', 'b']))
170
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
171
    def test_has_root_works(self):
172
        current_transport = self.get_transport()
173
        self.assertTrue(current_transport.has('/'))
174
        root = current_transport.clone('/')
175
        self.assertTrue(root.has(''))
176
1530.1.3 by Robert Collins
transport implementations now tested consistently.
177
    def test_get(self):
178
        t = self.get_transport()
179
180
        files = ['a', 'b', 'e', 'g']
181
        contents = ['contents of a\n',
182
                    'contents of b\n',
183
                    'contents of e\n',
184
                    'contents of g\n',
185
                    ]
1551.2.39 by abentley
Fix line endings in tests
186
        self.build_tree(files, transport=t, line_endings='binary')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
187
        self.check_transport_contents('contents of a\n', t, 'a')
188
        content_f = t.get_multi(files)
3059.2.18 by Vincent Ladeuil
Take spiv review comments into account.
189
        # Use itertools.izip() instead of use zip() or map(), since they fully
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
190
        # evaluate their inputs, the transport requests should be issued and
191
        # handled sequentially (we don't want to force transport to buffer).
3059.2.10 by Vincent Ladeuil
Jam's review feedback.
192
        for content, f in itertools.izip(contents, content_f):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
193
            self.assertEqual(content, f.read())
194
195
        content_f = t.get_multi(iter(files))
3059.2.18 by Vincent Ladeuil
Take spiv review comments into account.
196
        # Use itertools.izip() for the same reason
3059.2.10 by Vincent Ladeuil
Jam's review feedback.
197
        for content, f in itertools.izip(contents, content_f):
1530.1.3 by Robert Collins
transport implementations now tested consistently.
198
            self.assertEqual(content, f.read())
199
200
        self.assertRaises(NoSuchFile, t.get, 'c')
201
        self.assertListRaises(NoSuchFile, t.get_multi, ['a', 'b', 'c'])
202
        self.assertListRaises(NoSuchFile, t.get_multi, iter(['a', 'b', 'c']))
203
2052.6.2 by Robert Collins
Merge bzr.dev.
204
    def test_get_directory_read_gives_ReadError(self):
205
        """consistent errors for read() on a file returned by get()."""
2052.6.1 by Robert Collins
``Transport.get`` has had its interface made more clear for ease of use.
206
        t = self.get_transport()
207
        if t.is_readonly():
208
            self.build_tree(['a directory/'])
209
        else:
210
            t.mkdir('a%20directory')
211
        # getting the file must either work or fail with a PathError
212
        try:
213
            a_file = t.get('a%20directory')
2052.6.2 by Robert Collins
Merge bzr.dev.
214
        except (errors.PathError, errors.RedirectRequested):
2052.6.1 by Robert Collins
``Transport.get`` has had its interface made more clear for ease of use.
215
            # early failure return immediately.
216
            return
3111.1.23 by Vincent Ladeuil
Make HTTP/1.1 the default implementation reveals one more bug.
217
        # having got a file, read() must either work (i.e. http reading a dir
218
        # listing) or fail with ReadError
2052.6.1 by Robert Collins
``Transport.get`` has had its interface made more clear for ease of use.
219
        try:
220
            a_file.read()
221
        except errors.ReadError:
222
            pass
223
1955.3.3 by John Arbash Meinel
Implement and test 'get_bytes'
224
    def test_get_bytes(self):
225
        t = self.get_transport()
226
227
        files = ['a', 'b', 'e', 'g']
228
        contents = ['contents of a\n',
229
                    'contents of b\n',
230
                    'contents of e\n',
231
                    'contents of g\n',
232
                    ]
233
        self.build_tree(files, transport=t, line_endings='binary')
234
        self.check_transport_contents('contents of a\n', t, 'a')
235
236
        for content, fname in zip(contents, files):
237
            self.assertEqual(content, t.get_bytes(fname))
238
239
        self.assertRaises(NoSuchFile, t.get_bytes, 'c')
240
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
241
    def test_get_with_open_write_stream_sees_all_content(self):
2671.3.4 by Robert Collins
Sync up with open file streams on get/get_bytes.
242
        t = self.get_transport()
243
        if t.is_readonly():
244
            return
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
245
        handle = t.open_write_stream('foo')
2671.3.4 by Robert Collins
Sync up with open file streams on get/get_bytes.
246
        try:
2671.3.6 by Robert Collins
Review feedback.
247
            handle.write('b')
2671.3.4 by Robert Collins
Sync up with open file streams on get/get_bytes.
248
            self.assertEqual('b', t.get('foo').read())
249
        finally:
2671.3.6 by Robert Collins
Review feedback.
250
            handle.close()
2671.3.4 by Robert Collins
Sync up with open file streams on get/get_bytes.
251
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
252
    def test_get_bytes_with_open_write_stream_sees_all_content(self):
2671.3.4 by Robert Collins
Sync up with open file streams on get/get_bytes.
253
        t = self.get_transport()
254
        if t.is_readonly():
255
            return
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
256
        handle = t.open_write_stream('foo')
2671.3.4 by Robert Collins
Sync up with open file streams on get/get_bytes.
257
        try:
2671.3.6 by Robert Collins
Review feedback.
258
            handle.write('b')
2671.3.4 by Robert Collins
Sync up with open file streams on get/get_bytes.
259
            self.assertEqual('b', t.get_bytes('foo'))
260
            self.assertEqual('b', t.get('foo').read())
261
        finally:
2671.3.6 by Robert Collins
Review feedback.
262
            handle.close()
2671.3.4 by Robert Collins
Sync up with open file streams on get/get_bytes.
263
1955.3.1 by John Arbash Meinel
Add put_bytes() and a base-level implementation for it
264
    def test_put_bytes(self):
265
        t = self.get_transport()
266
267
        if t.is_readonly():
268
            self.assertRaises(TransportNotPossible,
269
                    t.put_bytes, 'a', 'some text for a\n')
270
            return
271
272
        t.put_bytes('a', 'some text for a\n')
273
        self.failUnless(t.has('a'))
274
        self.check_transport_contents('some text for a\n', t, 'a')
275
276
        # The contents should be overwritten
277
        t.put_bytes('a', 'new text for a\n')
278
        self.check_transport_contents('new text for a\n', t, 'a')
279
280
        self.assertRaises(NoSuchFile,
281
                          t.put_bytes, 'path/doesnt/exist/c', 'contents')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
282
1955.3.27 by John Arbash Meinel
rename non_atomic_put_* to put_*non_atomic, and re-order the functions
283
    def test_put_bytes_non_atomic(self):
284
        t = self.get_transport()
285
286
        if t.is_readonly():
287
            self.assertRaises(TransportNotPossible,
288
                    t.put_bytes_non_atomic, 'a', 'some text for a\n')
289
            return
290
291
        self.failIf(t.has('a'))
292
        t.put_bytes_non_atomic('a', 'some text for a\n')
293
        self.failUnless(t.has('a'))
294
        self.check_transport_contents('some text for a\n', t, 'a')
295
        # Put also replaces contents
296
        t.put_bytes_non_atomic('a', 'new\ncontents for\na\n')
297
        self.check_transport_contents('new\ncontents for\na\n', t, 'a')
298
299
        # Make sure we can create another file
300
        t.put_bytes_non_atomic('d', 'contents for\nd\n')
301
        # And overwrite 'a' with empty contents
302
        t.put_bytes_non_atomic('a', '')
303
        self.check_transport_contents('contents for\nd\n', t, 'd')
304
        self.check_transport_contents('', t, 'a')
305
306
        self.assertRaises(NoSuchFile, t.put_bytes_non_atomic, 'no/such/path',
307
                                       'contents\n')
308
        # Now test the create_parent flag
309
        self.assertRaises(NoSuchFile, t.put_bytes_non_atomic, 'dir/a',
310
                                       'contents\n')
311
        self.failIf(t.has('dir/a'))
312
        t.put_bytes_non_atomic('dir/a', 'contents for dir/a\n',
313
                               create_parent_dir=True)
314
        self.check_transport_contents('contents for dir/a\n', t, 'dir/a')
315
        
316
        # But we still get NoSuchFile if we can't make the parent dir
317
        self.assertRaises(NoSuchFile, t.put_bytes_non_atomic, 'not/there/a',
318
                                       'contents\n',
319
                                       create_parent_dir=True)
320
321
    def test_put_bytes_permissions(self):
322
        t = self.get_transport()
323
324
        if t.is_readonly():
325
            return
326
        if not t._can_roundtrip_unix_modebits():
327
            # Can't roundtrip, so no need to run this test
328
            return
329
        t.put_bytes('mode644', 'test text\n', mode=0644)
330
        self.assertTransportMode(t, 'mode644', 0644)
331
        t.put_bytes('mode666', 'test text\n', mode=0666)
332
        self.assertTransportMode(t, 'mode666', 0666)
333
        t.put_bytes('mode600', 'test text\n', mode=0600)
334
        self.assertTransportMode(t, 'mode600', 0600)
335
        # Yes, you can put_bytes a file such that it becomes readonly
336
        t.put_bytes('mode400', 'test text\n', mode=0400)
337
        self.assertTransportMode(t, 'mode400', 0400)
338
339
        # The default permissions should be based on the current umask
340
        umask = osutils.get_umask()
341
        t.put_bytes('nomode', 'test text\n', mode=None)
342
        self.assertTransportMode(t, 'nomode', 0666 & ~umask)
343
        
344
    def test_put_bytes_non_atomic_permissions(self):
345
        t = self.get_transport()
346
347
        if t.is_readonly():
348
            return
349
        if not t._can_roundtrip_unix_modebits():
350
            # Can't roundtrip, so no need to run this test
351
            return
352
        t.put_bytes_non_atomic('mode644', 'test text\n', mode=0644)
353
        self.assertTransportMode(t, 'mode644', 0644)
354
        t.put_bytes_non_atomic('mode666', 'test text\n', mode=0666)
355
        self.assertTransportMode(t, 'mode666', 0666)
356
        t.put_bytes_non_atomic('mode600', 'test text\n', mode=0600)
357
        self.assertTransportMode(t, 'mode600', 0600)
358
        t.put_bytes_non_atomic('mode400', 'test text\n', mode=0400)
359
        self.assertTransportMode(t, 'mode400', 0400)
360
361
        # The default permissions should be based on the current umask
362
        umask = osutils.get_umask()
363
        t.put_bytes_non_atomic('nomode', 'test text\n', mode=None)
364
        self.assertTransportMode(t, 'nomode', 0666 & ~umask)
1946.2.12 by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put
365
366
        # We should also be able to set the mode for a parent directory
367
        # when it is created
368
        t.put_bytes_non_atomic('dir700/mode664', 'test text\n', mode=0664,
369
                               dir_mode=0700, create_parent_dir=True)
370
        self.assertTransportMode(t, 'dir700', 0700)
371
        t.put_bytes_non_atomic('dir770/mode664', 'test text\n', mode=0664,
372
                               dir_mode=0770, create_parent_dir=True)
373
        self.assertTransportMode(t, 'dir770', 0770)
374
        t.put_bytes_non_atomic('dir777/mode664', 'test text\n', mode=0664,
375
                               dir_mode=0777, create_parent_dir=True)
376
        self.assertTransportMode(t, 'dir777', 0777)
1955.3.27 by John Arbash Meinel
rename non_atomic_put_* to put_*non_atomic, and re-order the functions
377
        
378
    def test_put_file(self):
379
        t = self.get_transport()
380
381
        if t.is_readonly():
382
            self.assertRaises(TransportNotPossible,
383
                    t.put_file, 'a', StringIO('some text for a\n'))
384
            return
385
2745.5.2 by Robert Collins
* ``bzrlib.transport.Transport.put_file`` now returns the number of bytes
386
        result = t.put_file('a', StringIO('some text for a\n'))
387
        # put_file returns the length of the data written
388
        self.assertEqual(16, result)
1955.3.27 by John Arbash Meinel
rename non_atomic_put_* to put_*non_atomic, and re-order the functions
389
        self.failUnless(t.has('a'))
390
        self.check_transport_contents('some text for a\n', t, 'a')
391
        # Put also replaces contents
2745.5.2 by Robert Collins
* ``bzrlib.transport.Transport.put_file`` now returns the number of bytes
392
        result = t.put_file('a', StringIO('new\ncontents for\na\n'))
393
        self.assertEqual(19, result)
1955.3.27 by John Arbash Meinel
rename non_atomic_put_* to put_*non_atomic, and re-order the functions
394
        self.check_transport_contents('new\ncontents for\na\n', t, 'a')
395
        self.assertRaises(NoSuchFile,
396
                          t.put_file, 'path/doesnt/exist/c',
397
                              StringIO('contents'))
398
399
    def test_put_file_non_atomic(self):
400
        t = self.get_transport()
401
402
        if t.is_readonly():
403
            self.assertRaises(TransportNotPossible,
404
                    t.put_file_non_atomic, 'a', StringIO('some text for a\n'))
405
            return
406
407
        self.failIf(t.has('a'))
408
        t.put_file_non_atomic('a', StringIO('some text for a\n'))
409
        self.failUnless(t.has('a'))
410
        self.check_transport_contents('some text for a\n', t, 'a')
411
        # Put also replaces contents
412
        t.put_file_non_atomic('a', StringIO('new\ncontents for\na\n'))
413
        self.check_transport_contents('new\ncontents for\na\n', t, 'a')
414
415
        # Make sure we can create another file
416
        t.put_file_non_atomic('d', StringIO('contents for\nd\n'))
417
        # And overwrite 'a' with empty contents
418
        t.put_file_non_atomic('a', StringIO(''))
419
        self.check_transport_contents('contents for\nd\n', t, 'd')
420
        self.check_transport_contents('', t, 'a')
421
422
        self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'no/such/path',
423
                                       StringIO('contents\n'))
424
        # Now test the create_parent flag
425
        self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'dir/a',
426
                                       StringIO('contents\n'))
427
        self.failIf(t.has('dir/a'))
428
        t.put_file_non_atomic('dir/a', StringIO('contents for dir/a\n'),
1955.3.20 by John Arbash Meinel
Add non_atomic_put_bytes() and tests for it
429
                              create_parent_dir=True)
1946.1.8 by John Arbash Meinel
Update non_atomic_put to have a create_parent_dir flag
430
        self.check_transport_contents('contents for dir/a\n', t, 'dir/a')
431
        
432
        # But we still get NoSuchFile if we can't make the parent dir
1955.3.27 by John Arbash Meinel
rename non_atomic_put_* to put_*non_atomic, and re-order the functions
433
        self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'not/there/a',
1955.3.20 by John Arbash Meinel
Add non_atomic_put_bytes() and tests for it
434
                                       StringIO('contents\n'),
435
                                       create_parent_dir=True)
436
1955.3.7 by John Arbash Meinel
Fix the deprecation warnings in the transport tests themselves
437
    def test_put_file_permissions(self):
1955.3.18 by John Arbash Meinel
[merge] Transport.non_atomic_put()
438
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
439
        t = self.get_transport()
440
441
        if t.is_readonly():
442
            return
1711.4.32 by John Arbash Meinel
Skip permission tests on win32 no modebits
443
        if not t._can_roundtrip_unix_modebits():
444
            # Can't roundtrip, so no need to run this test
445
            return
1955.3.7 by John Arbash Meinel
Fix the deprecation warnings in the transport tests themselves
446
        t.put_file('mode644', StringIO('test text\n'), mode=0644)
1530.1.21 by Robert Collins
Review feedback fixes.
447
        self.assertTransportMode(t, 'mode644', 0644)
1955.3.7 by John Arbash Meinel
Fix the deprecation warnings in the transport tests themselves
448
        t.put_file('mode666', StringIO('test text\n'), mode=0666)
1530.1.21 by Robert Collins
Review feedback fixes.
449
        self.assertTransportMode(t, 'mode666', 0666)
1955.3.7 by John Arbash Meinel
Fix the deprecation warnings in the transport tests themselves
450
        t.put_file('mode600', StringIO('test text\n'), mode=0600)
1530.1.21 by Robert Collins
Review feedback fixes.
451
        self.assertTransportMode(t, 'mode600', 0600)
1530.1.15 by Robert Collins
Move put mode tests into test_transport_implementation.
452
        # Yes, you can put a file such that it becomes readonly
1955.3.7 by John Arbash Meinel
Fix the deprecation warnings in the transport tests themselves
453
        t.put_file('mode400', StringIO('test text\n'), mode=0400)
1530.1.21 by Robert Collins
Review feedback fixes.
454
        self.assertTransportMode(t, 'mode400', 0400)
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
455
        # The default permissions should be based on the current umask
456
        umask = osutils.get_umask()
1955.3.7 by John Arbash Meinel
Fix the deprecation warnings in the transport tests themselves
457
        t.put_file('nomode', StringIO('test text\n'), mode=None)
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
458
        self.assertTransportMode(t, 'nomode', 0666 & ~umask)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
459
        
1955.3.27 by John Arbash Meinel
rename non_atomic_put_* to put_*non_atomic, and re-order the functions
460
    def test_put_file_non_atomic_permissions(self):
461
        t = self.get_transport()
462
463
        if t.is_readonly():
464
            return
465
        if not t._can_roundtrip_unix_modebits():
466
            # Can't roundtrip, so no need to run this test
467
            return
468
        t.put_file_non_atomic('mode644', StringIO('test text\n'), mode=0644)
469
        self.assertTransportMode(t, 'mode644', 0644)
470
        t.put_file_non_atomic('mode666', StringIO('test text\n'), mode=0666)
471
        self.assertTransportMode(t, 'mode666', 0666)
472
        t.put_file_non_atomic('mode600', StringIO('test text\n'), mode=0600)
473
        self.assertTransportMode(t, 'mode600', 0600)
474
        # Yes, you can put_file_non_atomic a file such that it becomes readonly
475
        t.put_file_non_atomic('mode400', StringIO('test text\n'), mode=0400)
476
        self.assertTransportMode(t, 'mode400', 0400)
477
478
        # The default permissions should be based on the current umask
479
        umask = osutils.get_umask()
480
        t.put_file_non_atomic('nomode', StringIO('test text\n'), mode=None)
481
        self.assertTransportMode(t, 'nomode', 0666 & ~umask)
482
        
1946.2.12 by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put
483
        # We should also be able to set the mode for a parent directory
484
        # when it is created
485
        sio = StringIO()
486
        t.put_file_non_atomic('dir700/mode664', sio, mode=0664,
487
                              dir_mode=0700, create_parent_dir=True)
488
        self.assertTransportMode(t, 'dir700', 0700)
489
        t.put_file_non_atomic('dir770/mode664', sio, mode=0664,
490
                              dir_mode=0770, create_parent_dir=True)
491
        self.assertTransportMode(t, 'dir770', 0770)
492
        t.put_file_non_atomic('dir777/mode664', sio, mode=0664,
493
                              dir_mode=0777, create_parent_dir=True)
494
        self.assertTransportMode(t, 'dir777', 0777)
495
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
496
    def test_put_bytes_unicode(self):
497
        # Expect put_bytes to raise AssertionError or UnicodeEncodeError if
498
        # given unicode "bytes".  UnicodeEncodeError doesn't really make sense
499
        # (we don't want to encode unicode here at all, callers should be
500
        # strictly passing bytes to put_bytes), but we allow it for backwards
501
        # compatibility.  At some point we should use a specific exception.
2414.1.2 by Andrew Bennetts
Deal with review comments.
502
        # See https://bugs.launchpad.net/bzr/+bug/106898.
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
503
        t = self.get_transport()
504
        if t.is_readonly():
505
            return
506
        unicode_string = u'\u1234'
507
        self.assertRaises(
508
            (AssertionError, UnicodeEncodeError),
509
            t.put_bytes, 'foo', unicode_string)
510
511
    def test_put_file_unicode(self):
512
        # Like put_bytes, except with a StringIO.StringIO of a unicode string.
513
        # This situation can happen (and has) if code is careless about the type
514
        # of "string" they initialise/write to a StringIO with.  We cannot use
515
        # cStringIO, because it never returns unicode from read.
516
        # Like put_bytes, UnicodeEncodeError isn't quite the right exception to
517
        # raise, but we raise it for hysterical raisins.
518
        t = self.get_transport()
519
        if t.is_readonly():
520
            return
521
        unicode_file = pyStringIO(u'\u1234')
522
        self.assertRaises(UnicodeEncodeError, t.put_file, 'foo', unicode_file)
523
1530.1.3 by Robert Collins
transport implementations now tested consistently.
524
    def test_mkdir(self):
525
        t = self.get_transport()
526
527
        if t.is_readonly():
528
            # cannot mkdir on readonly transports. We're not testing for 
529
            # cache coherency because cache behaviour is not currently
530
            # defined for the transport interface.
531
            self.assertRaises(TransportNotPossible, t.mkdir, '.')
532
            self.assertRaises(TransportNotPossible, t.mkdir, 'new_dir')
533
            self.assertRaises(TransportNotPossible, t.mkdir_multi, ['new_dir'])
534
            self.assertRaises(TransportNotPossible, t.mkdir, 'path/doesnt/exist')
535
            return
536
        # Test mkdir
537
        t.mkdir('dir_a')
538
        self.assertEqual(t.has('dir_a'), True)
539
        self.assertEqual(t.has('dir_b'), False)
540
541
        t.mkdir('dir_b')
542
        self.assertEqual(t.has('dir_b'), True)
543
544
        t.mkdir_multi(['dir_c', 'dir_d'])
545
546
        t.mkdir_multi(iter(['dir_e', 'dir_f']))
547
        self.assertEqual(list(t.has_multi(
548
            ['dir_a', 'dir_b', 'dir_c', 'dir_q',
549
             'dir_d', 'dir_e', 'dir_f', 'dir_b'])),
550
            [True, True, True, False,
551
             True, True, True, True])
552
553
        # we were testing that a local mkdir followed by a transport
554
        # mkdir failed thusly, but given that we * in one process * do not
555
        # concurrently fiddle with disk dirs and then use transport to do 
556
        # things, the win here seems marginal compared to the constraint on
557
        # the interface. RBC 20051227
558
        t.mkdir('dir_g')
559
        self.assertRaises(FileExists, t.mkdir, 'dir_g')
560
561
        # Test get/put in sub-directories
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
562
        t.put_bytes('dir_a/a', 'contents of dir_a/a')
563
        t.put_file('dir_b/b', StringIO('contents of dir_b/b'))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
564
        self.check_transport_contents('contents of dir_a/a', t, 'dir_a/a')
565
        self.check_transport_contents('contents of dir_b/b', t, 'dir_b/b')
566
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
567
        # mkdir of a dir with an absent parent
568
        self.assertRaises(NoSuchFile, t.mkdir, 'missing/dir')
569
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
570
    def test_mkdir_permissions(self):
571
        t = self.get_transport()
572
        if t.is_readonly():
573
            return
1608.2.7 by Martin Pool
Rename supports_unix_modebits to _can_roundtrip_unix_modebits for clarity
574
        if not t._can_roundtrip_unix_modebits():
1608.2.5 by Martin Pool
Add Transport.supports_unix_modebits, so tests can
575
            # no sense testing on this transport
576
            return
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
577
        # Test mkdir with a mode
578
        t.mkdir('dmode755', mode=0755)
1530.1.21 by Robert Collins
Review feedback fixes.
579
        self.assertTransportMode(t, 'dmode755', 0755)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
580
        t.mkdir('dmode555', mode=0555)
1530.1.21 by Robert Collins
Review feedback fixes.
581
        self.assertTransportMode(t, 'dmode555', 0555)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
582
        t.mkdir('dmode777', mode=0777)
1530.1.21 by Robert Collins
Review feedback fixes.
583
        self.assertTransportMode(t, 'dmode777', 0777)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
584
        t.mkdir('dmode700', mode=0700)
1530.1.21 by Robert Collins
Review feedback fixes.
585
        self.assertTransportMode(t, 'dmode700', 0700)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
586
        t.mkdir_multi(['mdmode755'], mode=0755)
1530.1.21 by Robert Collins
Review feedback fixes.
587
        self.assertTransportMode(t, 'mdmode755', 0755)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
588
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
589
        # Default mode should be based on umask
590
        umask = osutils.get_umask()
591
        t.mkdir('dnomode', mode=None)
592
        self.assertTransportMode(t, 'dnomode', 0777 & ~umask)
593
2671.3.2 by Robert Collins
Start open_file_stream logic.
594
    def test_opening_a_file_stream_creates_file(self):
595
        t = self.get_transport()
596
        if t.is_readonly():
597
            return
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
598
        handle = t.open_write_stream('foo')
2671.3.2 by Robert Collins
Start open_file_stream logic.
599
        try:
600
            self.assertEqual('', t.get_bytes('foo'))
601
        finally:
2671.3.6 by Robert Collins
Review feedback.
602
            handle.close()
2671.3.2 by Robert Collins
Start open_file_stream logic.
603
2671.3.3 by Robert Collins
Add mode parameter to Transport.open_file_stream.
604
    def test_opening_a_file_stream_can_set_mode(self):
605
        t = self.get_transport()
606
        if t.is_readonly():
607
            return
608
        if not t._can_roundtrip_unix_modebits():
609
            # Can't roundtrip, so no need to run this test
610
            return
611
        def check_mode(name, mode, expected):
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
612
            handle = t.open_write_stream(name, mode=mode)
2671.3.6 by Robert Collins
Review feedback.
613
            handle.close()
2671.3.3 by Robert Collins
Add mode parameter to Transport.open_file_stream.
614
            self.assertTransportMode(t, name, expected)
615
        check_mode('mode644', 0644, 0644)
616
        check_mode('mode666', 0666, 0666)
617
        check_mode('mode600', 0600, 0600)
618
        # The default permissions should be based on the current umask
619
        check_mode('nomode', None, 0666 & ~osutils.get_umask())
620
1530.1.3 by Robert Collins
transport implementations now tested consistently.
621
    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.
622
        # FIXME: test:   same server to same server (partly done)
623
        # same protocol two servers
624
        # and    different protocols (done for now except for MemoryTransport.
625
        # - RBC 20060122
626
627
        def simple_copy_files(transport_from, transport_to):
628
            files = ['a', 'b', 'c', 'd']
629
            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.
630
            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.
631
            for f in files:
632
                self.check_transport_contents(transport_to.get(f).read(),
633
                                              transport_from, f)
634
1530.1.3 by Robert Collins
transport implementations now tested consistently.
635
        t = self.get_transport()
1685.1.42 by John Arbash Meinel
A couple more fixes to make sure memory:/// works correctly.
636
        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.
637
        simple_copy_files(t, temp_transport)
638
        if not t.is_readonly():
639
            t.mkdir('copy_to_simple')
640
            t2 = t.clone('copy_to_simple')
641
            simple_copy_files(t, t2)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
642
643
644
        # Test that copying into a missing directory raises
645
        # NoSuchFile
646
        if t.is_readonly():
1530.1.21 by Robert Collins
Review feedback fixes.
647
            self.build_tree(['e/', 'e/f'])
1530.1.3 by Robert Collins
transport implementations now tested consistently.
648
        else:
649
            t.mkdir('e')
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
650
            t.put_bytes('e/f', 'contents of e')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
651
        self.assertRaises(NoSuchFile, t.copy_to, ['e/f'], temp_transport)
652
        temp_transport.mkdir('e')
653
        t.copy_to(['e/f'], temp_transport)
654
655
        del temp_transport
1685.1.42 by John Arbash Meinel
A couple more fixes to make sure memory:/// works correctly.
656
        temp_transport = MemoryTransport('memory:///')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
657
658
        files = ['a', 'b', 'c', 'd']
659
        t.copy_to(iter(files), temp_transport)
660
        for f in files:
661
            self.check_transport_contents(temp_transport.get(f).read(),
662
                                          t, f)
663
        del temp_transport
664
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
665
        for mode in (0666, 0644, 0600, 0400):
1685.1.42 by John Arbash Meinel
A couple more fixes to make sure memory:/// works correctly.
666
            temp_transport = MemoryTransport("memory:///")
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
667
            t.copy_to(files, temp_transport, mode=mode)
668
            for f in files:
1530.1.21 by Robert Collins
Review feedback fixes.
669
                self.assertTransportMode(temp_transport, f, mode)
1530.1.16 by Robert Collins
Move mkdir and copy_to permissions tests to test_transport_impleentation.
670
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
671
    def test_append_file(self):
672
        t = self.get_transport()
673
674
        if t.is_readonly():
1530.1.3 by Robert Collins
transport implementations now tested consistently.
675
            self.assertRaises(TransportNotPossible,
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
676
                    t.append_file, 'a', 'add\nsome\nmore\ncontents\n')
1955.3.2 by John Arbash Meinel
Implement and test 'Transport.append_bytes', cleanup the tests of plain append
677
            return
1955.3.10 by John Arbash Meinel
clean up append, append_bytes, and append_multi tests
678
        t.put_bytes('a', 'diff\ncontents for\na\n')
679
        t.put_bytes('b', 'contents\nfor b\n')
1955.3.2 by John Arbash Meinel
Implement and test 'Transport.append_bytes', cleanup the tests of plain append
680
681
        self.assertEqual(20,
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
682
            t.append_file('a', StringIO('add\nsome\nmore\ncontents\n')))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
683
684
        self.check_transport_contents(
685
            'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
686
            t, 'a')
687
1955.3.10 by John Arbash Meinel
clean up append, append_bytes, and append_multi tests
688
        # a file with no parent should fail..
689
        self.assertRaises(NoSuchFile,
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
690
                          t.append_file, 'missing/path', StringIO('content'))
1955.3.10 by John Arbash Meinel
clean up append, append_bytes, and append_multi tests
691
692
        # And we can create new files, too
693
        self.assertEqual(0,
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
694
            t.append_file('c', StringIO('some text\nfor a missing file\n')))
1955.3.10 by John Arbash Meinel
clean up append, append_bytes, and append_multi tests
695
        self.check_transport_contents('some text\nfor a missing file\n',
696
                                      t, 'c')
697
698
    def test_append_bytes(self):
699
        t = self.get_transport()
700
701
        if t.is_readonly():
702
            self.assertRaises(TransportNotPossible,
703
                    t.append_bytes, 'a', 'add\nsome\nmore\ncontents\n')
704
            return
705
706
        self.assertEqual(0, t.append_bytes('a', 'diff\ncontents for\na\n'))
707
        self.assertEqual(0, t.append_bytes('b', 'contents\nfor b\n'))
708
709
        self.assertEqual(20,
710
            t.append_bytes('a', 'add\nsome\nmore\ncontents\n'))
711
712
        self.check_transport_contents(
713
            'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
714
            t, 'a')
715
716
        # a file with no parent should fail..
717
        self.assertRaises(NoSuchFile,
718
                          t.append_bytes, 'missing/path', 'content')
719
720
    def test_append_multi(self):
721
        t = self.get_transport()
722
723
        if t.is_readonly():
724
            return
725
        t.put_bytes('a', 'diff\ncontents for\na\n'
726
                         'add\nsome\nmore\ncontents\n')
727
        t.put_bytes('b', 'contents\nfor b\n')
728
1955.3.2 by John Arbash Meinel
Implement and test 'Transport.append_bytes', cleanup the tests of plain append
729
        self.assertEqual((43, 15),
730
            t.append_multi([('a', StringIO('and\nthen\nsome\nmore\n')),
731
                            ('b', StringIO('some\nmore\nfor\nb\n'))]))
732
1530.1.3 by Robert Collins
transport implementations now tested consistently.
733
        self.check_transport_contents(
734
            'diff\ncontents for\na\n'
735
            'add\nsome\nmore\ncontents\n'
736
            'and\nthen\nsome\nmore\n',
737
            t, 'a')
738
        self.check_transport_contents(
739
                'contents\nfor b\n'
740
                'some\nmore\nfor\nb\n',
741
                t, 'b')
742
1955.3.2 by John Arbash Meinel
Implement and test 'Transport.append_bytes', cleanup the tests of plain append
743
        self.assertEqual((62, 31),
744
            t.append_multi(iter([('a', StringIO('a little bit more\n')),
745
                                 ('b', StringIO('from an iterator\n'))])))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
746
        self.check_transport_contents(
747
            'diff\ncontents for\na\n'
748
            'add\nsome\nmore\ncontents\n'
749
            'and\nthen\nsome\nmore\n'
750
            'a little bit more\n',
751
            t, 'a')
752
        self.check_transport_contents(
753
                'contents\nfor b\n'
754
                'some\nmore\nfor\nb\n'
755
                'from an iterator\n',
756
                t, 'b')
757
1955.3.2 by John Arbash Meinel
Implement and test 'Transport.append_bytes', cleanup the tests of plain append
758
        self.assertEqual((80, 0),
759
            t.append_multi([('a', StringIO('some text in a\n')),
760
                            ('d', StringIO('missing file r\n'))]))
761
1530.1.3 by Robert Collins
transport implementations now tested consistently.
762
        self.check_transport_contents(
763
            'diff\ncontents for\na\n'
764
            'add\nsome\nmore\ncontents\n'
765
            'and\nthen\nsome\nmore\n'
766
            'a little bit more\n'
767
            'some text in a\n',
768
            t, 'a')
769
        self.check_transport_contents('missing file r\n', t, 'd')
770
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
771
    def test_append_file_mode(self):
1955.3.10 by John Arbash Meinel
clean up append, append_bytes, and append_multi tests
772
        """Check that append accepts a mode parameter"""
1666.1.6 by Robert Collins
Make knit the default format.
773
        # check append accepts a mode
774
        t = self.get_transport()
775
        if t.is_readonly():
1955.3.10 by John Arbash Meinel
clean up append, append_bytes, and append_multi tests
776
            self.assertRaises(TransportNotPossible,
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
777
                t.append_file, 'f', StringIO('f'), mode=None)
1666.1.6 by Robert Collins
Make knit the default format.
778
            return
1955.3.15 by John Arbash Meinel
Deprecate 'Transport.append' in favor of Transport.append_file or Transport.append_bytes
779
        t.append_file('f', StringIO('f'), mode=None)
1666.1.6 by Robert Collins
Make knit the default format.
780
        
1955.3.2 by John Arbash Meinel
Implement and test 'Transport.append_bytes', cleanup the tests of plain append
781
    def test_append_bytes_mode(self):
782
        # check append_bytes accepts a mode
783
        t = self.get_transport()
784
        if t.is_readonly():
1955.3.10 by John Arbash Meinel
clean up append, append_bytes, and append_multi tests
785
            self.assertRaises(TransportNotPossible,
786
                t.append_bytes, 'f', 'f', mode=None)
1955.3.2 by John Arbash Meinel
Implement and test 'Transport.append_bytes', cleanup the tests of plain append
787
            return
1955.3.10 by John Arbash Meinel
clean up append, append_bytes, and append_multi tests
788
        t.append_bytes('f', 'f', mode=None)
1955.3.2 by John Arbash Meinel
Implement and test 'Transport.append_bytes', cleanup the tests of plain append
789
        
1530.1.3 by Robert Collins
transport implementations now tested consistently.
790
    def test_delete(self):
791
        # TODO: Test Transport.delete
792
        t = self.get_transport()
793
794
        # Not much to do with a readonly transport
795
        if t.is_readonly():
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
796
            self.assertRaises(TransportNotPossible, t.delete, 'missing')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
797
            return
798
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
799
        t.put_bytes('a', 'a little bit of text\n')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
800
        self.failUnless(t.has('a'))
801
        t.delete('a')
802
        self.failIf(t.has('a'))
803
804
        self.assertRaises(NoSuchFile, t.delete, 'a')
805
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
806
        t.put_bytes('a', 'a text\n')
807
        t.put_bytes('b', 'b text\n')
808
        t.put_bytes('c', 'c text\n')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
809
        self.assertEqual([True, True, True],
810
                list(t.has_multi(['a', 'b', 'c'])))
811
        t.delete_multi(['a', 'c'])
812
        self.assertEqual([False, True, False],
813
                list(t.has_multi(['a', 'b', 'c'])))
814
        self.failIf(t.has('a'))
815
        self.failUnless(t.has('b'))
816
        self.failIf(t.has('c'))
817
818
        self.assertRaises(NoSuchFile,
819
                t.delete_multi, ['a', 'b', 'c'])
820
821
        self.assertRaises(NoSuchFile,
822
                t.delete_multi, iter(['a', 'b', 'c']))
823
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
824
        t.put_bytes('a', 'another a text\n')
825
        t.put_bytes('c', 'another c text\n')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
826
        t.delete_multi(iter(['a', 'b', 'c']))
827
828
        # We should have deleted everything
829
        # SftpServer creates control files in the
830
        # working directory, so we can just do a
831
        # plain "listdir".
832
        # self.assertEqual([], os.listdir('.'))
833
2671.3.1 by Robert Collins
* New method ``bzrlib.transport.Transport.get_recommended_page_size``.
834
    def test_recommended_page_size(self):
835
        """Transports recommend a page size for partial access to files."""
836
        t = self.get_transport()
837
        self.assertIsInstance(t.recommended_page_size(), int)
838
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
839
    def test_rmdir(self):
840
        t = self.get_transport()
841
        # Not much to do with a readonly transport
842
        if t.is_readonly():
843
            self.assertRaises(TransportNotPossible, t.rmdir, 'missing')
844
            return
845
        t.mkdir('adir')
846
        t.mkdir('adir/bdir')
847
        t.rmdir('adir/bdir')
1948.3.12 by Vincent LADEUIL
Fix Aaron's third review remarks.
848
        # ftp may not be able to raise NoSuchFile for lack of
849
        # details when failing
850
        self.assertRaises((NoSuchFile, PathError), t.rmdir, 'adir/bdir')
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
851
        t.rmdir('adir')
1948.3.12 by Vincent LADEUIL
Fix Aaron's third review remarks.
852
        self.assertRaises((NoSuchFile, PathError), t.rmdir, 'adir')
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
853
1553.5.10 by Martin Pool
New DirectoryNotEmpty exception, and raise this from local and memory
854
    def test_rmdir_not_empty(self):
855
        """Deleting a non-empty directory raises an exception
856
        
857
        sftp (and possibly others) don't give us a specific "directory not
858
        empty" exception -- we can just see that the operation failed.
859
        """
860
        t = self.get_transport()
861
        if t.is_readonly():
862
            return
863
        t.mkdir('adir')
864
        t.mkdir('adir/bdir')
865
        self.assertRaises(PathError, t.rmdir, 'adir')
866
2338.5.1 by Andrew Bennetts
Fix bug in MemoryTransport.rmdir.
867
    def test_rmdir_empty_but_similar_prefix(self):
868
        """rmdir does not get confused by sibling paths.
869
        
870
        A naive implementation of MemoryTransport would refuse to rmdir
871
        ".bzr/branch" if there is a ".bzr/branch-format" directory, because it
872
        uses "path.startswith(dir)" on all file paths to determine if directory
873
        is empty.
874
        """
875
        t = self.get_transport()
876
        if t.is_readonly():
877
            return
878
        t.mkdir('foo')
879
        t.put_bytes('foo-bar', '')
880
        t.mkdir('foo-baz')
881
        t.rmdir('foo')
882
        self.assertRaises((NoSuchFile, PathError), t.rmdir, 'foo')
883
        self.failUnless(t.has('foo-bar'))
884
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
885
    def test_rename_dir_succeeds(self):
886
        t = self.get_transport()
887
        if t.is_readonly():
888
            raise TestSkipped("transport is readonly")
889
        t.mkdir('adir')
890
        t.mkdir('adir/asubdir')
891
        t.rename('adir', 'bdir')
892
        self.assertTrue(t.has('bdir/asubdir'))
893
        self.assertFalse(t.has('adir'))
894
895
    def test_rename_dir_nonempty(self):
896
        """Attempting to replace a nonemtpy directory should fail"""
897
        t = self.get_transport()
898
        if t.is_readonly():
899
            raise TestSkipped("transport is readonly")
900
        t.mkdir('adir')
901
        t.mkdir('adir/asubdir')
902
        t.mkdir('bdir')
903
        t.mkdir('bdir/bsubdir')
1910.7.17 by Andrew Bennetts
Various cosmetic changes.
904
        # any kind of PathError would be OK, though we normally expect
905
        # DirectoryNotEmpty
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
906
        self.assertRaises(PathError, t.rename, 'bdir', 'adir')
907
        # nothing was changed so it should still be as before
908
        self.assertTrue(t.has('bdir/bsubdir'))
909
        self.assertFalse(t.has('adir/bdir'))
910
        self.assertFalse(t.has('adir/bsubdir'))
911
3010.2.1 by Martin Pool
Followon from MemoryTransport._abspath fix: add test_rename_across_subdirs, and fix error construction
912
    def test_rename_across_subdirs(self):
913
        t = self.get_transport()
914
        if t.is_readonly():
915
            raise TestNotApplicable("transport is readonly")
916
        t.mkdir('a')
917
        t.mkdir('b')
918
        ta = t.clone('a')
919
        tb = t.clone('b')
920
        ta.put_bytes('f', 'aoeu')
921
        ta.rename('f', '../b/f')
922
        self.assertTrue(tb.has('f'))
923
        self.assertFalse(ta.has('f'))
924
        self.assertTrue(t.has('b/f'))
925
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
926
    def test_delete_tree(self):
927
        t = self.get_transport()
928
929
        # Not much to do with a readonly transport
930
        if t.is_readonly():
931
            self.assertRaises(TransportNotPossible, t.delete_tree, 'missing')
932
            return
933
934
        # and does it like listing ?
935
        t.mkdir('adir')
936
        try:
937
            t.delete_tree('adir')
938
        except TransportNotPossible:
939
            # ok, this transport does not support delete_tree
940
            return
941
        
942
        # did it delete that trivial case?
943
        self.assertRaises(NoSuchFile, t.stat, 'adir')
944
945
        self.build_tree(['adir/',
946
                         'adir/file', 
947
                         'adir/subdir/', 
948
                         'adir/subdir/file', 
949
                         'adir/subdir2/',
950
                         'adir/subdir2/file',
951
                         ], transport=t)
952
953
        t.delete_tree('adir')
954
        # adir should be gone now.
955
        self.assertRaises(NoSuchFile, t.stat, 'adir')
956
1530.1.3 by Robert Collins
transport implementations now tested consistently.
957
    def test_move(self):
958
        t = self.get_transport()
959
960
        if t.is_readonly():
961
            return
962
963
        # TODO: I would like to use os.listdir() to
964
        # make sure there are no extra files, but SftpServer
965
        # creates control files in the working directory
966
        # perhaps all of this could be done in a subdirectory
967
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
968
        t.put_bytes('a', 'a first file\n')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
969
        self.assertEquals([True, False], list(t.has_multi(['a', 'b'])))
970
971
        t.move('a', 'b')
972
        self.failUnless(t.has('b'))
973
        self.failIf(t.has('a'))
974
975
        self.check_transport_contents('a first file\n', t, 'b')
976
        self.assertEquals([False, True], list(t.has_multi(['a', 'b'])))
977
978
        # Overwrite a file
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
979
        t.put_bytes('c', 'c this file\n')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
980
        t.move('c', 'b')
981
        self.failIf(t.has('c'))
982
        self.check_transport_contents('c this file\n', t, 'b')
983
984
        # TODO: Try to write a test for atomicity
3059.2.18 by Vincent Ladeuil
Take spiv review comments into account.
985
        # TODO: Test moving into a non-existent subdirectory
1530.1.3 by Robert Collins
transport implementations now tested consistently.
986
        # TODO: Test Transport.move_multi
987
988
    def test_copy(self):
989
        t = self.get_transport()
990
991
        if t.is_readonly():
992
            return
993
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
994
        t.put_bytes('a', 'a file\n')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
995
        t.copy('a', 'b')
996
        self.check_transport_contents('a file\n', t, 'b')
997
998
        self.assertRaises(NoSuchFile, t.copy, 'c', 'd')
999
        os.mkdir('c')
1000
        # What should the assert be if you try to copy a
1001
        # file over a directory?
1002
        #self.assertRaises(Something, t.copy, 'a', 'c')
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
1003
        t.put_bytes('d', 'text in d\n')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1004
        t.copy('d', 'b')
1005
        self.check_transport_contents('text in d\n', t, 'b')
1006
1007
        # TODO: test copy_multi
1008
1009
    def test_connection_error(self):
1910.7.17 by Andrew Bennetts
Various cosmetic changes.
1010
        """ConnectionError is raised when connection is impossible.
1011
        
3021.1.2 by Vincent Ladeuil
Fix bug #164567 by catching connection errors.
1012
        The error should be raised from the first operation on the transport.
1910.7.17 by Andrew Bennetts
Various cosmetic changes.
1013
        """
1530.1.9 by Robert Collins
Test bogus urls with http in the new infrastructure.
1014
        try:
1015
            url = self._server.get_bogus_url()
1016
        except NotImplementedError:
1017
            raise TestSkipped("Transport %s has no bogus URL support." %
1018
                              self._server.__class__)
2485.8.38 by Vincent Ladeuil
Finish sftp refactoring. Test suite passing.
1019
        t = get_transport(url)
1020
        self.assertRaises((ConnectionError, NoSuchFile), t.get, '.bzr/branch')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1021
1022
    def test_stat(self):
1023
        # TODO: Test stat, just try once, and if it throws, stop testing
1024
        from stat import S_ISDIR, S_ISREG
1025
1026
        t = self.get_transport()
1027
1028
        try:
1029
            st = t.stat('.')
1030
        except TransportNotPossible, e:
1031
            # This transport cannot stat
1032
            return
1033
1034
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e']
1035
        sizes = [14, 0, 16, 0, 18] 
1551.2.39 by abentley
Fix line endings in tests
1036
        self.build_tree(paths, transport=t, line_endings='binary')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1037
1038
        for path, size in zip(paths, sizes):
1039
            st = t.stat(path)
1040
            if path.endswith('/'):
1041
                self.failUnless(S_ISDIR(st.st_mode))
1042
                # directory sizes are meaningless
1043
            else:
1044
                self.failUnless(S_ISREG(st.st_mode))
1045
                self.assertEqual(size, st.st_size)
1046
1047
        remote_stats = list(t.stat_multi(paths))
1048
        remote_iter_stats = list(t.stat_multi(iter(paths)))
1049
1050
        self.assertRaises(NoSuchFile, t.stat, 'q')
1051
        self.assertRaises(NoSuchFile, t.stat, 'b/a')
1052
1053
        self.assertListRaises(NoSuchFile, t.stat_multi, ['a', 'c', 'd'])
1054
        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.
1055
        self.build_tree(['subdir/', 'subdir/file'], transport=t)
1056
        subdir = t.clone('subdir')
1057
        subdir.stat('./file')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1058
        subdir.stat('.')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1059
1060
    def test_list_dir(self):
1061
        # TODO: Test list_dir, just try once, and if it throws, stop testing
1062
        t = self.get_transport()
1063
        
1064
        if not t.listable():
1065
            self.assertRaises(TransportNotPossible, t.list_dir, '.')
1066
            return
1067
3211.6.1 by James Henstridge
* Remove relpath='' special case in MemoryTransport._abspath(), which
1068
        def sorted_list(d, transport):
1069
            l = list(transport.list_dir(d))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1070
            l.sort()
1071
            return l
1072
3211.6.1 by James Henstridge
* Remove relpath='' special case in MemoryTransport._abspath(), which
1073
        self.assertEqual([], sorted_list('.', t))
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
1074
        # c2 is precisely one letter longer than c here to test that
1075
        # suffixing is not confused.
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
1076
        # a%25b checks that quoting is done consistently across transports
1077
        tree_names = ['a', 'a%25b', 'b', 'c/', 'c/d', 'c/e', 'c2/']
2120.3.1 by John Arbash Meinel
Fix MemoryTransport.list_dir() implementation, and update tests
1078
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
1079
        if not t.is_readonly():
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
1080
            self.build_tree(tree_names, transport=t)
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
1081
        else:
2120.3.1 by John Arbash Meinel
Fix MemoryTransport.list_dir() implementation, and update tests
1082
            self.build_tree(tree_names)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1083
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
1084
        self.assertEqual(
3211.6.1 by James Henstridge
* Remove relpath='' special case in MemoryTransport._abspath(), which
1085
            ['a', 'a%2525b', 'b', 'c', 'c2'], sorted_list('', t))
1086
        self.assertEqual(
1087
            ['a', 'a%2525b', 'b', 'c', 'c2'], sorted_list('.', t))
1088
        self.assertEqual(['d', 'e'], sorted_list('c', t))
1089
1090
        # Cloning the transport produces an equivalent listing
1091
        self.assertEqual(['d', 'e'], sorted_list('', t.clone('c')))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1092
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
1093
        if not t.is_readonly():
1094
            t.delete('c/d')
1095
            t.delete('b')
1096
        else:
2120.3.1 by John Arbash Meinel
Fix MemoryTransport.list_dir() implementation, and update tests
1097
            os.unlink('c/d')
1098
            os.unlink('b')
1534.4.9 by Robert Collins
Add a readonly decorator for transports.
1099
            
3211.6.1 by James Henstridge
* Remove relpath='' special case in MemoryTransport._abspath(), which
1100
        self.assertEqual(['a', 'a%2525b', 'c', 'c2'], sorted_list('.', t))
1101
        self.assertEqual(['e'], sorted_list('c', t))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1102
1662.1.12 by Martin Pool
Translate unknown sftp errors to PathError, no NoSuchFile
1103
        self.assertListRaises(PathError, t.list_dir, 'q')
1104
        self.assertListRaises(PathError, t.list_dir, 'c/f')
1105
        self.assertListRaises(PathError, t.list_dir, 'a')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1106
1910.7.1 by Andrew Bennetts
Make sure list_dir always returns url-escaped names.
1107
    def test_list_dir_result_is_url_escaped(self):
1108
        t = self.get_transport()
1109
        if not t.listable():
1110
            raise TestSkipped("transport not listable")
1111
1112
        if not t.is_readonly():
1113
            self.build_tree(['a/', 'a/%'], transport=t)
1114
        else:
1115
            self.build_tree(['a/', 'a/%'])
1116
        
1910.7.2 by Andrew Bennetts
Also assert that list_dir returns plain str objects.
1117
        names = list(t.list_dir('a'))
1118
        self.assertEqual(['%25'], names)
1119
        self.assertIsInstance(names[0], str)
1910.7.1 by Andrew Bennetts
Make sure list_dir always returns url-escaped names.
1120
2485.8.25 by Vincent Ladeuil
Separate abspath from _remote_path, the intents are different.
1121
    def test_clone_preserve_info(self):
1122
        t1 = self.get_transport()
1123
        if not isinstance(t1, ConnectedTransport):
1124
            raise TestSkipped("not a connected transport")
1125
1126
        t2 = t1.clone('subdir')
1127
        self.assertEquals(t1._scheme, t2._scheme)
1128
        self.assertEquals(t1._user, t2._user)
1129
        self.assertEquals(t1._password, t2._password)
1130
        self.assertEquals(t1._host, t2._host)
1131
        self.assertEquals(t1._port, t2._port)
1132
2485.8.39 by Vincent Ladeuil
Add tests around connection reuse.
1133
    def test__reuse_for(self):
1134
        t = self.get_transport()
1135
        if not isinstance(t, ConnectedTransport):
1136
            raise TestSkipped("not a connected transport")
1137
1138
        def new_url(scheme=None, user=None, password=None,
1139
                    host=None, port=None, path=None):
2900.2.16 by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password).
1140
            """Build a new url from t.base changing only parts of it.
2485.8.39 by Vincent Ladeuil
Add tests around connection reuse.
1141
1142
            Only the parameters different from None will be changed.
1143
            """
2485.8.53 by Vincent Ladeuil
Apply test_connection_sharing to all transports to make smart
1144
            if scheme   is None: scheme   = t._scheme
1145
            if user     is None: user     = t._user
2485.8.39 by Vincent Ladeuil
Add tests around connection reuse.
1146
            if password is None: password = t._password
2485.8.53 by Vincent Ladeuil
Apply test_connection_sharing to all transports to make smart
1147
            if user     is None: user     = t._user
1148
            if host     is None: host     = t._host
1149
            if port     is None: port     = t._port
1150
            if path     is None: path     = t._path
2485.8.39 by Vincent Ladeuil
Add tests around connection reuse.
1151
            return t._unsplit_url(scheme, user, password, host, port, path)
1152
2900.2.16 by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password).
1153
        if t._scheme == 'ftp':
1154
            scheme = 'sftp'
1155
        else:
1156
            scheme = 'ftp'
1157
        self.assertIsNot(t, t._reuse_for(new_url(scheme=scheme)))
2485.8.39 by Vincent Ladeuil
Add tests around connection reuse.
1158
        if t._user == 'me':
1159
            user = 'you'
1160
        else:
1161
            user = 'me'
1162
        self.assertIsNot(t, t._reuse_for(new_url(user=user)))
1163
        # passwords are not taken into account because:
1164
        # - it makes no sense to have two different valid passwords for the
1165
        #   same user
1166
        # - _password in ConnectedTransport is intended to collect what the
1167
        #   user specified from the command-line and there are cases where the
1168
        #   new url can contain no password (if the url was built from an
1169
        #   existing transport.base for example)
1170
        # - password are considered part of the credentials provided at
1171
        #   connection creation time and as such may not be present in the url
1172
        #   (they may be typed by the user when prompted for example)
1173
        self.assertIs(t, t._reuse_for(new_url(password='from space')))
1174
        # We will not connect, we can use a invalid host
1175
        self.assertIsNot(t, t._reuse_for(new_url(host=t._host + 'bar')))
1176
        if t._port == 1234:
1177
            port = 4321
1178
        else:
1179
            port = 1234
1180
        self.assertIsNot(t, t._reuse_for(new_url(port=port)))
2990.2.1 by Vincent Ladeuil
ConnectedTransport._reuse_for fails to deal with local URLs.
1181
        # No point in trying to reuse a transport for a local URL
2990.2.2 by Vincent Ladeuil
Detect invalid transport reuse attempts by catching invalid URLs.
1182
        self.assertIs(None, t._reuse_for('/valid_but_not_existing'))
2485.8.39 by Vincent Ladeuil
Add tests around connection reuse.
1183
2485.8.53 by Vincent Ladeuil
Apply test_connection_sharing to all transports to make smart
1184
    def test_connection_sharing(self):
1185
        t = self.get_transport()
1186
        if not isinstance(t, ConnectedTransport):
1187
            raise TestSkipped("not a connected transport")
1188
1189
        c = t.clone('subdir')
2485.8.54 by Vincent Ladeuil
Refactor medium uses by making a distinction betweem shared and real medium.
1190
        # Some transports will create the connection  only when needed
2485.8.53 by Vincent Ladeuil
Apply test_connection_sharing to all transports to make smart
1191
        t.has('surely_not') # Force connection
1192
        self.assertIs(t._get_connection(), c._get_connection())
1193
2485.8.54 by Vincent Ladeuil
Refactor medium uses by making a distinction betweem shared and real medium.
1194
        # Temporary failure, we need to create a new dummy connection
2485.8.53 by Vincent Ladeuil
Apply test_connection_sharing to all transports to make smart
1195
        new_connection = object()
1196
        t._set_connection(new_connection)
2485.8.54 by Vincent Ladeuil
Refactor medium uses by making a distinction betweem shared and real medium.
1197
        # Check that both transports use the same connection
2485.8.53 by Vincent Ladeuil
Apply test_connection_sharing to all transports to make smart
1198
        self.assertIs(new_connection, t._get_connection())
1199
        self.assertIs(new_connection, c._get_connection())
1200
2485.8.39 by Vincent Ladeuil
Add tests around connection reuse.
1201
    def test_reuse_connection_for_various_paths(self):
1202
        t = self.get_transport()
1203
        if not isinstance(t, ConnectedTransport):
1204
            raise TestSkipped("not a connected transport")
1205
1206
        t.has('surely_not') # Force connection
1207
        self.assertIsNot(None, t._get_connection())
1208
1209
        subdir = t._reuse_for(t.base + 'whatever/but/deep/down/the/path')
1210
        self.assertIsNot(t, subdir)
1211
        self.assertIs(t._get_connection(), subdir._get_connection())
1212
1213
        home = subdir._reuse_for(t.base + 'home')
1214
        self.assertIs(t._get_connection(), home._get_connection())
1215
        self.assertIs(subdir._get_connection(), home._get_connection())
1216
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1217
    def test_clone(self):
1218
        # TODO: Test that clone moves up and down the filesystem
1219
        t1 = self.get_transport()
1220
1221
        self.build_tree(['a', 'b/', 'b/c'], transport=t1)
1222
1223
        self.failUnless(t1.has('a'))
1224
        self.failUnless(t1.has('b/c'))
1225
        self.failIf(t1.has('c'))
1226
1227
        t2 = t1.clone('b')
1228
        self.assertEqual(t1.base + 'b/', t2.base)
1229
1230
        self.failUnless(t2.has('c'))
1231
        self.failIf(t2.has('a'))
1232
1233
        t3 = t2.clone('..')
1234
        self.failUnless(t3.has('a'))
1235
        self.failIf(t3.has('c'))
1236
1237
        self.failIf(t1.has('b/d'))
1238
        self.failIf(t2.has('d'))
1239
        self.failIf(t3.has('b/d'))
1240
1241
        if t1.is_readonly():
1242
            open('b/d', 'wb').write('newfile\n')
1243
        else:
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
1244
            t2.put_bytes('d', 'newfile\n')
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1245
1246
        self.failUnless(t1.has('b/d'))
1247
        self.failUnless(t2.has('d'))
1248
        self.failUnless(t3.has('b/d'))
1249
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
1250
    def test_clone_to_root(self):
1251
        orig_transport = self.get_transport()
1252
        # Repeatedly go up to a parent directory until we're at the root
1253
        # directory of this transport
1254
        root_transport = orig_transport
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
1255
        new_transport = root_transport.clone("..")
2245.6.1 by Alexander Belchenko
win32 UNC path: recursive cloning UNC path to root stops on //HOST, not on //
1256
        # as we are walking up directories, the path must be
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
1257
        # growing less, except at the top
1258
        self.assertTrue(len(new_transport.base) < len(root_transport.base)
1259
            or new_transport.base == root_transport.base)
1260
        while new_transport.base != root_transport.base:
1261
            root_transport = new_transport
1262
            new_transport = root_transport.clone("..")
2245.6.1 by Alexander Belchenko
win32 UNC path: recursive cloning UNC path to root stops on //HOST, not on //
1263
            # as we are walking up directories, the path must be
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
1264
            # growing less, except at the top
1265
            self.assertTrue(len(new_transport.base) < len(root_transport.base)
1266
                or new_transport.base == root_transport.base)
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
1267
1268
        # Cloning to "/" should take us to exactly the same location.
1269
        self.assertEqual(root_transport.base, orig_transport.clone("/").base)
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
1270
        # the abspath of "/" from the original transport should be the same
1271
        # as the base at the root:
1272
        self.assertEqual(orig_transport.abspath("/"), root_transport.base)
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
1273
1910.15.5 by Andrew Bennetts
Transport behaviour at the root of the URL is now defined and tested.
1274
        # At the root, the URL must still end with / as its a directory
1275
        self.assertEqual(root_transport.base[-1], '/')
1276
1277
    def test_clone_from_root(self):
1278
        """At the root, cloning to a simple dir should just do string append."""
1279
        orig_transport = self.get_transport()
1280
        root_transport = orig_transport.clone('/')
1281
        self.assertEqual(root_transport.base + '.bzr/',
1282
            root_transport.clone('.bzr').base)
1283
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
1284
    def test_base_url(self):
1285
        t = self.get_transport()
1286
        self.assertEqual('/', t.base[-1])
1287
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1288
    def test_relpath(self):
1289
        t = self.get_transport()
1290
        self.assertEqual('', t.relpath(t.base))
1291
        # base ends with /
1292
        self.assertEqual('', t.relpath(t.base[:-1]))
3059.2.18 by Vincent Ladeuil
Take spiv review comments into account.
1293
        # subdirs which don't exist should still give relpaths.
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1294
        self.assertEqual('foo', t.relpath(t.base + 'foo'))
1295
        # trailing slash should be the same.
1296
        self.assertEqual('foo', t.relpath(t.base + 'foo/'))
1297
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
1298
    def test_relpath_at_root(self):
1299
        t = self.get_transport()
1300
        # clone all the way to the top
1301
        new_transport = t.clone('..')
1302
        while new_transport.base != t.base:
1303
            t = new_transport
1304
            new_transport = t.clone('..')
1305
        # we must be able to get a relpath below the root
1306
        self.assertEqual('', t.relpath(t.base))
1307
        # and a deeper one should work too
1308
        self.assertEqual('foo/bar', t.relpath(t.base + 'foo/bar'))
1309
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1310
    def test_abspath(self):
1311
        # smoke test for abspath. Corner cases for backends like unix fs's
1312
        # that have aliasing problems like symlinks should go in backend
1313
        # specific test cases.
1314
        transport = self.get_transport()
2485.8.21 by Vincent Ladeuil
Simplify debug.
1315
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1316
        self.assertEqual(transport.base + 'relpath',
1317
                         transport.abspath('relpath'))
1318
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
1319
        # This should work without raising an error.
1320
        transport.abspath("/")
1321
1322
        # the abspath of "/" and "/foo/.." should result in the same location
1323
        self.assertEqual(transport.abspath("/"), transport.abspath("/foo/.."))
1324
2070.3.1 by Andrew Bennetts
Fix memory_transport.abspath('/foo')
1325
        self.assertEqual(transport.clone("/").abspath('foo'),
1326
                         transport.abspath("/foo"))
1327
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
1328
    def test_local_abspath(self):
1329
        transport = self.get_transport()
1330
        try:
1331
            p = transport.local_abspath('.')
2018.18.4 by Martin Pool
Change Transport.local_abspath to raise NotLocalUrl, and test.
1332
        except (errors.NotLocalUrl, TransportNotPossible), e:
2018.18.22 by Martin Pool
merge bzr.dev
1333
            # should be formattable
2018.18.4 by Martin Pool
Change Transport.local_abspath to raise NotLocalUrl, and test.
1334
            s = str(e)
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
1335
        else:
1336
            self.assertEqual(getcwd(), p)
1337
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
1338
    def test_abspath_at_root(self):
1339
        t = self.get_transport()
1340
        # clone all the way to the top
1341
        new_transport = t.clone('..')
1342
        while new_transport.base != t.base:
1343
            t = new_transport
1344
            new_transport = t.clone('..')
1345
        # we must be able to get a abspath of the root when we ask for
1346
        # t.abspath('..') - this due to our choice that clone('..')
1347
        # should return the root from the root, combined with the desire that
1348
        # the url from clone('..') and from abspath('..') should be the same.
1349
        self.assertEqual(t.base, t.abspath('..'))
1350
        # '' should give us the root
1351
        self.assertEqual(t.base, t.abspath(''))
1352
        # and a path should append to the url
1353
        self.assertEqual(t.base + 'foo', t.abspath('foo'))
1354
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1355
    def test_iter_files_recursive(self):
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
1356
        transport = self.get_transport()
1357
        if not transport.listable():
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
1358
            self.assertRaises(TransportNotPossible,
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
1359
                              transport.iter_files_recursive)
1360
            return
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
1361
        self.build_tree(['isolated/',
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
1362
                         'isolated/dir/',
1363
                         'isolated/dir/foo',
1364
                         'isolated/dir/bar',
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
1365
                         'isolated/dir/b%25z', # make sure quoting is correct
1530.1.4 by Robert Collins
integrate Memory tests into transport interface tests.
1366
                         'isolated/bar'],
1367
                        transport=transport)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1368
        paths = set(transport.iter_files_recursive())
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
1369
        # nb the directories are not converted
1370
        self.assertEqual(paths,
1371
                    set(['isolated/dir/foo',
1372
                         'isolated/dir/bar',
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
1373
                         'isolated/dir/b%2525z',
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
1374
                         'isolated/bar']))
1375
        sub_transport = transport.clone('isolated')
1376
        paths = set(sub_transport.iter_files_recursive())
1959.2.1 by John Arbash Meinel
David Allouche: Make transports return escaped paths
1377
        self.assertEqual(paths,
1378
            set(['dir/foo', 'dir/bar', 'dir/b%2525z', 'bar']))
1379
1380
    def test_copy_tree(self):
1381
        # TODO: test file contents and permissions are preserved. This test was
1382
        # added just to ensure that quoting was handled correctly.
1383
        # -- David Allouche 2006-08-11
1384
        transport = self.get_transport()
1385
        if not transport.listable():
1386
            self.assertRaises(TransportNotPossible,
1387
                              transport.iter_files_recursive)
1388
            return
1389
        if transport.is_readonly():
1390
            return
1391
        self.build_tree(['from/',
1392
                         'from/dir/',
1393
                         'from/dir/foo',
1394
                         'from/dir/bar',
1395
                         'from/dir/b%25z', # make sure quoting is correct
1396
                         'from/bar'],
1397
                        transport=transport)
1398
        transport.copy_tree('from', 'to')
1399
        paths = set(transport.iter_files_recursive())
1400
        self.assertEqual(paths,
1401
                    set(['from/dir/foo',
1402
                         'from/dir/bar',
1403
                         'from/dir/b%2525z',
1404
                         'from/bar',
1405
                         'to/dir/foo',
1406
                         'to/dir/bar',
1407
                         'to/dir/b%2525z',
1408
                         'to/bar',]))
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
1409
1410
    def test_unicode_paths(self):
1685.1.57 by Martin Pool
[broken] Skip unicode blackbox tests if not supported by filesystem
1411
        """Test that we can read/write files with Unicode names."""
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
1412
        t = self.get_transport()
1413
1711.7.36 by John Arbash Meinel
Use different filenames to avoid path collisions on win32 w/ FAT32
1414
        # With FAT32 and certain encodings on win32
1415
        # '\xe5' and '\xe4' actually map to the same file
1416
        # adding a suffix kicks in the 'preserving but insensitive'
1417
        # route, and maintains the right files
1418
        files = [u'\xe5.1', # a w/ circle iso-8859-1
1419
                 u'\xe4.2', # a w/ dots iso-8859-1
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
1420
                 u'\u017d', # Z with umlat iso-8859-2
1421
                 u'\u062c', # Arabic j
1422
                 u'\u0410', # Russian A
1423
                 u'\u65e5', # Kanji person
1424
                ]
1425
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
1426
        try:
1711.4.13 by John Arbash Meinel
Use line_endings='binary' for win32
1427
            self.build_tree(files, transport=t, line_endings='binary')
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
1428
        except UnicodeError:
1429
            raise TestSkipped("cannot handle unicode paths in current encoding")
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
1430
1431
        # A plain unicode string is not a valid url
1432
        for fname in files:
1433
            self.assertRaises(InvalidURL, t.get, fname)
1434
1435
        for fname in files:
1436
            fname_utf8 = fname.encode('utf-8')
1437
            contents = 'contents of %s\n' % (fname_utf8,)
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
1438
            self.check_transport_contents(contents, t, urlutils.escape(fname))
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
1439
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1440
    def test_connect_twice_is_same_content(self):
2485.8.21 by Vincent Ladeuil
Simplify debug.
1441
        # check that our server (whatever it is) is accessible reliably
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1442
        # via get_transport and multiple connections share content.
1443
        transport = self.get_transport()
1444
        if transport.is_readonly():
1445
            return
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
1446
        transport.put_bytes('foo', 'bar')
2485.8.21 by Vincent Ladeuil
Simplify debug.
1447
        transport3 = self.get_transport()
1448
        self.check_transport_contents('bar', transport3, 'foo')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1449
        # its base should be usable.
2485.8.25 by Vincent Ladeuil
Separate abspath from _remote_path, the intents are different.
1450
        transport4 = get_transport(transport.base)
2485.8.21 by Vincent Ladeuil
Simplify debug.
1451
        self.check_transport_contents('bar', transport4, 'foo')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1452
1453
        # now opening at a relative url should give use a sane result:
1454
        transport.mkdir('newdir')
2485.8.25 by Vincent Ladeuil
Separate abspath from _remote_path, the intents are different.
1455
        transport5 = get_transport(transport.base + "newdir")
2485.8.21 by Vincent Ladeuil
Simplify debug.
1456
        transport6 = transport5.clone('..')
1457
        self.check_transport_contents('bar', transport6, 'foo')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1458
1459
    def test_lock_write(self):
1910.16.1 by Andrew Bennetts
lock_read and lock_write may raise TransportNotPossible.
1460
        """Test transport-level write locks.
1461
1462
        These are deprecated and transports may decline to support them.
1463
        """
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1464
        transport = self.get_transport()
1465
        if transport.is_readonly():
1466
            self.assertRaises(TransportNotPossible, transport.lock_write, 'foo')
1467
            return
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
1468
        transport.put_bytes('lock', '')
1910.16.1 by Andrew Bennetts
lock_read and lock_write may raise TransportNotPossible.
1469
        try:
1470
            lock = transport.lock_write('lock')
1752.3.6 by Andrew Bennetts
Merge from test-tweaks
1471
        except TransportNotPossible:
1910.16.1 by Andrew Bennetts
lock_read and lock_write may raise TransportNotPossible.
1472
            return
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1473
        # TODO make this consistent on all platforms:
1474
        # self.assertRaises(LockError, transport.lock_write, 'lock')
1475
        lock.unlock()
1476
1477
    def test_lock_read(self):
1910.16.1 by Andrew Bennetts
lock_read and lock_write may raise TransportNotPossible.
1478
        """Test transport-level read locks.
1479
1480
        These are deprecated and transports may decline to support them.
1481
        """
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1482
        transport = self.get_transport()
1483
        if transport.is_readonly():
1484
            file('lock', 'w').close()
1485
        else:
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
1486
            transport.put_bytes('lock', '')
1910.16.1 by Andrew Bennetts
lock_read and lock_write may raise TransportNotPossible.
1487
        try:
1488
            lock = transport.lock_read('lock')
1752.3.6 by Andrew Bennetts
Merge from test-tweaks
1489
        except TransportNotPossible:
1910.16.1 by Andrew Bennetts
lock_read and lock_write may raise TransportNotPossible.
1490
            return
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
1491
        # TODO make this consistent on all platforms:
1492
        # self.assertRaises(LockError, transport.lock_read, 'lock')
1493
        lock.unlock()
1185.85.80 by John Arbash Meinel
[merge] jam-integration 1527, including branch-formats, help text, misc bug fixes.
1494
1594.2.5 by Robert Collins
Readv patch from Johan Rydberg giving knits partial download support.
1495
    def test_readv(self):
1496
        transport = self.get_transport()
1497
        if transport.is_readonly():
1498
            file('a', 'w').write('0123456789')
1499
        else:
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
1500
            transport.put_bytes('a', '0123456789')
1185.85.76 by John Arbash Meinel
Adding an InvalidURL so transports can report they expect utf-8 quoted paths. Updated tests
1501
2004.1.22 by v.ladeuil+lp at free
Implements Range header handling for GET requests. Fix a test.
1502
        d = list(transport.readv('a', ((0, 1),)))
1503
        self.assertEqual(d[0], (0, '0'))
1504
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
1505
        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.
1506
        self.assertEqual(d[0], (0, '0'))
1594.2.17 by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading.
1507
        self.assertEqual(d[1], (1, '1'))
1508
        self.assertEqual(d[2], (3, '34'))
1509
        self.assertEqual(d[3], (9, '9'))
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
1510
1864.5.2 by John Arbash Meinel
always read in sorted order, and return in requested order, but only cache what is currently out of order
1511
    def test_readv_out_of_order(self):
1512
        transport = self.get_transport()
1513
        if transport.is_readonly():
1514
            file('a', 'w').write('0123456789')
1515
        else:
1955.3.11 by John Arbash Meinel
Clean up the rest of the api calls to deprecated functions in the test suite, and make sure Transport._pump is only accepting files, not strings
1516
            transport.put_bytes('a', '01234567890')
1864.5.2 by John Arbash Meinel
always read in sorted order, and return in requested order, but only cache what is currently out of order
1517
1518
        d = list(transport.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
1519
        self.assertEqual(d[0], (1, '1'))
1520
        self.assertEqual(d[1], (9, '9'))
1521
        self.assertEqual(d[2], (0, '0'))
1522
        self.assertEqual(d[3], (3, '34'))
1752.2.40 by Andrew Bennetts
Merge from bzr.dev.
1523
2745.5.1 by Robert Collins
* New parameter on ``bzrlib.transport.Transport.readv``
1524
    def test_readv_with_adjust_for_latency(self):
1525
        transport = self.get_transport()
1526
        # the adjust for latency flag expands the data region returned
1527
        # according to a per-transport heuristic, so testing is a little
1528
        # tricky as we need more data than the largest combining that our
1529
        # transports do. To accomodate this we generate random data and cross
1530
        # reference the returned data with the random data. To avoid doing
1531
        # multiple large random byte look ups we do several tests on the same
1532
        # backing data.
1533
        content = osutils.rand_bytes(200*1024)
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1534
        content_size = len(content)
2745.5.1 by Robert Collins
* New parameter on ``bzrlib.transport.Transport.readv``
1535
        if transport.is_readonly():
1536
            file('a', 'w').write(content)
1537
        else:
1538
            transport.put_bytes('a', content)
1539
        def check_result_data(result_vector):
1540
            for item in result_vector:
1541
                data_len = len(item[1])
1542
                self.assertEqual(content[item[0]:item[0] + data_len], item[1])
1543
1544
        # start corner case
1545
        result = list(transport.readv('a', ((0, 30),),
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1546
            adjust_for_latency=True, upper_limit=content_size))
2745.5.1 by Robert Collins
* New parameter on ``bzrlib.transport.Transport.readv``
1547
        # we expect 1 result, from 0, to something > 30
1548
        self.assertEqual(1, len(result))
1549
        self.assertEqual(0, result[0][0])
1550
        self.assertTrue(len(result[0][1]) >= 30)
1551
        check_result_data(result)
1552
        # end of file corner case
1553
        result = list(transport.readv('a', ((204700, 100),),
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1554
            adjust_for_latency=True, upper_limit=content_size))
2745.5.1 by Robert Collins
* New parameter on ``bzrlib.transport.Transport.readv``
1555
        # we expect 1 result, from 204800- its length, to the end
1556
        self.assertEqual(1, len(result))
1557
        data_len = len(result[0][1])
1558
        self.assertEqual(204800-data_len, result[0][0])
1559
        self.assertTrue(data_len >= 100)
1560
        check_result_data(result)
1561
        # out of order ranges are made in order
1562
        result = list(transport.readv('a', ((204700, 100), (0, 50)),
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1563
            adjust_for_latency=True, upper_limit=content_size))
2745.5.1 by Robert Collins
* New parameter on ``bzrlib.transport.Transport.readv``
1564
        # we expect 2 results, in order, start and end.
1565
        self.assertEqual(2, len(result))
1566
        # start
1567
        data_len = len(result[0][1])
1568
        self.assertEqual(0, result[0][0])
1569
        self.assertTrue(data_len >= 30)
1570
        # end
1571
        data_len = len(result[1][1])
1572
        self.assertEqual(204800-data_len, result[1][0])
1573
        self.assertTrue(data_len >= 100)
1574
        check_result_data(result)
1575
        # close ranges get combined (even if out of order)
1576
        for request_vector in [((400,50), (800, 234)), ((800, 234), (400,50))]:
1577
            result = list(transport.readv('a', request_vector,
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1578
                adjust_for_latency=True, upper_limit=content_size))
2745.5.1 by Robert Collins
* New parameter on ``bzrlib.transport.Transport.readv``
1579
            self.assertEqual(1, len(result))
1580
            data_len = len(result[0][1])
3059.2.18 by Vincent Ladeuil
Take spiv review comments into account.
1581
            # minimum length is from 400 to 1034 - 634
2745.5.1 by Robert Collins
* New parameter on ``bzrlib.transport.Transport.readv``
1582
            self.assertTrue(data_len >= 634)
1583
            # must contain the region 400 to 1034
1584
            self.assertTrue(result[0][0] <= 400)
1585
            self.assertTrue(result[0][0] + data_len >= 1034)
1586
            check_result_data(result)
2890.1.2 by Robert Collins
More readv adjust for latency tests and bugfixes.
1587
        # test from observed failure case.
1588
        if transport.is_readonly():
1589
            file('a', 'w').write('a'*1024*1024)
1590
        else:
1591
            transport.put_bytes('a', 'a'*1024*1024)
1592
        broken_vector = [(465219, 800), (225221, 800), (445548, 800),
1593
            (225037, 800), (221357, 800), (437077, 800), (947670, 800),
1594
            (465373, 800), (947422, 800)]
1595
        results = list(transport.readv('a', broken_vector, True, 1024*1024))
1596
        found_items = [False]*9
1597
        for pos, (start, length) in enumerate(broken_vector):
1598
            # check the range is covered by the result
1599
            for offset, data in results:
1600
                if offset <= start and start + length <= offset + len(data):
1601
                    found_items[pos] = True
1602
        self.assertEqual([True]*9, found_items)
2745.5.1 by Robert Collins
* New parameter on ``bzrlib.transport.Transport.readv``
1603
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
1604
    def test_get_with_open_write_stream_sees_all_content(self):
2671.3.5 by Robert Collins
* New methods on ``bzrlib.transport.Transport`` ``open_file_stream`` and
1605
        t = self.get_transport()
1606
        if t.is_readonly():
1607
            return
2671.3.9 by Robert Collins
Review feedback and fix VFat emulated transports to not claim to have unix permissions.
1608
        handle = t.open_write_stream('foo')
2671.3.5 by Robert Collins
* New methods on ``bzrlib.transport.Transport`` ``open_file_stream`` and
1609
        try:
2671.3.6 by Robert Collins
Review feedback.
1610
            handle.write('bcd')
2671.3.5 by Robert Collins
* New methods on ``bzrlib.transport.Transport`` ``open_file_stream`` and
1611
            self.assertEqual([(0, 'b'), (2, 'd')], list(t.readv('foo', ((0,1), (2,1)))))
1612
        finally:
2671.3.6 by Robert Collins
Review feedback.
1613
            handle.close()
2671.3.5 by Robert Collins
* New methods on ``bzrlib.transport.Transport`` ``open_file_stream`` and
1614
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1615
    def test_get_smart_medium(self):
1616
        """All transports must either give a smart medium, or know they can't.
1617
        """
1618
        transport = self.get_transport()
1619
        try:
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1620
            client_medium = transport.get_smart_medium()
1621
            self.assertIsInstance(client_medium, medium.SmartClientMedium)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1622
        except errors.NoSmartMedium:
1623
            # as long as we got it we're fine
1624
            pass
1625
2001.3.2 by John Arbash Meinel
Force all transports to raise ShortReadvError if they can
1626
    def test_readv_short_read(self):
1627
        transport = self.get_transport()
1628
        if transport.is_readonly():
1629
            file('a', 'w').write('0123456789')
1630
        else:
1631
            transport.put_bytes('a', '01234567890')
1632
1633
        # This is intentionally reading off the end of the file
1634
        # since we are sure that it cannot get there
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
1635
        self.assertListRaises((errors.ShortReadvError, errors.InvalidRange,
1636
                               # Can be raised by paramiko
1637
                               AssertionError),
2001.3.2 by John Arbash Meinel
Force all transports to raise ShortReadvError if they can
1638
                              transport.readv, 'a', [(1,1), (8,10)])
1639
1640
        # This is trying to seek past the end of the file, it should
1641
        # also raise a special error
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
1642
        self.assertListRaises((errors.ShortReadvError, errors.InvalidRange),
2001.3.2 by John Arbash Meinel
Force all transports to raise ShortReadvError if they can
1643
                              transport.readv, 'a', [(12,2)])