~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transport.py

  • Committer: John Arbash Meinel
  • Date: 2009-10-12 21:44:27 UTC
  • mto: This revision was merged to the branch mainline in revision 4737.
  • Revision ID: john@arbash-meinel.com-20091012214427-zddi1kmc2jlf7v31
Py_ssize_t and its associated function typedefs are not available w/ python 2.4

So we define them in python-compat.h
Even further, gcc issued a warning for:
static int
_workaround_pyrex_096()
So we changed it to:
_workaround_pyrex_096(void)

Also, some python api funcs were incorrectly defined as 'char *' when they meant
'const char *'. Work around that with a (char *) cast, to avoid compiler warnings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
 
18
 
from bzrlib.selftest import TestCaseInTempDir
19
 
 
20
 
def test_transport(tester, t, readonly=False):
21
 
    """Test a transport object. Basically, it assumes that the
22
 
    Transport object is connected to the current working directory.
23
 
    So that whatever is done through the transport, should show
24
 
    up in the working directory, and vice-versa.
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
 
18
from cStringIO import StringIO
 
19
import os
 
20
import subprocess
 
21
import sys
 
22
import threading
 
23
 
 
24
import bzrlib
 
25
from bzrlib import (
 
26
    errors,
 
27
    osutils,
 
28
    tests,
 
29
    urlutils,
 
30
    )
 
31
from bzrlib.errors import (DependencyNotPresent,
 
32
                           FileExists,
 
33
                           InvalidURLJoin,
 
34
                           NoSuchFile,
 
35
                           PathNotChild,
 
36
                           ReadError,
 
37
                           UnsupportedProtocol,
 
38
                           )
 
39
from bzrlib.tests import ParamikoFeature, TestCase, TestCaseInTempDir
 
40
from bzrlib.transport import (_clear_protocol_handlers,
 
41
                              _CoalescedOffset,
 
42
                              ConnectedTransport,
 
43
                              _get_protocol_handlers,
 
44
                              _set_protocol_handlers,
 
45
                              _get_transport_modules,
 
46
                              get_transport,
 
47
                              LateReadError,
 
48
                              register_lazy_transport,
 
49
                              register_transport_proto,
 
50
                              Transport,
 
51
                              )
 
52
from bzrlib.transport.chroot import ChrootServer
 
53
from bzrlib.transport.memory import MemoryTransport
 
54
from bzrlib.transport.local import (LocalTransport,
 
55
                                    EmulatedWin32LocalTransport)
 
56
from bzrlib.transport.pathfilter import PathFilteringServer
 
57
 
 
58
 
 
59
# TODO: Should possibly split transport-specific tests into their own files.
 
60
 
 
61
 
 
62
class TestTransport(TestCase):
 
63
    """Test the non transport-concrete class functionality."""
 
64
 
 
65
    def test__get_set_protocol_handlers(self):
 
66
        handlers = _get_protocol_handlers()
 
67
        self.assertNotEqual([], handlers.keys( ))
 
68
        try:
 
69
            _clear_protocol_handlers()
 
70
            self.assertEqual([], _get_protocol_handlers().keys())
 
71
        finally:
 
72
            _set_protocol_handlers(handlers)
 
73
 
 
74
    def test_get_transport_modules(self):
 
75
        handlers = _get_protocol_handlers()
 
76
        # don't pollute the current handlers
 
77
        _clear_protocol_handlers()
 
78
        class SampleHandler(object):
 
79
            """I exist, isnt that enough?"""
 
80
        try:
 
81
            _clear_protocol_handlers()
 
82
            register_transport_proto('foo')
 
83
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
 
84
                                    'TestTransport.SampleHandler')
 
85
            register_transport_proto('bar')
 
86
            register_lazy_transport('bar', 'bzrlib.tests.test_transport',
 
87
                                    'TestTransport.SampleHandler')
 
88
            self.assertEqual([SampleHandler.__module__,
 
89
                              'bzrlib.transport.chroot',
 
90
                              'bzrlib.transport.pathfilter'],
 
91
                             _get_transport_modules())
 
92
        finally:
 
93
            _set_protocol_handlers(handlers)
 
94
 
 
95
    def test_transport_dependency(self):
 
96
        """Transport with missing dependency causes no error"""
 
97
        saved_handlers = _get_protocol_handlers()
 
98
        # don't pollute the current handlers
 
99
        _clear_protocol_handlers()
 
100
        try:
 
101
            register_transport_proto('foo')
 
102
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
 
103
                    'BadTransportHandler')
 
104
            try:
 
105
                get_transport('foo://fooserver/foo')
 
106
            except UnsupportedProtocol, e:
 
107
                e_str = str(e)
 
108
                self.assertEquals('Unsupported protocol'
 
109
                                  ' for url "foo://fooserver/foo":'
 
110
                                  ' Unable to import library "some_lib":'
 
111
                                  ' testing missing dependency', str(e))
 
112
            else:
 
113
                self.fail('Did not raise UnsupportedProtocol')
 
114
        finally:
 
115
            # restore original values
 
116
            _set_protocol_handlers(saved_handlers)
 
117
 
 
118
    def test_transport_fallback(self):
 
119
        """Transport with missing dependency causes no error"""
 
120
        saved_handlers = _get_protocol_handlers()
 
121
        try:
 
122
            _clear_protocol_handlers()
 
123
            register_transport_proto('foo')
 
124
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
 
125
                    'BackupTransportHandler')
 
126
            register_lazy_transport('foo', 'bzrlib.tests.test_transport',
 
127
                    'BadTransportHandler')
 
128
            t = get_transport('foo://fooserver/foo')
 
129
            self.assertTrue(isinstance(t, BackupTransportHandler))
 
130
        finally:
 
131
            _set_protocol_handlers(saved_handlers)
 
132
 
 
133
    def test_ssh_hints(self):
 
134
        """Transport ssh:// should raise an error pointing out bzr+ssh://"""
 
135
        try:
 
136
            get_transport('ssh://fooserver/foo')
 
137
        except UnsupportedProtocol, e:
 
138
            e_str = str(e)
 
139
            self.assertEquals('Unsupported protocol'
 
140
                              ' for url "ssh://fooserver/foo":'
 
141
                              ' bzr supports bzr+ssh to operate over ssh, use "bzr+ssh://fooserver/foo".',
 
142
                              str(e))
 
143
        else:
 
144
            self.fail('Did not raise UnsupportedProtocol')
 
145
 
 
146
    def test_LateReadError(self):
 
147
        """The LateReadError helper should raise on read()."""
 
148
        a_file = LateReadError('a path')
 
149
        try:
 
150
            a_file.read()
 
151
        except ReadError, error:
 
152
            self.assertEqual('a path', error.path)
 
153
        self.assertRaises(ReadError, a_file.read, 40)
 
154
        a_file.close()
 
155
 
 
156
    def test__combine_paths(self):
 
157
        t = Transport('/')
 
