~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
62
    def get_transport(self, path=None):
63
        """Return a transport relative to self._test_root."""
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.
64
        return bzrlib.transport.get_transport(self.get_url(path))
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
65
66
67
class SFTPLockTests (TestCaseWithSFTPServer):
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
68
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
69
    def test_sftp_locks(self):
70
        from bzrlib.errors import LockError
71
        t = self.get_transport()
72
73
        l = t.lock_write('bogus')
74
        self.failUnlessExists('bogus.write-lock')
75
76
        # Don't wait for the lock, locking an already locked
77
        # file should raise an assert
78
        self.assertRaises(LockError, t.lock_write, 'bogus')
79
80
        l.unlock()
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
81
        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
82
83
        open('something.write-lock', 'wb').write('fake lock\n')
84
        self.assertRaises(LockError, t.lock_write, 'something')
85
        os.remove('something.write-lock')
86
87
        l = t.lock_write('something')
88
89
        l2 = t.lock_write('bogus')
90
91
        l.unlock()
92
        l2.unlock()
93
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
94
    def test_multiple_connections(self):
95
        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.
96
        self.assertTrue('sftpserver - new connection' in self.get_server().logs)
97
        self.get_server().logs = []
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
98
        # The second request should reuse the first connection
99
        # SingleListener only allows for a single connection,
100
        # So the next line fails unless the connection is reused
101
        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.
102
        self.assertEquals(self.get_server().logs, [])
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
103
104
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
105
class SFTPTransportTestRelative(TestCaseWithSFTPServer):
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
106
    """Test the SFTP transport with homedir based relative paths."""
107
108
    def test__remote_path(self):
109
        t = self.get_transport()
2321.3.7 by Alexander Belchenko
fixes for passing test_sftp_transport on win32 (thankyou John)
110
        # This test require unix-like absolute path
111
        test_dir = self.test_dir
112
        if sys.platform == 'win32':
113
            # using hack suggested by John Meinel.
114
            # TODO: write another mock server for this test
115
            #       and use absolute path without drive letter
116
            test_dir = '/' + test_dir
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
117
        # try what is currently used:
118
        # remote path = self._abspath(relpath)
2321.3.7 by Alexander Belchenko
fixes for passing test_sftp_transport on win32 (thankyou John)
119
        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
120
        # 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)
121
        root_segments = test_dir.split('/')
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
122
        root_parent = '/'.join(root_segments[:-1])
123
        # .. should be honoured
124
        self.assertEqual(root_parent + '/sibling', t._remote_path('../sibling'))
125
        # /  should be illegal ?
126
        ### FIXME decide and then test for all transports. RBC20051208
127
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
128
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
129
class SFTPTransportTestRelativeRoot(TestCaseWithSFTPServer):
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
130
    """Test the SFTP transport with homedir based relative paths."""
131
132
    def setUp(self):
133
        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.
134
        super(SFTPTransportTestRelativeRoot, self).setUp()
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
135
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
136
    def test__remote_path_relative_root(self):
137
        # relative paths are preserved
138
        t = self.get_transport('')
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
139
        # the remote path should be ''
140
        self.assertEqual('', t._path)
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
141
        self.assertEqual('a', t._remote_path('a'))
142
143
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
144
class FakeSFTPTransport (object):
145
    _sftp = object()
146
fake = FakeSFTPTransport()
147
148
1185.49.14 by John Arbash Meinel
[merge] bzr.dev
149
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
150
    def setUp(self):
151
        TestCase.setUp(self)
152
        if not paramiko_loaded:
153
            raise TestSkipped('you must have paramiko to run this test')
154
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
155
    def test_parse_url(self):
156
        from bzrlib.transport.sftp import SFTPTransport
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
157
        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
158
        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.
159
        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
160
        self.assertEquals(s._path, '/home/source')
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
161
        self.failUnless(s._password is None)
162
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
163
        self.assertEquals(s.base, 'sftp://simple.example.com/home/source/')
164
165
        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
166
        self.assertEquals(s._host, 'example.com')
167
        self.assertEquals(s._port, 2222)
168
        self.assertEquals(s._username, 'robey')
169
        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
170
        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)
171
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
172
        # Base should not keep track of the password
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
173
        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.
174
175
    def test_relpath(self):
176
        from bzrlib.transport.sftp import SFTPTransport
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
177
        from bzrlib.errors import PathNotChild
1185.49.23 by John Arbash Meinel
bugreport from Matthieu Moy: relpath was failing, but throwing an unhelpful exception.
178
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
179
        s = SFTPTransport('sftp://user@host.com/abs/path', clone_from=fake)
