~bzr-pqm/bzr/bzr.dev

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