158
        self.assertEqual('/home/sarah/project/foo',
 
159
                         t._combine_paths('/home/sarah', 'project/foo'))
 
160
        self.assertEqual('/etc',
 
161
                         t._combine_paths('/home/sarah', '../../etc'))
 
162
        self.assertEqual('/etc',
 
163
                         t._combine_paths('/home/sarah', '../../../etc'))
 
164
        self.assertEqual('/etc',
 
165
                         t._combine_paths('/home/sarah', '/etc'))
 
166
 
 
167
    def test_local_abspath_non_local_transport(self):
 
168
        # the base implementation should throw
 
169
        t = MemoryTransport()
 
170
        e = self.assertRaises(errors.NotLocalUrl, t.local_abspath, 't')
 
171
        self.assertEqual('memory:///t is not a local path.', str(e))
 
172
 
 
173
 
 
174
class TestCoalesceOffsets(TestCase):
 
175
 
 
176
    def check(self, expected, offsets, limit=0, max_size=0, fudge=0):
 
177
        coalesce = Transport._coalesce_offsets
 
178
        exp = [_CoalescedOffset(*x) for x in expected]
 
179
        out = list(coalesce(offsets, limit=limit, fudge_factor=fudge,
 
180
                            max_size=max_size))
 
181
        self.assertEqual(exp, out)
 
182
 
 
183
    def test_coalesce_empty(self):
 
184
        self.check([], [])
 
185
 
 
186
    def test_coalesce_simple(self):
 
187
        self.check([(0, 10, [(0, 10)])], [(0, 10)])
 
188
 
 
189
    def test_coalesce_unrelated(self):
 
190
        self.check([(0, 10, [(0, 10)]),
 
191
                    (20, 10, [(0, 10)]),
 
192
                   ], [(0, 10), (20, 10)])
 
193
 
 
194
    def test_coalesce_unsorted(self):
 
195
        self.check([(20, 10, [(0, 10)]),
 
196
                    (0, 10, [(0, 10)]),
 
197
                   ], [(20, 10), (0, 10)])
 
198
 
 
199
    def test_coalesce_nearby(self):
 
200
        self.check([(0, 20, [(0, 10), (10, 10)])],
 
201
                   [(0, 10), (10, 10)])
 
202
 
 
203
    def test_coalesce_overlapped(self):
 
204
        self.assertRaises(ValueError,
 
205
            self.check, [(0, 15, [(0, 10), (5, 10)])],
 
206
                        [(0, 10), (5, 10)])
 
207
 
 
208
    def test_coalesce_limit(self):
 
209
        self.check([(10, 50, [(0, 10), (10, 10), (20, 10),
 
210
                              (30, 10), (40, 10)]),
 
211
                    (60, 50, [(0, 10), (10, 10), (20, 10),
 
212
                              (30, 10), (40, 10)]),
 
213
                   ], [(10, 10), (20, 10), (30, 10), (40, 10),
 
214
                       (50, 10), (60, 10), (70, 10), (80, 10),
 
215
                       (90, 10), (100, 10)],
 
216
                    limit=5)
 
217
 
 
218
    def test_coalesce_no_limit(self):
 
219
        self.check([(10, 100, [(0, 10), (10, 10), (20, 10),
 
220
                               (30, 10), (40, 10), (50, 10),
 
221
                               (60, 10), (70, 10), (80, 10),
 
222
                               (90, 10)]),
 
223
                   ], [(10, 10), (20, 10), (30, 10), (40, 10),
 
224
                       (50, 10), (60, 10), (70, 10), (80, 10),
 
225
                       (90, 10), (100, 10)])
 
226
 
 
227
    def test_coalesce_fudge(self):
 
228
        self.check([(10, 30, [(0, 10), (20, 10)]),
 
229
                    (100, 10, [(0, 10),]),
 
230
                   ], [(10, 10), (30, 10), (100, 10)],
 
231
                   fudge=10
 
232
                  )
 
233
    def test_coalesce_max_size(self):
 
234
        self.check([(10, 20, [(0, 10), (10, 10)]),
 
235
                    (30, 50, [(0, 50)]),
 
236
                    # If one range is above max_size, it gets its own coalesced
 
237
                    # offset
 
238
                    (100, 80, [(0, 80),]),],
 
239
                   [(10, 10), (20, 10), (30, 50), (100, 80)],
 
240
                   max_size=50
 
241
                  )
 
242
 
 
243
    def test_coalesce_no_max_size(self):
 
244
        self.check([(10, 170, [(0, 10), (10, 10), (20, 50), (70, 100)]),],
 
245
                   [(10, 10), (20, 10), (30, 50), (80, 100)],
 
246
                  )
 
247
 
 
248
    def test_coalesce_default_limit(self):
 
249
        # By default we use a 100MB max size.
 
250
        ten_mb = 10*1024*1024
 
251
        self.check([(0, 10*ten_mb, [(i*ten_mb, ten_mb) for i in range(10)]),
 
252
                    (10*ten_mb, ten_mb, [(0, ten_mb)])],
 
253
                   [(i*ten_mb, ten_mb) for i in range(11)])
 
254
        self.check([(0, 11*ten_mb, [(i*ten_mb, ten_mb) for i in range(11)]),],
 
255
                   [(i*ten_mb, ten_mb) for i in range(11)],
 
256
                   max_size=1*1024*1024*1024)
 
257
 
 
258
 
 
259
class TestMemoryTransport(TestCase):
 
260
 
 
261
    def test_get_transport(self):
 
262
        MemoryTransport()
 
263
 
 
264
    def test_clone(self):
 
265
        transport = MemoryTransport()
 
266
        self.assertTrue(isinstance(transport, MemoryTransport))
 
267
        self.assertEqual("memory:///", transport.clone("/").base)
 
268
 
 
269
    def test_abspath(self):
 
270
        transport = MemoryTransport()
 
271
        self.assertEqual("memory:///relpath", transport.abspath('relpath'))
 
272
 
 
273
    def test_abspath_of_root(self):
 
274
        transport = MemoryTransport()
 
275
        self.assertEqual("memory:///", transport.base)
 
276
        self.assertEqual("memory:///", transport.abspath('/'))
 
277
 
 
278
    def test_abspath_of_relpath_starting_at_root(self):
 
279
        transport = MemoryTransport()
 
280
        self.assertEqual("memory:///foo", transport.abspath('/foo'))
 
281
 
 
282
    def test_append_and_get(self):
 
283
        transport = MemoryTransport()
 
284
        transport.append_bytes('path', 'content')
 
285
        self.assertEqual(transport.get('path').read(), 'content')
 
286
        transport.append_file('path', StringIO('content'))
 
287
        self.assertEqual(transport.get('path').read(), 'contentcontent')
 
288
 
 
289
    def test_put_and_get(self):
 
290
        transport = MemoryTransport()
 
291
        transport.put_file('path', StringIO('content'))
 
292
        self.assertEqual(transport.get('path').read(), 'content')
 