180
        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.
181
        # Can't test this one, because we actually get an AssertionError
182
        # TODO: Consider raising an exception rather than an assert
183
        #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.
184
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user2@host.com/abs/path/sub')
185
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user@otherhost.com/abs/path/sub')
186
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user@host.com:33/abs/path/sub')
187
        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
188
1185.49.25 by John Arbash Meinel
Added a couple more test cases, just in case.
189
        # 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.
190
        s = SFTPTransport('sftp://host.com/abs/path', clone_from=fake)
191
        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.
192
193
        # Make sure it works when parts of the path will be url encoded
194
        # TODO: These may be incorrect, we might need to urllib.urlencode() before
195
        # we pass the paths into the SFTPTransport constructor
196
        s = SFTPTransport('sftp://host.com/dev/,path', clone_from=fake)
197
        self.assertEquals(s.relpath('sftp://host.com/dev/,path/sub'), 'sub')
198
        s = SFTPTransport('sftp://host.com/dev/%path', clone_from=fake)
199
        self.assertEquals(s.relpath('sftp://host.com/dev/%path/sub'), 'sub')
200
1185.33.58 by Martin Pool
[patch] Better error when sftp urls are given with invalid port numbers (Matthieu Moy)
201
    def test_parse_invalid_url(self):
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
202
        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)
203
        try:
204
            s = SFTPTransport('sftp://lilypond.org:~janneke/public_html/bzr/gub',
205
                              clone_from=fake)
