~bzr-pqm/bzr/bzr.dev

2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>
2
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
3
#
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
8
#
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
13
#
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18
import os
19
import socket
2321.3.7 by Alexander Belchenko
fixes for passing test_sftp_transport on win32 (thankyou John)
20
import sys
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
21
import threading
1871.1.3 by Robert Collins
proof of concept slowsocket wrapper.
22
import time
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
23
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
24
import bzrlib.bzrdir as bzrdir
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
25
import bzrlib.errors as errors
2013.1.2 by John Arbash Meinel
Add a test that we can always fall back to the paramiko vendor
26
from bzrlib.osutils import pathjoin, lexists, set_or_unset_env
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
27
from bzrlib.tests import TestCaseWithTransport, TestCase, TestSkipped
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
28
from bzrlib.tests.HttpServer import HttpServer
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
29
import bzrlib.transport
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
30
from bzrlib.transport import get_transport
1711.2.132 by John Arbash Meinel
Clean up PEP8 and unused imports in bench_sftp.py, and missing import in bzrlib/tests/test_sftp_transport.py
31
import bzrlib.transport.http
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
32
from bzrlib.workingtree import WorkingTree
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
33
34
try:
35
    import paramiko
36
    paramiko_loaded = True
37
except ImportError:
38
    paramiko_loaded = False
39
1874.1.12 by Carl Friedrich Bolz
More fixes according to John's comments.
40
1874.1.14 by Carl Friedrich Bolz
Rename setup method to make its intent clearer. Some PEP 8 issues.
41
def set_test_transport_to_sftp(testcase):
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
42
    """A helper to set transports on test case instances."""
1874.1.6 by holger krekel
(cfbolz, hpk) Factor out common set_transport code.
43
    from bzrlib.transport.sftp import SFTPAbsoluteServer, SFTPHomeDirServer
44
    if getattr(testcase, '_get_remote_is_absolute', None) is None:
45
        testcase._get_remote_is_absolute = True
46
    if testcase._get_remote_is_absolute:
2018.5.114 by Robert Collins
Commit current test pass improvements.
47
        testcase.transport_server = SFTPAbsoluteServer
1874.1.6 by holger krekel
(cfbolz, hpk) Factor out common set_transport code.
48
    else:
2018.5.114 by Robert Collins
Commit current test pass improvements.
49
        testcase.transport_server = SFTPHomeDirServer
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
50
    testcase.transport_readonly_server = HttpServer
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
51
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
52
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
53
class TestCaseWithSFTPServer(TestCaseWithTransport):
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
54
    """A test case base class that provides a sftp server on localhost."""
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
55
56
    def setUp(self):
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
57
        super(TestCaseWithSFTPServer, self).setUp()
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
58
        if not paramiko_loaded:
59
            raise TestSkipped('you must have paramiko to run this test')
2381.1.1 by Robert Collins
Split out hpss test fixes which dont depend on new or altered API's.
60
        set_test_transport_to_sftp(self)
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
61
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
62
63
class SFTPLockTests (TestCaseWithSFTPServer):
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
64
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
65
    def test_sftp_locks(self):
66
        from bzrlib.errors import LockError
67
        t = self.get_transport()
68
69
        l = t.lock_write('bogus')
70
        self.failUnlessExists('bogus.write-lock')
71
72
        # Don't wait for the lock, locking an already locked
73
        # file should raise an assert
74
        self.assertRaises(LockError, t.lock_write, 'bogus')
75
76
        l.unlock()
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
77
        self.failIf(lexists('bogus.write-lock'))
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
78
79
        open('something.write-lock', 'wb').write('fake lock\n')
80
        self.assertRaises(LockError, t.lock_write, 'something')
81
        os.remove('something.write-lock')
82
83
        l = t.lock_write('something')
84
85
        l2 = t.lock_write('bogus')
86
87
        l.unlock()
88
        l2.unlock()