293
        transport.put_bytes('path', 'content')
 
294
        self.assertEqual(transport.get('path').read(), 'content')
 
295
 
 
296
    def test_append_without_dir_fails(self):
 
297
        transport = MemoryTransport()
 
298
        self.assertRaises(NoSuchFile,
 
299
                          transport.append_bytes, 'dir/path', 'content')
 
300
 
 
301
    def test_put_without_dir_fails(self):
 
302
        transport = MemoryTransport()
 
303
        self.assertRaises(NoSuchFile,
 
304
                          transport.put_file, 'dir/path', StringIO('content'))
 
305
 
 
306
    def test_get_missing(self):
 
307
        transport = MemoryTransport()
 
308
        self.assertRaises(NoSuchFile, transport.get, 'foo')
 
309
 
 
310
    def test_has_missing(self):
 
311
        transport = MemoryTransport()
 
312
        self.assertEquals(False, transport.has('foo'))
 
313
 
 
314
    def test_has_present(self):
 
315
        transport = MemoryTransport()
 
316
        transport.append_bytes('foo', 'content')
 
317
        self.assertEquals(True, transport.has('foo'))
 
318
 
 
319
    def test_list_dir(self):
 
320
        transport = MemoryTransport()
 
321
        transport.put_bytes('foo', 'content')
 
322
        transport.mkdir('dir')
 
323
        transport.put_bytes('dir/subfoo', 'content')
 
324
        transport.put_bytes('dirlike', 'content')
 
325
 
 
326
        self.assertEquals(['dir', 'dirlike', 'foo'], sorted(transport.list_dir('.')))
 
327
        self.assertEquals(['subfoo'], sorted(transport.list_dir('dir')))
 
328
 
 
329
    def test_mkdir(self):
 
330
        transport = MemoryTransport()
 
331
        transport.mkdir('dir')
 
332
        transport.append_bytes('dir/path', 'content')
 
333
        self.assertEqual(transport.get('dir/path').read(), 'content')
 
334
 
 
335
    def test_mkdir_missing_parent(self):
 
336
        transport = MemoryTransport()
 
337
        self.assertRaises(NoSuchFile,
 
338
                          transport.mkdir, 'dir/dir')
 
339
 
 
340
    def test_mkdir_twice(self):
 
341
        transport = MemoryTransport()
 
342
        transport.mkdir('dir')
 
343
        self.assertRaises(FileExists, transport.mkdir, 'dir')
 
344
 
 
345
    def test_parameters(self):
 
346
        transport = MemoryTransport()
 
347
        self.assertEqual(True, transport.listable())
 
348
        self.assertEqual(False, transport.is_readonly())
 
349
 
 
350
    def test_iter_files_recursive(self):
 
351
        transport = MemoryTransport()
 
352
        transport.mkdir('dir')
 
353
        transport.put_bytes('dir/foo', 'content')
 
354
        transport.put_bytes('dir/bar', 'content')
 
355
        transport.put_bytes('bar', 'content')
 
356
        paths = set(transport.iter_files_recursive())
 
357
        self.assertEqual(set(['dir/foo', 'dir/bar', 'bar']), paths)
 
358
 
 
359
    def test_stat(self):
 
360
        transport = MemoryTransport()
 
361
        transport.put_bytes('foo', 'content')
 
362
        transport.put_bytes('bar', 'phowar')
 
363
        self.assertEqual(7, transport.stat('foo').st_size)
 
364
        self.assertEqual(6, transport.stat('bar').st_size)
 
365
 
 
366
 
 
367
class ChrootDecoratorTransportTest(TestCase):
 
368
    """Chroot decoration specific tests."""
 
369
 
 
370
    def test_abspath(self):
 
371
        # The abspath is always relative to the chroot_url.
 
372
        server = ChrootServer(get_transport('memory:///foo/bar/'))
 
373
        self.start_server(server)
 
374
        transport = get_transport(server.get_url())
 
375
        self.assertEqual(server.get_url(), transport.abspath('/'))
 
376
 
 
377
        subdir_transport = transport.clone('subdir')
 
378
        self.assertEqual(server.get_url(), subdir_transport.abspath('/'))
 
379
 
 
380
    def test_clone(self):
 
381
        server = ChrootServer(get_transport('memory:///foo/bar/'))
 
382
        self.start_server(server)
 
383
        transport = get_transport(server.get_url())
 
384
        # relpath from root and root path are the same
 
385
        relpath_cloned = transport.clone('foo')
 
386
        abspath_cloned = transport.clone('/foo')
 
387
        self.assertEqual(server, relpath_cloned.server)
 
388
        self.assertEqual(server, abspath_cloned.server)
 
389
 
 
390
    def test_chroot_url_preserves_chroot(self):
 
391
        """Calling get_transport on a chroot transport's base should produce a
 
392
        transport with exactly the same behaviour as the original chroot
 
393
        transport.
 
394
 
 
395
        This is so that it is not possible to escape a chroot by doing::
 
396
            url = chroot_transport.base
 
397
            parent_url = urlutils.join(url, '..')
 
398
            new_transport = get_transport(parent_url)
 
399
        """
 
400
        server = ChrootServer(get_transport('memory:///path/subpath'))
 
401
        self.start_server(server)
 
402
        transport = get_transport(server.get_url())
 
403
        new_transport = get_transport(transport.base)
 
404
        self.assertEqual(transport.server, new_transport.server)
 
405
        self.assertEqual(transport.base, new_transport.base)
 
406
 
 
407
    def test_urljoin_preserves_chroot(self):
 
408
        """Using urlutils.join(url, '..') on a chroot URL should not produce a
 
409
        URL that escapes the intended chroot.
 
410
 
 
411
        This is so that it is not possible to escape a chroot by doing::
 
412
            url = chroot_transport.base
 
413
            parent_url = urlutils.join(url, '..')
 
414
            new_transport = get_transport(parent_url)
 
415
        """
 
416
        server = ChrootServer(get_transport('memory:///path/'))
 
417
        self.start_server(server)
 
418
        transport = get_transport(server.get_url())
 
419
        self.assertRaises(
 
420
            InvalidURLJoin, urlutils.join, transport.base, '..')
 
421
 
 
422
 
 
423
class ChrootServerTest(TestCase):
 
424
 
 
425
    def test_construct(self):
 
426
        backing_transport = MemoryTransport()
 
427
        server = ChrootServer(backing_transport)
 
428
        self.assertEqual(backing_transport, server.backing_transport)
 
429
 
 
430
    def test_setUp(self):
 
431
        backing_transport = MemoryTransport()
 
432
        server = ChrootServer(backing_transport)
 
433
        server.setUp()
 
434
        try:
 
435
            self.assertTrue(server.scheme in _get_protocol_handlers().keys())
 
436
        finally:
 
437
            server.tearDown()
 
438
 
 
439
    def test_tearDown(self):
 
440
        backing_transport = MemoryTransport()
 
441
        server = ChrootServer(backing_transport)
 
