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