89
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
90
    def test_multiple_connections(self):
91
        t = self.get_transport()
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
92
        self.assertTrue('sftpserver - new connection' in self.get_server().logs)
93
        self.get_server().logs = []
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
94
        # The second request should reuse the first connection
95
        # SingleListener only allows for a single connection,
96
        # So the next line fails unless the connection is reused
97
        t2 = self.get_transport()
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
98
        self.assertEquals(self.get_server().logs, [])
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
99
100
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
101
class SFTPTransportTestRelative(TestCaseWithSFTPServer):
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
102
    """Test the SFTP transport with homedir based relative paths."""
103
104
    def test__remote_path(self):
105
        t = self.get_transport()
2321.3.7 by Alexander Belchenko
fixes for passing test_sftp_transport on win32 (thankyou John)
106
        # This test require unix-like absolute path
107
        test_dir = self.test_dir
108
        if sys.platform == 'win32':
109
            # using hack suggested by John Meinel.
110
            # TODO: write another mock server for this test
111
            #       and use absolute path without drive letter
112
            test_dir = '/' + test_dir
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
113
        # try what is currently used:
114
        # remote path = self._abspath(relpath)
2321.3.7 by Alexander Belchenko
fixes for passing test_sftp_transport on win32 (thankyou John)
115
        self.assertEqual(test_dir + '/relative', t._remote_path('relative'))
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
116
        # we dont os.path.join because windows gives us the wrong path
2321.3.7 by Alexander Belchenko
fixes for passing test_sftp_transport on win32 (thankyou John)
117
        root_segments = test_dir.split('/')
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
118
        root_parent = '/'.join(root_segments[:-1])
119
        # .. should be honoured
120
        self.assertEqual(root_parent + '/sibling', t._remote_path('../sibling'))
121
        # /  should be illegal ?
122
        ### FIXME decide and then test for all transports. RBC20051208
123
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
124
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
125
class SFTPTransportTestRelativeRoot(TestCaseWithSFTPServer):
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
126
    """Test the SFTP transport with homedir based relative paths."""
127
128
    def setUp(self):
129
        self._get_remote_is_absolute = False
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
130
        super(SFTPTransportTestRelativeRoot, self).setUp()
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
131
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
132
    def test__remote_path_relative_root(self):
133
        # relative paths are preserved
134
        t = self.get_transport('')
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
135
        # the remote path should be ''
136
        self.assertEqual('', t._path)
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
137
        self.assertEqual('a', t._remote_path('a'))
138
139
1185.40.4 by Robey Pointer
fix sftp urls to support the ietf draft url spec wrt relative vs absolute sftp urls (this will break existing branch urls); fix username/password parsing in sftp urls; add unit tests to make sure sftp url parsing is working
140
class FakeSFTPTransport (object):
141
    _sftp = object()
142
fake = FakeSFTPTransport()
143
144
1185.49.14 by John Arbash Meinel
[merge] bzr.dev
145
class SFTPNonServerTest(TestCase):
1185.58.12 by John Arbash Meinel
Changing so that sftp tests are skipped rather than hidden when paramiko isn't present
146
    def setUp(self):
147
        TestCase.setUp(self)
148
        if not paramiko_loaded:
149
            raise TestSkipped('you must have paramiko to run this test')
150
1185.40.4 by Robey Pointer
fix sftp urls to support the ietf draft url spec wrt relative vs absolute sftp urls (this will break existing branch urls); fix username/password parsing in sftp urls; add unit tests to make sure sftp url parsing is working
151
    def test_parse_url(self):
152
        from bzrlib.transport.sftp import SFTPTransport
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
153
        s = SFTPTransport('sftp://simple.example.com/home/source', clone_from=fake)
1185.40.4 by Robey Pointer
fix sftp urls to support the ietf draft url spec wrt relative vs absolute sftp urls (this will break existing branch urls); fix username/password parsing in sftp urls; add unit tests to make sure sftp url parsing is working
154
        self.assertEquals(s._host, 'simple.example.com')