442
        server.setUp()
 
443
        server.tearDown()
 
444
        self.assertFalse(server.scheme in _get_protocol_handlers().keys())
 
445
 
 
446
    def test_get_url(self):
 
447
        backing_transport = MemoryTransport()
 
448
        server = ChrootServer(backing_transport)
 
449
        server.setUp()
 
450
        try:
 
451
            self.assertEqual('chroot-%d:///' % id(server), server.get_url())
 
452
        finally:
 
453
            server.tearDown()
 
454
 
 
455
 
 
456
class PathFilteringDecoratorTransportTest(TestCase):
 
457
    """Pathfilter decoration specific tests."""
 
458
 
 
459
    def test_abspath(self):
 
460
        # The abspath is always relative to the base of the backing transport.
 
461
        server = PathFilteringServer(get_transport('memory:///foo/bar/'),
 
462
            lambda x: x)
 
463
        server.setUp()
 
464
        transport = get_transport(server.get_url())
 
465
        self.assertEqual(server.get_url(), transport.abspath('/'))
 
466
 
 
467
        subdir_transport = transport.clone('subdir')
 
468
        self.assertEqual(server.get_url(), subdir_transport.abspath('/'))
 
469
        server.tearDown()
 
470
 
 
471
    def make_pf_transport(self, filter_func=None):
 
472
        """Make a PathFilteringTransport backed by a MemoryTransport.
 
473
        
 
474
        :param filter_func: by default this will be a no-op function.  Use this
 
475
            parameter to override it."""
 
476
        if filter_func is None:
 
477
            filter_func = lambda x: x
 
478
        server = PathFilteringServer(
 
479
            get_transport('memory:///foo/bar/'), filter_func)
 
480
        server.setUp()
 
481
        self.addCleanup(server.tearDown)
 
482
        return get_transport(server.get_url())
 
483
 
 
484
    def test__filter(self):
 
485
        # _filter (with an identity func as filter_func) always returns
 
486
        # paths relative to the base of the backing transport.
 
487
        transport = self.make_pf_transport()
 
488
        self.assertEqual('foo', transport._filter('foo'))
 
489
        self.assertEqual('foo/bar', transport._filter('foo/bar'))
 
490
        self.assertEqual('', transport._filter('..'))
 
491
        self.assertEqual('', transport._filter('/'))
 
492
        # The base of the pathfiltering transport is taken into account too.
 
493
        transport = transport.clone('subdir1/subdir2')
 
494
        self.assertEqual('subdir1/subdir2/foo', transport._filter('foo'))
 
495
        self.assertEqual(
 
496
            'subdir1/subdir2/foo/bar', transport._filter('foo/bar'))
 
497
        self.assertEqual('subdir1', transport._filter('..'))
 
498
        self.assertEqual('', transport._filter('/'))
 
499
 
 
500
    def test_filter_invocation(self):
 
501
        filter_log = []
 
502
        def filter(path):
 
503
            filter_log.append(path)
 
504
            return path
 
505
        transport = self.make_pf_transport(filter)
 
506
        transport.has('abc')
 
507
        self.assertEqual(['abc'], filter_log)
 
508
        del filter_log[:]
 
509
        transport.clone('abc').has('xyz')
 
510
        self.assertEqual(['abc/xyz'], filter_log)
 
511
        del filter_log[:]
 
512
        transport.has('/abc')
 
513
        self.assertEqual(['abc'], filter_log)
 
514
 
 
515
    def test_clone(self):
 
516
        transport = self.make_pf_transport()
 
517
        # relpath from root and root path are the same
 
518
        relpath_cloned = transport.clone('foo')
 
519
        abspath_cloned = transport.clone('/foo')
 
520
        self.assertEqual(transport.server, relpath_cloned.server)
 
521
        self.assertEqual(transport.server, abspath_cloned.server)
 
522
 
 
523
    def test_url_preserves_pathfiltering(self):
 
524
        """Calling get_transport on a pathfiltered transport's base should
 
525
        produce a transport with exactly the same behaviour as the original
 
526
        pathfiltered transport.
 
527
 
 
528
        This is so that it is not possible to escape (accidentally or
 
529
        otherwise) the filtering by doing::
 
530
            url = filtered_transport.base
 
531
            parent_url = urlutils.join(url, '..')
 
532
            new_transport = get_transport(parent_url)
 
533
        """
 
534
        transport = self.make_pf_transport()
 
535
        new_transport = get_transport(transport.base)
 
536
        self.assertEqual(transport.server, new_transport.server)
 
537
        self.assertEqual(transport.base, new_transport.base)
 
538
 
 
539
 
 
540
class ReadonlyDecoratorTransportTest(TestCase):
 
541
    """Readonly decoration specific tests."""
 
542
 
 
543
    def test_local_parameters(self):
 
544
        import bzrlib.transport.readonly as readonly
 
545
        # connect to . in readonly mode
 
546
        transport = readonly.ReadonlyTransportDecorator('readonly+.')
 
547
        self.assertEqual(True, transport.listable())
 
548
        self.assertEqual(True, transport.is_readonly())
 
549
 
 
550
    def test_http_parameters(self):
 
551
        from bzrlib.tests.http_server import HttpServer
 
552
        import bzrlib.transport.readonly as readonly
 
553
        # connect to '.' via http which is not listable
 
554
        server = HttpServer()
 
555
        self.start_server(server)
 
556
        transport = get_transport('readonly+' + server.get_url())
 
557
        self.failUnless(isinstance(transport,
 
558
                                   readonly.ReadonlyTransportDecorator))
 
559
        self.assertEqual(False, transport.listable())
 
560
        self.assertEqual(True, transport.is_readonly())
 
561
 
 
562
 
 
563
class FakeNFSDecoratorTests(TestCaseInTempDir):
 
564
    """NFS decorator specific tests."""
 
565
 
 
566
    def get_nfs_transport(self, url):
 
567
        import bzrlib.transport.fakenfs as fakenfs
 
568
        # connect to url with nfs decoration
 
569
        return fakenfs.FakeNFSTransportDecorator('fakenfs+' + url)
 
570
 
 
571
    def test_local_parameters(self):
 
572
        # the listable and is_readonly parameters
 
573
        # are not changed by the fakenfs decorator
 
574
        transport = self.get_nfs_transport('.')
 
575
        self.assertEqual(True, transport.listable())
 
576
        self.assertEqual(False, transport.is_readonly())
 
577
 
 
578
    def test_http_parameters(self):
 
579
        # the listable and is_readonly parameters
 
580
        # are not changed by the fakenfs decorator
 
581
        from bzrlib.tests.http_server import HttpServer
 
582
        # connect to '.' via http which is not listable
 
583
        server = HttpServer()
 
584
        self.start_server(server)
 
585
        transport = self.get_nfs_transport(server.get_url())
 
586
        self.assertIsInstance(
 
587
            transport, bzrlib.transport.fakenfs.FakeNFSTransportDecorator)
 