206
            self.fail('expected exception not raised')
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
207
        except TransportError, e:
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
208
            self.assertEquals(str(e),
209
                    'Transport error: '
1910.15.8 by Andrew Bennetts
Put url in 'invalid port number' message on a new line.
210
                    'invalid port number ~janneke in url:\n'
1910.15.1 by Andrew Bennetts
More tests for abspath and clone behaviour
211
                    '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)
212
2013.1.2 by John Arbash Meinel
Add a test that we can always fall back to the paramiko vendor
213
    def test_get_paramiko_vendor(self):
214
        """Test that if no 'ssh' is available we get builtin paramiko"""
215
        from bzrlib.transport import ssh
216
        # set '.' as the only location in the path, forcing no 'ssh' to exist
2221.5.18 by Dmitry Vasiliev
Fixed variable name
217
        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
218
        orig_path = set_or_unset_env('PATH', '.')
219
        try:
220
            # No vendor defined yet, query for one
2221.5.18 by Dmitry Vasiliev
Fixed variable name
221
            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
222
            vendor = ssh._get_ssh_vendor()
223
            self.assertIsInstance(vendor, ssh.ParamikoVendor)
224
        finally:
225
            set_or_unset_env('PATH', orig_path)
2221.5.18 by Dmitry Vasiliev
Fixed variable name
226
            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
227
1986.1.10 by Robert Collins
Merge from bzr.dev, fixing found bugs handling 'has('/')' in MemoryTransport and SFTP transports.
228
    def test_abspath_root_sibling_server(self):
229
        from bzrlib.transport.sftp import SFTPSiblingAbsoluteServer
230
        server = SFTPSiblingAbsoluteServer()
231
        server.setUp()
232
        try:
233
            transport = get_transport(server.get_url())
234
            self.assertFalse(transport.abspath('/').endswith('/~/'))
235
            self.assertTrue(transport.abspath('/').endswith('/'))
236
            del transport
237
        finally:
238
            server.tearDown()
239
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
240
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
241
class SFTPBranchTest(TestCaseWithSFTPServer):
242
    """Test some stuff when accessing a bzr Branch over sftp"""
243
244
    def test_lock_file(self):
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
245
        # old format branches use a special lock file on sftp.
246
        b = self.make_branch('', format=bzrdir.BzrDirFormat6())
247
        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
248
        self.failUnlessExists('.bzr/')
249
        self.failUnlessExists('.bzr/branch-format')
250
        self.failUnlessExists('.bzr/branch-lock')
251
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
        b.lock_write()
254
        self.failUnlessExists('.bzr/branch-lock.write-lock')
255
        b.unlock()
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
256
        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
257
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
    def test_push_support(self):
259
        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.
260
        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.
261
        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.
262
        t.add('foo')
263
        t.commit('foo', rev_id='a1')
264
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.
265
        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.
266
        b2.pull(b)
267
268
        self.assertEquals(b2.revision_history(), ['a1'])
269
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.
270
        open('a/foo', 'wt').write('something new in foo\n')
271
        t.commit('new', rev_id='a2')
272
        b2.pull(b)
273
274
        self.assertEquals(b2.revision_history(), ['a1', 'a2'])
275
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
276
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
277
class SSHVendorConnection(TestCaseWithSFTPServer):
278
    """Test that the ssh vendors can all connect.
279
280
    Verify that a full-handshake (SSH over loopback TCP) sftp connection works.
281
282
    We have 3 sftp implementations in the test suite:
283
      'loopback': Doesn't use ssh, just uses a local socket. Most tests are
284
                  done this way to save the handshaking time, so it is not
285
                  tested again here
286
      'none':     This uses paramiko's built-in ssh client and server, and layers
287
                  sftp on top of it.
288
      None:       If 'ssh' exists on the machine, then it will be spawned as a
289
                  child process.
290
    """
1547.1.4 by Robey Pointer
add a single full-handshake test to verify that sftp-over-ssh works
291
    
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
292
    def setUp(self):
293
        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.
294
        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
295
296
        def create_server():
297
            """Just a wrapper so that when created, it will set _vendor"""
298
            # SFTPFullAbsoluteServer can handle any vendor,
299
            # it just needs to be set between the time it is instantiated
300
            # and the time .setUp() is called
301
            server = SFTPFullAbsoluteServer()
302
            server._vendor = self._test_vendor
303
            return server
304
        self._test_vendor = 'loopback'
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
305
        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
306
        f = open('a_file', 'wb')
307
        try:
308
            f.write('foobar\n')
309
        finally:
310
            f.close()
311
312
    def set_vendor(self, vendor):
313
        self._test_vendor = vendor
314
315
    def test_connection_paramiko(self):
1951.1.8 by Andrew Bennetts
Make _get_ssh_vendor return the vendor object, rather than just a string.
316
        from bzrlib.transport import ssh
317
        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
318
        t = self.get_transport()
319
        self.assertEqual('foobar\n', t.get('a_file').read())
320
321
    def test_connection_vendor(self):
322
        raise TestSkipped("We don't test spawning real ssh,"
323
                          " because it prompts for a password."
324
                          " Enable this test if we figure out"
325
                          " how to prevent this.")
326
        self.set_vendor(None)
327
        t = self.get_transport()
328
        self.assertEqual('foobar\n', t.get('a_file').read())
329
330
331
class SSHVendorBadConnection(TestCaseWithTransport):
332
    """Test that the ssh vendors handle bad connection properly
333
334
    We don't subclass TestCaseWithSFTPServer, because we don't actually
335
    need an SFTP connection.
336
    """
337
338
    def setUp(self):
339
        if not paramiko_loaded:
340
            raise TestSkipped('you must have paramiko to run this test')
341
        super(SSHVendorBadConnection, self).setUp()
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
342
        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
343
1185.49.35 by John Arbash Meinel
Update tests to use a truly unused port
344
        # open a random port, so we know nobody else is using it
345
        # but don't actually listen on the port.
346
        s = socket.socket()
347
        s.bind(('localhost', 0))
348
        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
349
2221.5.18 by Dmitry Vasiliev
Fixed variable name
350
        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
351
        def reset():
2221.5.18 by Dmitry Vasiliev
Fixed variable name
352
            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
353
            s.close()
354
        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
355
356
    def set_vendor(self, vendor):
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
357
        import bzrlib.transport.ssh
2221.5.18 by Dmitry Vasiliev
Fixed variable name
358
        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
359
360
    def test_bad_connection_paramiko(self):
361
        """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.
362
        from bzrlib.transport import ssh
363
        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
364
        self.assertRaises(errors.ConnectionError,
365
                          bzrlib.transport.get_transport, self.bogus_url)
366
367
    def test_bad_connection_ssh(self):
368
        """None => auto-detect vendor"""
369
        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
370
        # This is how I would normally test the connection code
371
        # it makes it very clear what we are testing.
372
        # However, 'ssh' will create stipple on the output, so instead
373
        # I'm using run_bzr_subprocess, and parsing the output
374
        # try:
375
        #     t = bzrlib.transport.get_transport(self.bogus_url)
376
        # except errors.ConnectionError:
377
        #     # Correct error
378
        #     pass
379
        # except errors.NameError, e:
380
        #     if 'SSHException' in str(e):
381
        #         raise TestSkipped('Known NameError bug in paramiko 1.6.1')
382
        #     raise
383
        # else:
384
        #     self.fail('Excepted ConnectionError to be raised')
385
386
        out, err = self.run_bzr_subprocess('log', self.bogus_url, retcode=3)
387
        self.assertEqual('', out)
388
        if "NameError: global name 'SSHException'" in err:
389
            # We aren't fixing this bug, because it is a bug in
390
            # paramiko, but we know about it, so we don't have to
391
            # fail the test
392
            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
393
        self.assertContainsRe(err, r'bzr: ERROR: Unable to connect to SSH host'
394
                                   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
395
1871.1.3 by Robert Collins
proof of concept slowsocket wrapper.
396
397
class SFTPLatencyKnob(TestCaseWithSFTPServer):
398
    """Test that the testing SFTPServer's latency knob works."""
399
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
400
    def test_latency_knob_slows_transport(self):
401
        # change the latency knob to 500ms. We take about 40ms for a 
1871.1.3 by Robert Collins
proof of concept slowsocket wrapper.
402
        # loopback connection ordinarily.
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
403
        start_time = time.time()
404
        self.get_server().add_latency = 0.5
405
        transport = self.get_transport()
406
        with_latency_knob_time = time.time() - start_time
407
        self.assertTrue(with_latency_knob_time > 0.4)
408
409
    def test_default(self):
410
        # This test is potentially brittle: under extremely high machine load
411
        # it could fail, but that is quite unlikely
412
        start_time = time.time()
413
        transport = self.get_transport()
414
        regular_time = time.time() - start_time
415
        self.assertTrue(regular_time < 0.5)
416
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
417
418
class FakeSocket(object):
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
419
    """Fake socket object used to test the SocketDelay wrapper without
420
    using a real socket.
421
    """
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
422
423
    def __init__(self):
424
        self._data = ""
425
426
    def send(self, data, flags=0):
427
        self._data += data
428
        return len(data)
429
430
    def sendall(self, data, flags=0):
431
        self._data += data
432
        return len(data)
433
434
    def recv(self, size, flags=0):
435
        if size < len(self._data):
436
            result = self._data[:size]
437
            self._data = self._data[size:]
438
            return result
439
        else:
440
            result = self._data
441
            self._data = ""
442
            return result
443
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
444
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
445
class TestSocketDelay(TestCase):
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
446
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
447
    def setUp(self):
448
        TestCase.setUp(self)
1993.2.2 by John Arbash Meinel
Skip tests that require paramiko (or think they do)
449
        if not paramiko_loaded:
450
            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
451
452
    def test_delay(self):
453
        from bzrlib.transport.sftp import SocketDelay
454
        sending = FakeSocket()
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
455
        receiving = SocketDelay(sending, 0.1, bandwidth=1000000,
456
                                really_sleep=False)
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
457
        # check that simulated time is charged only per round-trip:
458
        t1 = SocketDelay.simulated_time
459
        receiving.send("connect1")
460
        self.assertEqual(sending.recv(1024), "connect1")
461
        t2 = SocketDelay.simulated_time
462
        self.assertAlmostEqual(t2 - t1, 0.1)
463
        receiving.send("connect2")
464
        self.assertEqual(sending.recv(1024), "connect2")
465
        sending.send("hello")
466
        self.assertEqual(receiving.recv(1024), "hello")
467
        t3 = SocketDelay.simulated_time
468
        self.assertAlmostEqual(t3 - t2, 0.1)
469
        sending.send("hello")
470
        self.assertEqual(receiving.recv(1024), "hello")
471
        sending.send("hello")
472
        self.assertEqual(receiving.recv(1024), "hello")
473
        sending.send("hello")
474
        self.assertEqual(receiving.recv(1024), "hello")
475
        t4 = SocketDelay.simulated_time
476
        self.assertAlmostEqual(t4, t3)
477
478
    def test_bandwidth(self):
479
        from bzrlib.transport.sftp import SocketDelay
480
        sending = FakeSocket()
1874.1.9 by Carl Friedrich Bolz
Try to fix all the issues outline by john and Robert.
481
        receiving = SocketDelay(sending, 0, bandwidth=8.0/(1024*1024),
482
                                really_sleep=False)
1874.1.2 by Carl Friedrich Bolz
Refined the SocketDelay to charge latency only once per round-trip and to
483
        # check that simulated time is charged only per round-trip:
484
        t1 = SocketDelay.simulated_time
485
        receiving.send("connect")
486
        self.assertEqual(sending.recv(1024), "connect")
487
        sending.send("a" * 100)
488
        self.assertEqual(receiving.recv(1024), "a" * 100)
489
        t2 = SocketDelay.simulated_time
490
        self.assertAlmostEqual(t2 - t1, 100 + 7)
491
1874.1.3 by Carl Friedrich Bolz
Merge bzr.dev.
492