1185.49.23 by John Arbash Meinel
bugreport from Matthieu Moy: relpath was failing, but throwing an unhelpful exception.
155
        self.assertEquals(s._port, None)
1185.48.5 by James Henstridge
Change SFTP url parsing back to treat the path in sftp://host/path as
156
        self.assertEquals(s._path, '/home/source')
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
157
        self.failUnless(s._password is None)
158
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
159
        self.assertEquals(s.base, 'sftp://simple.example.com/home/source/')
160
161
        s = SFTPTransport('sftp://ro%62ey:h%40t@example.com:2222/~/relative', clone_from=fake)
1185.40.4 by Robey Pointer
fix sftp urls to support the ietf draft url spec wrt relative vs absolute sftp urls (this will break existing branch urls); fix username/password parsing in sftp urls; add unit tests to make sure sftp url parsing is working
162
        self.assertEquals(s._host, 'example.com')
163
        self.assertEquals(s._port, 2222)
164
        self.assertEquals(s._username, 'robey')
165
        self.assertEquals(s._password, 'h@t')
1185.48.5 by James Henstridge
Change SFTP url parsing back to treat the path in sftp://host/path as
166
        self.assertEquals(s._path, 'relative')
1185.33.58 by Martin Pool
[patch] Better error when sftp urls are given with invalid port numbers (Matthieu Moy)
167
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
168
        # Base should not keep track of the password
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
169
        self.assertEquals(s.base, 'sftp://robey@example.com:2222/~/relative/')
1185.49.23 by John Arbash Meinel
bugreport from Matthieu Moy: relpath was failing, but throwing an unhelpful exception.
170
171
    def test_relpath(self):
172
        from bzrlib.transport.sftp import SFTPTransport
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
173
        from bzrlib.errors import PathNotChild
1185.49.23 by John Arbash Meinel
bugreport from Matthieu Moy: relpath was failing, but throwing an unhelpful exception.
174
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
175
        s = SFTPTransport('sftp://user@host.com/abs/path', clone_from=fake)
176
        self.assertEquals(s.relpath('sftp://user@host.com/abs/path/sub'), 'sub')
2216 by Canonical.com Patch Queue Manager
(Vincent Ladeuil) Fix proxy issues with earlier python versions.
177
        # Can't test this one, because we actually get an AssertionError
178
        # TODO: Consider raising an exception rather than an assert
179
        #self.assertRaises(PathNotChild, s.relpath, 'http://user@host.com/abs/path/sub')
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
180
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user2@host.com/abs/path/sub')
181
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user@otherhost.com/abs/path/sub')
182
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user@host.com:33/abs/path/sub')
183
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user@host.com/~/rel/path/sub')
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
184
1185.49.25 by John Arbash Meinel
Added a couple more test cases, just in case.
185
        # Make sure it works when we don't supply a username
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
186
        s = SFTPTransport('sftp://host.com/abs/path', clone_from=fake)
187
        self.assertEquals(s.relpath('sftp://host.com/abs/path/sub'), 'sub')
1185.49.25 by John Arbash Meinel
Added a couple more test cases, just in case.
188
189
        # Make sure it works when parts of the path will be url encoded
190
        # TODO: These may be incorrect, we might need to urllib.urlencode() before
191
        # we pass the paths into the SFTPTransport constructor
192
        s = SFTPTransport('sftp://host.com/dev/,path', clone_from=fake)
193
        self.assertEquals(s.relpath('sftp://host.com/dev/,path/sub'), 'sub')
194
        s = SFTPTransport('sftp://host.com/dev/%path', clone_from=fake)
195
        self.assertEquals(s.relpath('sftp://host.com/dev/%path/sub'), 'sub')