588
        self.assertEqual(False, transport.listable())
 
589
        self.assertEqual(True, transport.is_readonly())
 
590
 
 
591
    def test_fakenfs_server_default(self):
 
592
        # a FakeNFSServer() should bring up a local relpath server for itself
 
593
        import bzrlib.transport.fakenfs as fakenfs
 
594
        server = fakenfs.FakeNFSServer()
 
595
        self.start_server(server)
 
596
        # the url should be decorated appropriately
 
597
        self.assertStartsWith(server.get_url(), 'fakenfs+')
 
598
        # and we should be able to get a transport for it
 
599
        transport = get_transport(server.get_url())
 
600
        # which must be a FakeNFSTransportDecorator instance.
 
601
        self.assertIsInstance(transport, fakenfs.FakeNFSTransportDecorator)
 
602
 
 
603
    def test_fakenfs_rename_semantics(self):
 
604
        # a FakeNFS transport must mangle the way rename errors occur to
 
605
        # look like NFS problems.
 
606
        transport = self.get_nfs_transport('.')
 
607
        self.build_tree(['from/', 'from/foo', 'to/', 'to/bar'],
 
608
                        transport=transport)
 
609
        self.assertRaises(errors.ResourceBusy,
 
610
                          transport.rename, 'from', 'to')
 
611
 
 
612
 
 
613
class FakeVFATDecoratorTests(TestCaseInTempDir):
 
614
    """Tests for simulation of VFAT restrictions"""
 
615
 
 
616
    def get_vfat_transport(self, url):
 
617
        """Return vfat-backed transport for test directory"""
 
618
        from bzrlib.transport.fakevfat import FakeVFATTransportDecorator
 
619
        return FakeVFATTransportDecorator('vfat+' + url)
 
620
 
 
621
    def test_transport_creation(self):
 
622
        from bzrlib.transport.fakevfat import FakeVFATTransportDecorator
 
623
        transport = self.get_vfat_transport('.')
 
624
        self.assertIsInstance(transport, FakeVFATTransportDecorator)
 
625
 
 
626
    def test_transport_mkdir(self):
 
627
        transport = self.get_vfat_transport('.')
 
628
        transport.mkdir('HELLO')
 
629
        self.assertTrue(transport.has('hello'))
 
630
        self.assertTrue(transport.has('Hello'))
 
631
 
 
632
    def test_forbidden_chars(self):
 
633
        transport = self.get_vfat_transport('.')
 
634
        self.assertRaises(ValueError, transport.has, "<NU>")
 
635
 
 
636
 
 
637
class BadTransportHandler(Transport):
 
638
    def __init__(self, base_url):
 
639
        raise DependencyNotPresent('some_lib', 'testing missing dependency')
 
640
 
 
641
 
 
642
class BackupTransportHandler(Transport):
 
643
    """Test transport that works as a backup for the BadTransportHandler"""
 
644
    pass
 
645
 
 
646
 
 
647
class TestTransportImplementation(TestCaseInTempDir):
 
648
    """Implementation verification for transports.
 
649
 
 
650
    To verify a transport we need a server factory, which is a callable
 
651
    that accepts no parameters and returns an implementation of
 
652
    bzrlib.transport.Server.
 
653
 
 
654
    That Server is then used to construct transport instances and test
 
655
    the transport via loopback activity.
 
656
 
 
657
    Currently this assumes that the Transport object is connected to the
 
658
    current working directory.  So that whatever is done
 
659
    through the transport, should show up in the working
 
660
    directory, and vice-versa. This is a bug, because its possible to have
 
661
    URL schemes which provide access to something that may not be
 
662
    result in storage on the local disk, i.e. due to file system limits, or
 
663
    due to it being a database or some other non-filesystem tool.
25
664
 
26
665
    This also tests to make sure that the functions work with both
27
666
    generators and lists (assuming iter(list) is effectively a generator)
28
667
    """
29
 
    import tempfile, os
30
 
    from bzrlib.transport.local import LocalTransport
31
 
 
32
 
    # Test has
33
 
    files = ['a', 'b', 'e', 'g']
34
 
    tester.build_tree(files)
35
 
    tester.assertEqual(t.has('a'), True)
36
 
    tester.assertEqual(t.has('c'), False)
37
 
    tester.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
38
 
            [True, True, False, False, True, False, True, False])
39
 
    tester.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
40
 
            [True, True, False, False, True, False, True, False])
41
 
 
42
 
    # Test get
43
 
    tester.assertEqual(t.get('a').read(), open('a').read())
44
 
    content_f = t.get_multi(files)
45
 
    for path,f in zip(files, content_f):
46
 
        tester.assertEqual(open(path).read(), f.read())
47
 
 
48
 
    content_f = t.get_multi(iter(files))
49
 
    for path,f in zip(files, content_f):
50
 
        tester.assertEqual(open(path).read(), f.read())
51
 
 
52
 
    tester.assertRaises(NoSuchFile, t.get, 'c')
53
 
    try:
54
 
        files = list(t.get_multi(['a', 'b', 'c']))
55
 
    except NoSuchFile:
56
 
        pass
57
 
    else:
58
 
        tester.fail('Failed to raise NoSuchFile for missing file in get_multi')
59
 
    try:
60
 
        files = list(t.get_multi(iter(['a', 'b', 'c', 'e'])))
61
 
    except NoSuchFile:
62
 
        pass
63
 
    else:
64
 
        tester.fail('Failed to raise NoSuchFile for missing file in get_multi')
65
 
 
66
 
    # Test put
67
 
    if readonly:
68
 
        open('c', 'wb').write('some text for c\n')
69
 
    else:
70
 
        t.put('c', 'some text for c\n')
71
 
    tester.assert_(os.path.exists('c'))
72
 
    tester.assertEqual(open('c').read(), 'some text for c\n')
73
 
    tester.assertEqual(t.get('c').read(), 'some text for c\n')
74
 
    # Make sure 'has' is updated
75
 
    tester.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
76
 
            [True, True, True, False, True, False, True, False])
77
 
    if readonly:
78
 
        open('a', 'wb').write('new\ncontents for\na\n')
79
 
        open('d', 'wb').write('contents\nfor d\n')
80
 
    else:
81
 
        # Put also replaces contents
82
 
        tester.assertEqual(t.put_multi([('a', 'new\ncontents for\na\n'),
83
 
                                      ('d', 'contents\nfor d\n')]),
84
 
                         2)
85
 
    tester.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
86
 
            [True, True, True, True, True, False, True, False])
87
 
    tester.assertEqual(open('a').read(), 'new\ncontents for\na\n')
88
 
    tester.assertEqual(open('d').read(), 'contents\nfor d\n')
89
 
 
90
 
    if readonly:
91
 
        open('a', 'wb').write('diff\ncontents for\na\n')
92
 
        open('d', 'wb').write('another contents\nfor d\n')
93
 
    else:
