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