196
1185.33.58 by Martin Pool
[patch] Better error when sftp urls are given with invalid port numbers (Matthieu Moy)
197
    def test_parse_invalid_url(self):
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
198
        from bzrlib.transport.sftp import SFTPTransport, TransportError
1185.33.58 by Martin Pool
[patch] Better error when sftp urls are given with invalid port numbers (Matthieu Moy)
199
        try:
200
            s = SFTPTransport('sftp://lilypond.org:~janneke/public_html/bzr/gub',
201
                              clone_from=fake)
202
            self.fail('expected exception not raised')
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
203
        except TransportError, e:
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
204
            self.assertEquals(str(e),
205
                    'Transport error: '
1910.15.8 by Andrew Bennetts
Put url in 'invalid port number' message on a new line.
206
                    'invalid port number ~janneke in url:\n'
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
207
                    'sftp://lilypond.org:~janneke/public_html/bzr/gub ')
1185.33.58 by Martin Pool
[patch] Better error when sftp urls are given with invalid port numbers (Matthieu Moy)
208
2013.1.2 by John Arbash Meinel
Add a test that we can always fall back to the paramiko vendor
209
    def test_get_paramiko_vendor(self):
210
        """Test that if no 'ssh' is available we get builtin paramiko"""
211
        from bzrlib.transport import ssh
212
        # set '.' as the only location in the path, forcing no 'ssh' to exist
2221.5.18 by Dmitry Vasiliev
Fixed variable name
213
        orig_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
2013.1.2 by John Arbash Meinel
Add a test that we can always fall back to the paramiko vendor
214
        orig_path = set_or_unset_env('PATH', '.')
215
        try:
216
            # No vendor defined yet, query for one
2221.5.18 by Dmitry Vasiliev
Fixed variable name
217
            ssh._ssh_vendor_manager.clear_cache()
2013.1.2 by John Arbash Meinel
Add a test that we can always fall back to the paramiko vendor
218
            vendor = ssh._get_ssh_vendor()
219
            self.assertIsInstance(vendor, ssh.ParamikoVendor)
220
        finally:
221
            set_or_unset_env('PATH', orig_path)
2221.5.18 by Dmitry Vasiliev
Fixed variable name
222
            ssh._ssh_vendor_manager._cached_ssh_vendor = orig_vendor
2013.1.2 by John Arbash Meinel
Add a test that we can always fall back to the paramiko vendor
223
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
224
    def test_abspath_root_sibling_server(self):
225
        from bzrlib.transport.sftp import SFTPSiblingAbsoluteServer
226
        server = SFTPSiblingAbsoluteServer()
227
        server.setUp()
228
        try:
229
            transport = get_transport(server.get_url())
230
            self.assertFalse(transport.abspath('/').endswith('/~/'))
231
            self.assertTrue(transport.abspath('/').endswith('/'))
232
            del transport
233
        finally:
234
            server.tearDown()
235
1185.40.4 by Robey Pointer
fix sftp urls to support the ietf draft url spec wrt relative vs absolute sftp urls (this will break existing branch urls); fix username/password parsing in sftp urls; add unit tests to make sure sftp url parsing is working
236
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
237
class SFTPBranchTest(TestCaseWithSFTPServer):
238
    """Test some stuff when accessing a bzr Branch over sftp"""
239
240
    def test_lock_file(self):
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
241
        # old format branches use a special lock file on sftp.
242
        b = self.make_branch('', format=bzrdir.BzrDirFormat6())
243
        b = bzrlib.branch.Branch.open(self.get_url())
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
244
        self.failUnlessExists('.bzr/')
245
        self.failUnlessExists('.bzr/branch-format')
246
        self.failUnlessExists('.bzr/branch-lock')
247
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
248
        self.failIf(lexists('.bzr/branch-lock.write-lock'))
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
249
        b.lock_write()
250
        self.failUnlessExists('.bzr/branch-lock.write-lock')