94
 
        tester.assertEqual(
95
 
            t.put_multi(iter([('a', 'diff\ncontents for\na\n'),
96
 
                              ('d', 'another contents\nfor d\n')]))
97
 
                         , 2)
98
 
    tester.assertEqual(open('a').read(), 'diff\ncontents for\na\n')
99
 
    tester.assertEqual(open('d').read(), 'another contents\nfor d\n')
100
 
 
101
 
    if not readonly:
102
 
        tester.assertRaises(NoSuchFile, t.put, 'path/doesnt/exist/c', 'contents')
103
 
 
104
 
    # Test mkdir
105
 
    os.mkdir('dir_a')
106
 
    tester.assertEqual(t.has('dir_a'), True)
107
 
    tester.assertEqual(t.has('dir_b'), False)
108
 
 
109
 
    if readonly:
110
 
        os.mkdir('dir_b')
111
 
    else:
112
 
        t.mkdir('dir_b')
113
 
    tester.assertEqual(t.has('dir_b'), True)
114
 
    tester.assert_(os.path.isdir('dir_b'))
115
 
 
116
 
    if readonly:
117
 
        os.mkdir('dir_c')
118
 
        os.mkdir('dir_d')
119
 
    else:
120
 
        t.mkdir_multi(['dir_c', 'dir_d'])
121
 
    tester.assertEqual(list(t.has_multi(['dir_a', 'dir_b', 'dir_c', 'dir_d', 'dir_e', 'dir_b'])),
122
 
            [True, True, True, True, False, True])
123
 
    for d in ['dir_a', 'dir_b', 'dir_c', 'dir_d']:
124
 
        tester.assert_(os.path.isdir(d))
125
 
 
126
 
    if not readonly:
127
 
        tester.assertRaises(NoSuchFile, t.mkdir, 'path/doesnt/exist')
128
 
        tester.assertRaises(FileExists, t.mkdir, 'dir_a') # Creating a directory again should fail
129
 
 
130
 
    # Make sure the transport recognizes when a
131
 
    # directory is created by other means
132
 
    # Caching Transports will fail, because dir_e was already seen not
133
 
    # to exist. So instead, we will search for a new directory
134
 
    #os.mkdir('dir_e')
135
 
    #if not readonly:
136
 
    #    tester.assertRaises(FileExists, t.mkdir, 'dir_e')
137
 
 
138
 
    os.mkdir('dir_f')
139
 
    if not readonly:
140
 
        tester.assertRaises(FileExists, t.mkdir, 'dir_f')
141
 
 
142
 
    # Test get/put in sub-directories
143
 
    if readonly:
144
 
        open('dir_a/a', 'wb').write('contents of dir_a/a')
145
 
        open('dir_b/b', 'wb').write('contents of dir_b/b')
146
 
    else:
147
 
        tester.assertEqual(
148
 
            t.put_multi([('dir_a/a', 'contents of dir_a/a'),
149
 
                         ('dir_b/b', 'contents of dir_b/b')])
150
 
                      , 2)
151
 
    for f in ('dir_a/a', 'dir_b/b'):
152
 
        tester.assertEqual(t.get(f).read(), open(f).read())
153
 
 
154
 
    # Test copy_to
155
 
    dtmp = tempfile.mkdtemp(dir='.', prefix='test-transport-')
156
 
    dtmp_base = os.path.basename(dtmp)
157
 
    local_t = LocalTransport(dtmp)
158
 
 
159
 
    files = ['a', 'b', 'c', 'd']
160
 
    t.copy_to(files, local_t)
161
 
    for f in files:
162
 
        tester.assertEquals(open(f).read(), open(os.path.join(dtmp_base, f)).read())
163
 
 
164
 
    # TODO: Test append
165
 
    # TODO: Make sure all entries support file-like objects as well as strings.
166
 
 
167
 
class LocalTransportTest(TestCaseInTempDir):
168
 
    def test_local_transport(self):
169
 
        from bzrlib.transport.local import LocalTransport
170
 
 
171
 
        t = LocalTransport('.')
172
 
        test_transport(self, t)
173
 
 
174
 
class HttpServer(object):
175
 
    """This just encapsulates spawning and stopping
176
 
    an httpserver.
177
 
    """
178
 
    def __init__(self):
179
 
        """This just spawns a separate process to serve files from
180
 
        this directory. Call the .stop() function to kill the
181
 
        process.
182
 
        """
183
 
        from BaseHTTPServer import HTTPServer
184
 
        from SimpleHTTPServer import SimpleHTTPRequestHandler
185
 
        import os
186
 
        if hasattr(os, 'fork'):
187
 
            self.pid = os.fork()
188
 
            if self.pid != 0:
189
 
                return
190
 
        else: # How do we handle windows, which doesn't have fork?
191
 
            raise NotImplementedError('At present HttpServer cannot fork on Windows')
192
 
 
193
 
            # We might be able to do something like os.spawn() for the
194
 
            # python executable, and give it a simple script to run.
195
 
            # but then how do we kill it?
196
 
 
197
 
        try:
198
 
            self.s = HTTPServer(('', 9999), SimpleHTTPRequestHandler)
199
 
            # TODO: Is there something nicer than killing the server when done?
200
 
            self.s.serve_forever()
201
 
        except KeyboardInterrupt:
202
 
            pass
203
 
        os._exit(0)
204
 
 
205
 
    def stop(self):
206
 
        import os
207
 
        if self.pid is None:
208
 
            return
209
 
        if hasattr(os, 'kill'):
210
 
            import signal
211
 
            os.kill(self.pid, signal.SIGINT)
212
 
            os.waitpid(self.pid, 0)
213
 
            self.pid = None
 
668
 
 
669
    def setUp(self):
 
670
        super(TestTransportImplementation, self).setUp()
 
671
        self._server = self.transport_server()
 
672
        self.start_server(self._server)
 
673
 
 
674
    def get_transport(self, relpath=None):
 
675
        """Return a connected transport to the local directory.
 
676
 
 
677
        :param relpath: a path relative to the base url.
 
678
        """
 
679
        base_url = self._server.get_url()
 
680
        url = self._adjust_url(base_url, relpath)
 
681
        # try getting the transport via the regular interface:
 
682
        t = get_transport(url)
 
683
        # vila--20070607 if the following are commented out the test suite
 
684
        # still pass. Is this really still needed or was it a forgotten
 
685
        # temporary fix ?
 
686
        if not isinstance(t, self.transport_class):
 
687
            # we did not get the correct transport class type. Override the
 
688
            # regular connection behaviour by direct construction.
 
689
            t = self.transport_class(url)
 
690
        return t
 
691
 
 
692
 
 
693
class TestLocalTransports(TestCase):
 
694
 
 
695
    def test_get_transport_from_abspath(self):
 
696
        here = osutils.abspath('.')
 
697
        t = get_transport(here)
 
698
        self.assertIsInstance(t, LocalTransport)
 
699
        self.assertEquals(t.base, urlutils.local_path_to_url(here) + '/')
 
