73
63
super(TestCaseWithSFTPServer, self).setUp()
74
if not paramiko_loaded:
75
raise TestSkipped('you must have paramiko to run this test')
64
self.requireFeature(features.paramiko)
76
65
set_test_transport_to_sftp(self)
79
class SFTPLockTests (TestCaseWithSFTPServer):
68
class SFTPLockTests(TestCaseWithSFTPServer):
81
70
def test_sftp_locks(self):
82
71
from bzrlib.errors import LockError
83
72
t = self.get_transport()
85
74
l = t.lock_write('bogus')
86
self.failUnlessExists('bogus.write-lock')
75
self.assertPathExists('bogus.write-lock')
88
77
# Don't wait for the lock, locking an already locked
89
78
# file should raise an assert
90
79
self.assertRaises(LockError, t.lock_write, 'bogus')
93
self.failIf(lexists('bogus.write-lock'))
82
self.assertFalse(lexists('bogus.write-lock'))
95
open('something.write-lock', 'wb').write('fake lock\n')
84
with open('something.write-lock', 'wb') as f: f.write('fake lock\n')
96
85
self.assertRaises(LockError, t.lock_write, 'something')
97
86
os.remove('something.write-lock')
151
140
def test__remote_path_relative_root(self):
152
141
# relative paths are preserved
153
142
t = self.get_transport('')
154
self.assertEqual('/~/', t._path)
143
self.assertEqual('/~/', t._parsed_url.path)
155
144
# the remote path should be relative to home dir
156
145
# (i.e. not begining with a '/')
157
146
self.assertEqual('a', t._remote_path('a'))
160
149
class SFTPNonServerTest(TestCase):
163
if not paramiko_loaded:
164
raise TestSkipped('you must have paramiko to run this test')
152
super(SFTPNonServerTest, self).setUp()
153
self.requireFeature(features.paramiko)
166
155
def test_parse_url_with_home_dir(self):
167
s = SFTPTransport('sftp://ro%62ey:h%40t@example.com:2222/~/relative')
168
self.assertEquals(s._host, 'example.com')
169
self.assertEquals(s._port, 2222)
170
self.assertEquals(s._user, 'robey')
171
self.assertEquals(s._password, 'h@t')
172
self.assertEquals(s._path, '/~/relative/')
156
s = _mod_sftp.SFTPTransport(
157
'sftp://ro%62ey:h%40t@example.com:2222/~/relative')
158
self.assertEqual(s._parsed_url.host, 'example.com')
159
self.assertEqual(s._parsed_url.port, 2222)
160
self.assertEqual(s._parsed_url.user, 'robey')
161
self.assertEqual(s._parsed_url.password, 'h@t')
162
self.assertEqual(s._parsed_url.path, '/~/relative/')
174
164
def test_relpath(self):
175
s = SFTPTransport('sftp://user@host.com/abs/path')
165
s = _mod_sftp.SFTPTransport('sftp://user@host.com/abs/path')
176
166
self.assertRaises(errors.PathNotChild, s.relpath,
177
167
'sftp://user@host.com/~/rel/path/sub')
180
170
"""Test that if no 'ssh' is available we get builtin paramiko"""
181
171
from bzrlib.transport import ssh
182
172
# set '.' as the only location in the path, forcing no 'ssh' to exist
183
orig_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
184
orig_path = set_or_unset_env('PATH', '.')
186
# No vendor defined yet, query for one
187
ssh._ssh_vendor_manager.clear_cache()
188
vendor = ssh._get_ssh_vendor()
189
self.assertIsInstance(vendor, ssh.ParamikoVendor)
191
set_or_unset_env('PATH', orig_path)
192
ssh._ssh_vendor_manager._cached_ssh_vendor = orig_vendor
173
self.overrideAttr(ssh, '_ssh_vendor_manager')
174
self.overrideEnv('PATH', '.')
175
ssh._ssh_vendor_manager.clear_cache()
176
vendor = ssh._get_ssh_vendor()
177
self.assertIsInstance(vendor, ssh.ParamikoVendor)
194
179
def test_abspath_root_sibling_server(self):
195
from bzrlib.transport.sftp import SFTPSiblingAbsoluteServer
196
server = SFTPSiblingAbsoluteServer()
199
transport = get_transport(server.get_url())
200
self.assertFalse(transport.abspath('/').endswith('/~/'))
201
self.assertTrue(transport.abspath('/').endswith('/'))
180
server = stub_sftp.SFTPSiblingAbsoluteServer()
181
server.start_server()
182
self.addCleanup(server.stop_server)
184
transport = _mod_transport.get_transport_from_url(server.get_url())
185
self.assertFalse(transport.abspath('/').endswith('/~/'))
186
self.assertTrue(transport.abspath('/').endswith('/'))
207
190
class SFTPBranchTest(TestCaseWithSFTPServer):
208
191
"""Test some stuff when accessing a bzr Branch over sftp"""
210
def test_lock_file(self):
211
# old format branches use a special lock file on sftp.
212
b = self.make_branch('', format=bzrdir.BzrDirFormat6())
213
b = bzrlib.branch.Branch.open(self.get_url())
214
self.failUnlessExists('.bzr/')
215
self.failUnlessExists('.bzr/branch-format')
216
self.failUnlessExists('.bzr/branch-lock')
218
self.failIf(lexists('.bzr/branch-lock.write-lock'))
220
self.failUnlessExists('.bzr/branch-lock.write-lock')
222
self.failIf(lexists('.bzr/branch-lock.write-lock'))
224
193
def test_push_support(self):
225
194
self.build_tree(['a/', 'a/foo'])
226
t = bzrdir.BzrDir.create_standalone_workingtree('a')
195
t = controldir.ControlDir.create_standalone_workingtree('a')
229
198
t.commit('foo', rev_id='a1')
231
b2 = bzrdir.BzrDir.create_branch_and_repo(self.get_url('/b'))
200
b2 = controldir.ControlDir.create_branch_and_repo(self.get_url('/b'))
234
self.assertEquals(b2.revision_history(), ['a1'])
203
self.assertEqual(b2.last_revision(), 'a1')
236
open('a/foo', 'wt').write('something new in foo\n')
205
with open('a/foo', 'wt') as f: f.write('something new in foo\n')
237
206
t.commit('new', rev_id='a2')
240
self.assertEquals(b2.revision_history(), ['a1', 'a2'])
209
self.assertEqual(b2.last_revision(), 'a2')
243
212
class SSHVendorConnection(TestCaseWithSFTPServer):
305
if not paramiko_loaded:
306
raise TestSkipped('you must have paramiko to run this test')
273
self.requireFeature(features.paramiko)
307
274
super(SSHVendorBadConnection, self).setUp()
308
import bzrlib.transport.ssh
310
276
# open a random port, so we know nobody else is using it
311
277
# but don't actually listen on the port.
312
278
s = socket.socket()
313
279
s.bind(('localhost', 0))
280
self.addCleanup(s.close)
314
281
self.bogus_url = 'sftp://%s:%s/' % s.getsockname()
316
orig_vendor = bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor
318
bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor = orig_vendor
320
self.addCleanup(reset)
322
def set_vendor(self, vendor):
323
import bzrlib.transport.ssh
324
bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor = vendor
283
def set_vendor(self, vendor, subprocess_stderr=None):
284
from bzrlib.transport import ssh
285
self.overrideAttr(ssh._ssh_vendor_manager, '_cached_ssh_vendor', vendor)
286
if subprocess_stderr is not None:
287
self.overrideAttr(ssh.SubprocessVendor, "_stderr_target",
326
290
def test_bad_connection_paramiko(self):
327
291
"""Test that a real connection attempt raises the right error"""
328
292
from bzrlib.transport import ssh
329
293
self.set_vendor(ssh.ParamikoVendor())
330
t = bzrlib.transport.get_transport(self.bogus_url)
294
t = _mod_transport.get_transport_from_url(self.bogus_url)
331
295
self.assertRaises(errors.ConnectionError, t.get, 'foobar')
333
297
def test_bad_connection_ssh(self):
334
298
"""None => auto-detect vendor"""
335
self.set_vendor(None)
336
# This is how I would normally test the connection code
337
# it makes it very clear what we are testing.
338
# However, 'ssh' will create stipple on the output, so instead
339
# I'm using run_bzr_subprocess, and parsing the output
341
# t = bzrlib.transport.get_transport(self.bogus_url)
342
# except errors.ConnectionError:
345
# except errors.NameError, e:
346
# if 'SSHException' in str(e):
347
# raise TestSkipped('Known NameError bug in paramiko 1.6.1')
350
# self.fail('Excepted ConnectionError to be raised')
352
out, err = self.run_bzr_subprocess(['log', self.bogus_url], retcode=3)
353
self.assertEqual('', out)
354
if "NameError: global name 'SSHException'" in err:
355
# We aren't fixing this bug, because it is a bug in
356
# paramiko, but we know about it, so we don't have to
358
raise TestSkipped('Known NameError bug with paramiko-1.6.1')
359
self.assertContainsRe(err, r'bzr: ERROR: Unable to connect to SSH host'
360
r' 127\.0\.0\.1:\d+; ')
299
f = file(os.devnull, "wb")
300
self.addCleanup(f.close)
301
self.set_vendor(None, f)
302
t = _mod_transport.get_transport_from_url(self.bogus_url)
304
self.assertRaises(errors.ConnectionError, t.get, 'foobar')
306
if "global name 'SSHException'" in str(e):
307
self.knownFailure('Known NameError bug in paramiko 1.6.1')
363
311
class SFTPLatencyKnob(TestCaseWithSFTPServer):
364
312
"""Test that the testing SFTPServer's latency knob works."""
366
314
def test_latency_knob_slows_transport(self):
367
# change the latency knob to 500ms. We take about 40ms for a
315
# change the latency knob to 500ms. We take about 40ms for a
368
316
# loopback connection ordinarily.
369
317
start_time = time.time()
370
318
self.get_server().add_latency = 0.5
414
362
class TestSocketDelay(TestCase):
418
if not paramiko_loaded:
419
raise TestSkipped('you must have paramiko to run this test')
365
super(TestSocketDelay, self).setUp()
366
self.requireFeature(features.paramiko)
421
368
def test_delay(self):
422
from bzrlib.transport.sftp import SocketDelay
423
369
sending = FakeSocket()
424
receiving = SocketDelay(sending, 0.1, bandwidth=1000000,
370
receiving = stub_sftp.SocketDelay(sending, 0.1, bandwidth=1000000,
426
372
# check that simulated time is charged only per round-trip:
427
t1 = SocketDelay.simulated_time
373
t1 = stub_sftp.SocketDelay.simulated_time
428
374
receiving.send("connect1")
429
375
self.assertEqual(sending.recv(1024), "connect1")
430
t2 = SocketDelay.simulated_time
376
t2 = stub_sftp.SocketDelay.simulated_time
431
377
self.assertAlmostEqual(t2 - t1, 0.1)
432
378
receiving.send("connect2")
433
379
self.assertEqual(sending.recv(1024), "connect2")
434
380
sending.send("hello")
435
381
self.assertEqual(receiving.recv(1024), "hello")
436
t3 = SocketDelay.simulated_time
382
t3 = stub_sftp.SocketDelay.simulated_time
437
383
self.assertAlmostEqual(t3 - t2, 0.1)
438
384
sending.send("hello")
439
385
self.assertEqual(receiving.recv(1024), "hello")
441
387
self.assertEqual(receiving.recv(1024), "hello")
442
388
sending.send("hello")
443
389
self.assertEqual(receiving.recv(1024), "hello")
444
t4 = SocketDelay.simulated_time
390
t4 = stub_sftp.SocketDelay.simulated_time
445
391
self.assertAlmostEqual(t4, t3)
447
393
def test_bandwidth(self):
448
from bzrlib.transport.sftp import SocketDelay
449
394
sending = FakeSocket()
450
receiving = SocketDelay(sending, 0, bandwidth=8.0/(1024*1024),
395
receiving = stub_sftp.SocketDelay(sending, 0, bandwidth=8.0/(1024*1024),
452
397
# check that simulated time is charged only per round-trip:
453
t1 = SocketDelay.simulated_time
398
t1 = stub_sftp.SocketDelay.simulated_time
454
399
receiving.send("connect")
455
400
self.assertEqual(sending.recv(1024), "connect")
456
401
sending.send("a" * 100)
457
402
self.assertEqual(receiving.recv(1024), "a" * 100)
458
t2 = SocketDelay.simulated_time
403
t2 = stub_sftp.SocketDelay.simulated_time
459
404
self.assertAlmostEqual(t2 - t1, 100 + 7)
407
class ReadvFile(object):
408
"""An object that acts like Paramiko's SFTPFile when readv() is used"""
410
def __init__(self, data):
413
def readv(self, requests):
414
for start, length in requests:
415
yield self._data[start:start+length]
421
def _null_report_activity(*a, **k):
425
class Test_SFTPReadvHelper(tests.TestCase):
427
def checkGetRequests(self, expected_requests, offsets):
428
self.requireFeature(features.paramiko)
429
helper = _mod_sftp._SFTPReadvHelper(offsets, 'artificial_test',
430
_null_report_activity)
431
self.assertEqual(expected_requests, helper._get_requests())
433
def test__get_requests(self):
434
# Small single requests become a single readv request
435
self.checkGetRequests([(0, 100)],
436
[(0, 20), (30, 50), (20, 10), (80, 20)])
437
# Non-contiguous ranges are given as multiple requests
438
self.checkGetRequests([(0, 20), (30, 50)],
439
[(10, 10), (30, 20), (0, 10), (50, 30)])
440
# Ranges larger than _max_request_size (32kB) are broken up into
441
# multiple requests, even if it actually spans multiple logical
443
self.checkGetRequests([(0, 32768), (32768, 32768), (65536, 464)],
444
[(0, 40000), (40000, 100), (40100, 1900),
447
def checkRequestAndYield(self, expected, data, offsets):
448
self.requireFeature(features.paramiko)
449
helper = _mod_sftp._SFTPReadvHelper(offsets, 'artificial_test',
450
_null_report_activity)
451
data_f = ReadvFile(data)
452
result = list(helper.request_and_yield_offsets(data_f))
453
self.assertEqual(expected, result)
455
def test_request_and_yield_offsets(self):
456
data = 'abcdefghijklmnopqrstuvwxyz'
457
self.checkRequestAndYield([(0, 'a'), (5, 'f'), (10, 'klm')], data,
458
[(0, 1), (5, 1), (10, 3)])
459
# Should combine requests, and split them again
460
self.checkRequestAndYield([(0, 'a'), (1, 'b'), (10, 'klm')], data,
461
[(0, 1), (1, 1), (10, 3)])
462
# Out of order requests. The requests should get combined, but then be
463
# yielded out-of-order. We also need one that is at the end of a
464
# previous range. See bug #293746
465
self.checkRequestAndYield([(0, 'a'), (10, 'k'), (4, 'efg'), (1, 'bcd')],
466
data, [(0, 1), (10, 1), (4, 3), (1, 3)])
469
class TestUsesAuthConfig(TestCaseWithSFTPServer):
470
"""Test that AuthenticationConfig can supply default usernames."""
472
def get_transport_for_connection(self, set_config):
473
port = self.get_server().port
475
conf = config.AuthenticationConfig()
476
conf._get_config().update(
477
{'sftptest': {'scheme': 'ssh', 'port': port, 'user': 'bar'}})
479
t = _mod_transport.get_transport_from_url(
480
'sftp://localhost:%d' % port)
481
# force a connection to be performed.
485
def test_sftp_uses_config(self):
486
t = self.get_transport_for_connection(set_config=True)
487
self.assertEqual('bar', t._get_credentials()[0])
489
def test_sftp_is_none_if_no_config(self):
490
t = self.get_transport_for_connection(set_config=False)
491
self.assertIs(None, t._get_credentials()[0])
493
def test_sftp_doesnt_prompt_username(self):
494
stdout = tests.StringIOWrapper()
495
ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n', stdout=stdout)
496
t = self.get_transport_for_connection(set_config=False)
497
self.assertIs(None, t._get_credentials()[0])
498
# No prompts should've been printed, stdin shouldn't have been read
499
self.assertEqual("", stdout.getvalue())
500
self.assertEqual(0, ui.ui_factory.stdin.tell())