251
        b.unlock()
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
252
        self.failIf(lexists('.bzr/branch-lock.write-lock'))
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
253
1185.49.26 by John Arbash Meinel
Adding tests for remote sftp branches without working trees, plus a bugfix to allow push to still work with a warning.
254
    def test_push_support(self):
255
        self.build_tree(['a/', 'a/foo'])
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
256
        t = bzrdir.BzrDir.create_standalone_workingtree('a')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
257
        b = t.branch
1185.49.26 by John Arbash Meinel
Adding tests for remote sftp branches without working trees, plus a bugfix to allow push to still work with a warning.
258
        t.add('foo')
259
        t.commit('foo', rev_id='a1')
260
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
261
        b2 = bzrdir.BzrDir.create_branch_and_repo(self.get_url('/b'))
1185.49.26 by John Arbash Meinel
Adding tests for remote sftp branches without working trees, plus a bugfix to allow push to still work with a warning.
262
        b2.pull(b)
263
264
        self.assertEquals(b2.revision_history(), ['a1'])
265
1185.31.48 by John Arbash Meinel
Added a small test to sftp to make sure some replacing was going on in the remote side.
266
        open('a/foo', 'wt').write('something new in foo\n')
267
        t.commit('new', rev_id='a2')
268
        b2.pull(b)
269
270
        self.assertEquals(b2.revision_history(), ['a1', 'a2'])
271
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
272
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
273
class SSHVendorConnection(TestCaseWithSFTPServer):
274
    """Test that the ssh vendors can all connect.
275
276
    Verify that a full-handshake (SSH over loopback TCP) sftp connection works.
277
278
    We have 3 sftp implementations in the test suite:
279
      'loopback': Doesn't use ssh, just uses a local socket. Most tests are
280
                  done this way to save the handshaking time, so it is not
281
                  tested again here
282
      'none':     This uses paramiko's built-in ssh client and server, and layers
283
                  sftp on top of it.
284
      None:       If 'ssh' exists on the machine, then it will be spawned as a
285
                  child process.
286
    """
1547.1.4 by Robey Pointer
add a single full-handshake test to verify that sftp-over-ssh works
287
    
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
288
    def setUp(self):
289
        super(SSHVendorConnection, self).setUp()
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
290
        from bzrlib.transport.sftp import SFTPFullAbsoluteServer
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
291
292
        def create_server():
293
            """Just a wrapper so that when created, it will set _vendor"""
294
            # SFTPFullAbsoluteServer can handle any vendor,
295
            # it just needs to be set between the time it is instantiated
296
            # and the time .setUp() is called
297
            server = SFTPFullAbsoluteServer()
298
            server._vendor = self._test_vendor
299
            return server
300
        self._test_vendor = 'loopback'
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
301
        self.vfs_transport_server = create_server
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
302
        f = open('a_file', 'wb')
303
        try:
304
            f.write('foobar\n')
305
        finally:
306
            f.close()
307
308
    def set_vendor(self, vendor):
309
        self._test_vendor = vendor
310
311
    def test_connection_paramiko(self):
1951.1.8 by Andrew Bennetts
Make _get_ssh_vendor return the vendor object, rather than just a string.
312
        from bzrlib.transport import ssh
313
        self.set_vendor(ssh.ParamikoVendor())
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
314
        t = self.get_transport()
315
        self.assertEqual('foobar\n', t.get('a_file').read())
316
317
    def test_connection_vendor(self):
318
        raise TestSkipped("We don't test spawning real ssh,"
319
                          " because it prompts for a password."
320
                          " Enable this test if we figure out"
321
                          " how to prevent this.")
322
        self.set_vendor(None)
323
        t = self.get_transport()
324
        self.assertEqual('foobar\n', t.get('a_file').read())