700
 
 
701
    def test_get_transport_from_relpath(self):
 
702
        here = osutils.abspath('.')
 
703
        t = get_transport('.')
 
704
        self.assertIsInstance(t, LocalTransport)
 
705
        self.assertEquals(t.base, urlutils.local_path_to_url('.') + '/')
 
706
 
 
707
    def test_get_transport_from_local_url(self):
 
708
        here = osutils.abspath('.')
 
709
        here_url = urlutils.local_path_to_url(here) + '/'
 
710
        t = get_transport(here_url)
 
711
        self.assertIsInstance(t, LocalTransport)
 
712
        self.assertEquals(t.base, here_url)
 
713
 
 
714
    def test_local_abspath(self):
 
715
        here = osutils.abspath('.')
 
716
        t = get_transport(here)
 
717
        self.assertEquals(t.local_abspath(''), here)
 
718
 
 
719
 
 
720
class TestWin32LocalTransport(TestCase):
 
721
 
 
722
    def test_unc_clone_to_root(self):
 
723
        # Win32 UNC path like \\HOST\path
 
724
        # clone to root should stop at least at \\HOST part
 
725
        # not on \\
 
726
        t = EmulatedWin32LocalTransport('file://HOST/path/to/some/dir/')
 
727
        for i in xrange(4):
 
728
            t = t.clone('..')
 
729
        self.assertEquals(t.base, 'file://HOST/')
 
730
        # make sure we reach the root
 
731
        t = t.clone('..')
 
732
        self.assertEquals(t.base, 'file://HOST/')
 
733
 
 
734
 
 
735
class TestConnectedTransport(TestCase):
 
736
    """Tests for connected to remote server transports"""
 
737
 
 
738
    def test_parse_url(self):
 
739
        t = ConnectedTransport('http://simple.example.com/home/source')
 
740
        self.assertEquals(t._host, 'simple.example.com')
 
741
        self.assertEquals(t._port, None)
 
742
        self.assertEquals(t._path, '/home/source/')
 
743
        self.failUnless(t._user is None)
 
744
        self.failUnless(t._password is None)
 
745
 
 
746
        self.assertEquals(t.base, 'http://simple.example.com/home/source/')
 
747
 
 
748
    def test_parse_url_with_at_in_user(self):
 
749
        # Bug 228058
 
750
        t = ConnectedTransport('ftp://user@host.com@www.host.com/')
 
751
        self.assertEquals(t._user, 'user@host.com')
 
752
 
 
753
    def test_parse_quoted_url(self):
 
754
        t = ConnectedTransport('http://ro%62ey:h%40t@ex%41mple.com:2222/path')
 
755
        self.assertEquals(t._host, 'exAmple.com')
 
756
        self.assertEquals(t._port, 2222)
 
757
        self.assertEquals(t._user, 'robey')
 
758
        self.assertEquals(t._password, 'h@t')
 
759
        self.assertEquals(t._path, '/path/')
 
760
 
 
761
        # Base should not keep track of the password
 
762
        self.assertEquals(t.base, 'http://robey@exAmple.com:2222/path/')
 
763
 
 
764
    def test_parse_invalid_url(self):
 
765
        self.assertRaises(errors.InvalidURL,
 
766
                          ConnectedTransport,
 
767
                          'sftp://lily.org:~janneke/public/bzr/gub')
 
768
 
 
769
    def test_relpath(self):
 
770
        t = ConnectedTransport('sftp://user@host.com/abs/path')
 
771
 
 
772
        self.assertEquals(t.relpath('sftp://user@host.com/abs/path/sub'), 'sub')
 
773
        self.assertRaises(errors.PathNotChild, t.relpath,
 
774
                          'http://user@host.com/abs/path/sub')
 
775
        self.assertRaises(errors.PathNotChild, t.relpath,
 
776
                          'sftp://user2@host.com/abs/path/sub')
 
777
        self.assertRaises(errors.PathNotChild, t.relpath,
 
778
                          'sftp://user@otherhost.com/abs/path/sub')
 
779
        self.assertRaises(errors.PathNotChild, t.relpath,
 
780
                          'sftp://user@host.com:33/abs/path/sub')
 
781
        # Make sure it works when we don't supply a username
 
782
        t = ConnectedTransport('sftp://host.com/abs/path')
 
783
        self.assertEquals(t.relpath('sftp://host.com/abs/path/sub'), 'sub')
 
784
 
 
785
        # Make sure it works when parts of the path will be url encoded
 
786
        t = ConnectedTransport('sftp://host.com/dev/%path')
 
787
        self.assertEquals(t.relpath('sftp://host.com/dev/%path/sub'), 'sub')
 
788
 
 
789
    def test_connection_sharing_propagate_credentials(self):
 
790
        t = ConnectedTransport('ftp://user@host.com/abs/path')
 
791
        self.assertEquals('user', t._user)
 
792
        self.assertEquals('host.com', t._host)
 
793
        self.assertIs(None, t._get_connection())
 
794
        self.assertIs(None, t._password)
 
795
        c = t.clone('subdir')
 
796
        self.assertIs(None, c._get_connection())
 
797
        self.assertIs(None, t._password)
 
798
 
 
799
        # Simulate the user entering a password
 
800
        password = 'secret'
 
801
        connection = object()
 
802
        t._set_connection(connection, password)
 
803
        self.assertIs(connection, t._get_connection())
 
804
        self.assertIs(password, t._get_credentials())
 
805
        self.assertIs(connection, c._get_connection())
 
806
        self.assertIs(password, c._get_credentials())
 
807
 
 
808
        # credentials can be updated
 
809
        new_password = 'even more secret'
 
810
        c._update_credentials(new_password)
 
811
        self.assertIs(connection, t._get_connection())
 
812
        self.assertIs(new_password, t._get_credentials())
 
813
        self.assertIs(connection, c._get_connection())
 
814
        self.assertIs(new_password, c._get_credentials())
 
815
 
 
816
 
 
817
class TestReusedTransports(TestCase):
 
818
    """Tests for transport reuse"""
 
819
 
 
820
    def test_reuse_same_transport(self):
 
821
        possible_transports = []
 
822
        t1 = get_transport('http://foo/',
 
823
                           possible_transports=possible_transports)
 
824
        self.assertEqual([t1], possible_transports)
 
825
        t2 = get_transport('http://foo/', possible_transports=[t1])
 
826
        self.assertIs(t1, t2)
 
827
 
 
828
        # Also check that final '/' are handled correctly
 
829
        t3 = get_transport('http://foo/path/')
 
830
        t4 = get_transport('http://foo/path', possible_transports=[t3])
 
831
        self.assertIs(t3, t4)
 
832
 
 
833
        t5 = get_transport('http://foo/path')
 
834
        t6 = get_transport('http://foo/path/', possible_transports=[t5])
 
835
        self.assertIs(t5, t6)
 
836
 
 
837
    def test_don_t_reuse_different_transport(self):
 