325
326
327
class SSHVendorBadConnection(TestCaseWithTransport):
328
    """Test that the ssh vendors handle bad connection properly
329
330
    We don't subclass TestCaseWithSFTPServer, because we don't actually
331
    need an SFTP connection.
332
    """
333
334
    def setUp(self):
335
        if not paramiko_loaded:
336
            raise TestSkipped('you must have paramiko to run this test')
337
        super(SSHVendorBadConnection, self).setUp()
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
338
        import bzrlib.transport.ssh
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
339
1185.49.35 by John Arbash Meinel
Update tests to use a truly unused port
340
        # open a random port, so we know nobody else is using it
341
        # but don't actually listen on the port.
342
        s = socket.socket()
343
        s.bind(('localhost', 0))
344
        self.bogus_url = 'sftp://%s:%s/' % s.getsockname()
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
345
2221.5.18 by Dmitry Vasiliev
Fixed variable name
346
        orig_vendor = bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor
1185.49.35 by John Arbash Meinel
Update tests to use a truly unused port
347
        def reset():
2221.5.18 by Dmitry Vasiliev
Fixed variable name
348
            bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor = orig_vendor
1185.49.35 by John Arbash Meinel
Update tests to use a truly unused port
349
            s.close()
350
        self.addCleanup(reset)
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
351
352
    def set_vendor(self, vendor):
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
353
        import bzrlib.transport.ssh
2221.5.18 by Dmitry Vasiliev
Fixed variable name
354
        bzrlib.transport.ssh._ssh_vendor_manager._cached_ssh_vendor = vendor
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
355
356
    def test_bad_connection_paramiko(self):
357
        """Test that a real connection attempt raises the right error"""
1951.1.8 by Andrew Bennetts
Make _get_ssh_vendor return the vendor object, rather than just a string.
358
        from bzrlib.transport import ssh
359
        self.set_vendor(ssh.ParamikoVendor())
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
360
        self.assertRaises(errors.ConnectionError,
361
                          bzrlib.transport.get_transport, self.bogus_url)
362
363
    def test_bad_connection_ssh(self):
364
        """None => auto-detect vendor"""
365
        self.set_vendor(None)
1185.49.33 by John Arbash Meinel
Spawn another bzr instance using run_bzr_subprocess, so we don't get stipple
366
        # This is how I would normally test the connection code
367
        # it makes it very clear what we are testing.
368
        # However, 'ssh' will create stipple on the output, so instead
369
        # I'm using run_bzr_subprocess, and parsing the output
370
        # try:
371
        #     t = bzrlib.transport.get_transport(self.bogus_url)
372
        # except errors.ConnectionError:
373
        #     # Correct error
374
        #     pass
375
        # except errors.NameError, e:
376
        #     if 'SSHException' in str(e):
377
        #         raise TestSkipped('Known NameError bug in paramiko 1.6.1')
378
        #     raise
379
        # else:
380
        #     self.fail('Excepted ConnectionError to be raised')
381
382
        out, err = self.run_bzr_subprocess('log', self.bogus_url, retcode=3)
383
        self.assertEqual('', out)
384
        if "NameError: global name 'SSHException'" in err:
385
            # We aren't fixing this bug, because it is a bug in
386
            # paramiko, but we know about it, so we don't have to
387
            # fail the test
388
            raise TestSkipped('Known NameError bug with paramiko-1.6.1')
2052.4.4 by John Arbash Meinel
Create a SocketConnectionError to make creating nice errors easier
389
        self.assertContainsRe(err, r'bzr: ERROR: Unable to connect to SSH host'
390
                                   r' 127\.0\.0\.1:\d+; ')
1185.49.32 by John Arbash Meinel
Update tests to show that all ssh vendor failed connections work correctly, has some stipple from real ssh
391
1871.1.3 by Robert Collins
proof of concept slowsocket wrapper.
392
393
class SFTPLatencyKnob(TestCaseWithSFTPServer):
394
    """Test that the testing SFTPServer's latency knob works."""
395
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
396
    def test_latency_knob_slows_transport(self):
397
        # change the latency knob to 500ms. We take about 40ms for a 
1871.1.3 by Robert Collins
proof of concept slowsocket wrapper.
398
        # loopback connection ordinarily.
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
399
        start_time = time.time()
400
        self.get_server().add_latency = 0.5
401
        transport = self.get_transport()
402
        with_latency_knob_time = time.time() - start_time
403
        self.assertTrue(with_latency_knob_time > 0.4)
404
405
    def test_default(self):
406
        # This test is potentially brittle: under extremely high machine load
407
        # it could fail, but that is quite unlikely
408
        start_time = time.time()
409
        transport = self.get_transport()
410
        regular_time = time.time() - start_time
411
        self.assertTrue(regular_time < 0.5)
412
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
413
414
class FakeSocket(object):
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
415
    """Fake socket object used to test the SocketDelay wrapper without
416
    using a real socket.
417
    """
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
418
419
    def __init__(self):
420
        self._data = ""
421
422
    def send(self, data, flags=0):
423
        self._data += data
424
        return len(data)
425
426
    def sendall(self, data, flags=0):
427
        self._data += data
428
        return len(data)
429
430
    def recv(self, size, flags=0):
431
        if size < len(self._data):
432
            result = self._data[:size]
433
            self._data = self._data[size:]
434
            return result
435
        else:
436
            result = self._data
437
            self._data = ""
438
            return result
439
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
440
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
441
class TestSocketDelay(TestCase):
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
442
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
443
    def setUp(self):
444
        TestCase.setUp(self)
1993.2.2 by John Arbash Meinel
Skip tests that require paramiko (or think they do)
445
        if not paramiko_loaded:
446
            raise TestSkipped('you must have paramiko to run this test')
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
447
448
    def test_delay(self):
449
        from bzrlib.transport.sftp import SocketDelay
450
        sending = FakeSocket()
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
451
        receiving = SocketDelay(sending, 0.1, bandwidth=1000000,
452
                                really_sleep=False)
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
453
        # check that simulated time is charged only per round-trip:
454
        t1 = SocketDelay.simulated_time
455
        receiving.send("connect1")
456
        self.assertEqual(sending.recv(1024), "connect1")
457
        t2 = SocketDelay.simulated_time
458
        self.assertAlmostEqual(t2 - t1, 0.1)
459
        receiving.send("connect2")
460
        self.assertEqual(sending.recv(1024), "connect2")
461
        sending.send("hello")
462
        self.assertEqual(receiving.recv(1024), "hello")
463
        t3 = SocketDelay.simulated_time
464
        self.assertAlmostEqual(t3 - t2, 0.1)
465
        sending.send("hello")
466
        self.assertEqual(receiving.recv(1024), "hello")
467
        sending.send("hello")
468
        self.assertEqual(receiving.recv(1024), "hello")
469
        sending.send("hello")
470
        self.assertEqual(receiving.recv(1024), "hello")
471
        t4 = SocketDelay.simulated_time
472
        self.assertAlmostEqual(t4, t3)
473
474
    def test_bandwidth(self):
475
        from bzrlib.transport.sftp import SocketDelay
476
        sending = FakeSocket()
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
477
        receiving = SocketDelay(sending, 0, bandwidth=8.0/(1024*1024),
478
                                really_sleep=False)
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
479
        # check that simulated time is charged only per round-trip:
480
        t1 = SocketDelay.simulated_time
481
        receiving.send("connect")
482
        self.assertEqual(sending.recv(1024), "connect")
483
        sending.send("a" * 100)
484
        self.assertEqual(receiving.recv(1024), "a" * 100)
485
        t2 = SocketDelay.simulated_time
486
        self.assertAlmostEqual(t2 - t1, 100 + 7)
487
1874.1.3 by Carl Friedrich Bolz
Merge bzr.dev.
488