838
        t1 = get_transport('http://foo/path')
 
839
        t2 = get_transport('http://bar/path', possible_transports=[t1])
 
840
        self.assertIsNot(t1, t2)
 
841
 
 
842
 
 
843
class TestTransportTrace(TestCase):
 
844
 
 
845
    def test_get(self):
 
846
        transport = get_transport('trace+memory://')
 
847
        self.assertIsInstance(
 
848
            transport, bzrlib.transport.trace.TransportTraceDecorator)
 
849
 
 
850
    def test_clone_preserves_activity(self):
 
851
        transport = get_transport('trace+memory://')
 
852
        transport2 = transport.clone('.')
 
853
        self.assertTrue(transport is not transport2)
 
854
        self.assertTrue(transport._activity is transport2._activity)
 
855
 
 
856
    # the following specific tests are for the operations that have made use of
 
857
    # logging in tests; we could test every single operation but doing that
 
858
    # still won't cause a test failure when the top level Transport API
 
859
    # changes; so there is little return doing that.
 
860
    def test_get(self):
 
861
        transport = get_transport('trace+memory:///')
 
862
        transport.put_bytes('foo', 'barish')
 
863
        transport.get('foo')
 
864
        expected_result = []
 
865
        # put_bytes records the bytes, not the content to avoid memory
 
866
        # pressure.
 
867
        expected_result.append(('put_bytes', 'foo', 6, None))
 
868
        # get records the file name only.
 
869
        expected_result.append(('get', 'foo'))
 
870
        self.assertEqual(expected_result, transport._activity)
 
871
 
 
872
    def test_readv(self):
 
873
        transport = get_transport('trace+memory:///')
 
874
        transport.put_bytes('foo', 'barish')
 
875
        list(transport.readv('foo', [(0, 1), (3, 2)], adjust_for_latency=True,
 
876
            upper_limit=6))
 
877
        expected_result = []
 
878
        # put_bytes records the bytes, not the content to avoid memory
 
879
        # pressure.
 
880
        expected_result.append(('put_bytes', 'foo', 6, None))
 
881
        # readv records the supplied offset request
 
882
        expected_result.append(('readv', 'foo', [(0, 1), (3, 2)], True, 6))
 
883
        self.assertEqual(expected_result, transport._activity)
 
884
 
 
885
 
 
886
class TestSSHConnections(tests.TestCaseWithTransport):
 
887
 
 
888
    def test_bzr_connect_to_bzr_ssh(self):
 
889
        """User acceptance that get_transport of a bzr+ssh:// behaves correctly.
 
890
 
 
891
        bzr+ssh:// should cause bzr to run a remote bzr smart server over SSH.
 
892
        """
 
893
        # This test actually causes a bzr instance to be invoked, which is very
 
894
        # expensive: it should be the only such test in the test suite.
 
895
        # A reasonable evolution for this would be to simply check inside
 
896
        # check_channel_exec_request that the command is appropriate, and then
 
897
        # satisfy requests in-process.
 
898
        self.requireFeature(ParamikoFeature)
 
899
        # SFTPFullAbsoluteServer has a get_url method, and doesn't
 
900
        # override the interface (doesn't change self._vendor).
 
901
        # Note that this does encryption, so can be slow.
 
902
        from bzrlib.transport.sftp import SFTPFullAbsoluteServer
 
903
        from bzrlib.tests.stub_sftp import StubServer
 
904
 
 
905
        # Start an SSH server
 
906
        self.command_executed = []
 
907
        # XXX: This is horrible -- we define a really dumb SSH server that
 
908
        # executes commands, and manage the hooking up of stdin/out/err to the
 
909
        # SSH channel ourselves.  Surely this has already been implemented
 
910
        # elsewhere?
 
911
        class StubSSHServer(StubServer):
 
912
 
 
913
            test = self
 
914
 
 
915
            def check_channel_exec_request(self, channel, command):
 
916
                self.test.command_executed.append(command)
 
917
                proc = subprocess.Popen(
 
918
                    command, shell=True, stdin=subprocess.PIPE,
 
919
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
920
 
 
921
                # XXX: horribly inefficient, not to mention ugly.
 
922
                # Start a thread for each of stdin/out/err, and relay bytes from
 
923
                # the subprocess to channel and vice versa.
 
924
                def ferry_bytes(read, write, close):
 
925
                    while True:
 
926
                        bytes = read(1)
 
927
                        if bytes == '':
 
928
                            close()
 
929
                            break
 
930
                        write(bytes)
 
931
 
 
932
                file_functions = [
 
933
                    (channel.recv, proc.stdin.write, proc.stdin.close),
 
934
                    (proc.stdout.read, channel.sendall, channel.close),
 
935
                    (proc.stderr.read, channel.sendall_stderr, channel.close)]
 
936
                for read, write, close in file_functions:
 
937
                    t = threading.Thread(
 
938
                        target=ferry_bytes, args=(read, write, close))
 
939
                    t.start()
 
940
 
 
941
                return True
 
942
 
 
943
        ssh_server = SFTPFullAbsoluteServer(StubSSHServer)
 
944
        # We *don't* want to override the default SSH vendor: the detected one
 
945
        # is the one to use.
 
946
        self.start_server(ssh_server)
 
947
        port = ssh_server._listener.port
 
948
 
 
949
        if sys.platform == 'win32':
 
950
            bzr_remote_path = sys.executable + ' ' + self.get_bzr_path()
214
951
        else:
215
 
            raise NotImplementedError('At present HttpServer cannot stop on Windows')
216
 
 
217
 
class HttpTransportTest(TestCaseInTempDir):
218
 
    def test_http_transport(self):
219
 
        from bzrlib.transport.http import HttpTransport
220
 
 
221
 
        s = HttpServer()
222
 
 
223
 
        t = HttpTransport('http://localhost:9999/')
224
 
        test_transport(self, t, readonly=True)
225
 
 
226
 
        s.stop()
227
 
 
 
952
            bzr_remote_path = self.get_bzr_path()
 
953
        os.environ['BZR_REMOTE_PATH'] = bzr_remote_path
 
954
 
 
955
        # Access the branch via a bzr+ssh URL.  The BZR_REMOTE_PATH environment
 
956
        # variable is used to tell bzr what command to run on the remote end.
 
957
        path_to_branch = osutils.abspath('.')
 
958
        if sys.platform == 'win32':
 
959
            # On Windows, we export all drives as '/C:/, etc. So we need to
 
960
            # prefix a '/' to get the right path.
 
961
            path_to_branch = '/' + path_to_branch
 
962
        url = 'bzr+ssh://fred:secret@localhost:%d%s' % (port, path_to_branch)
 
963
        t = get_transport(url)
 
964
        self.permit_url(t.base)
 
965
        t.mkdir('foo')
 
966
 
 
967
        self.assertEqual(
 
968
            ['%s serve --inet --directory=/ --allow-writes' % bzr_remote_path],
 
969
            self.command_executed)