~bzr-pqm/bzr/bzr.dev

1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>, Canonical Ltd
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
import os
18
import socket
19
import threading
20
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
21
from bzrlib.branch import Branch
22
import bzrlib.errors as errors
1530.1.7 by Robert Collins
merge integration.
23
from bzrlib.osutils import pathjoin, lexists
1185.58.12 by John Arbash Meinel
Changing so that sftp tests are skipped rather than hidden when paramiko isn't present
24
from bzrlib.tests import TestCaseInTempDir, TestCase, TestSkipped
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
25
import bzrlib.transport
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
26
27
try:
28
    import paramiko
29
    paramiko_loaded = True
30
except ImportError:
31
    paramiko_loaded = False
32
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
33
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
34
class TestCaseWithSFTPServer(TestCaseInTempDir):
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
35
    """A test case base class that provides a sftp server on localhost."""
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
36
37
    def setUp(self):
1532 by Robert Collins
Merge in John Meinels integration branch.
38
        if not paramiko_loaded:
39
            raise TestSkipped('you must have paramiko to run this test')
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
40
        super(TestCaseWithSFTPServer, self).setUp()
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
41
        from bzrlib.transport.sftp import SFTPAbsoluteServer, SFTPHomeDirServer
42
        if getattr(self, '_get_remote_is_absolute', None) is None:
43
            self._get_remote_is_absolute = True
44
        if self._get_remote_is_absolute:
45
            self.server = SFTPAbsoluteServer()
46
        else:
47
            self.server = SFTPHomeDirServer()
48
        self.server.setUp()
1530.1.21 by Robert Collins
Review feedback fixes.
49
        self.addCleanup(self.server.tearDown)
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
50
        self._sftp_url = self.server.get_url()
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
51
        self._root = self.test_dir
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
52
        # Set to a string in setUp to give sftp server a new homedir.
53
        self._override_home = None
1185.49.14 by John Arbash Meinel
[merge] bzr.dev
54
        self._is_setup = False
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
55
        self.sftplogs = []
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
56
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
57
    def get_remote_url(self, relpath_to_test_root):
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
58
        # FIXME use urljoin ?
59
        return self._sftp_url + '/' + relpath_to_test_root
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
60
61
    def get_transport(self, path=None):
62
        """Return a transport relative to self._test_root."""
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
63
        from bzrlib.transport import get_transport
64
        transport = get_transport(self._sftp_url)
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
65
        if path is None:
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
66
            return transport
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
67
        else:
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
68
            return transport.clone(path)
69
70
71
class SFTPLockTests (TestCaseWithSFTPServer):
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
72
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
73
    def test_sftp_locks(self):
74
        from bzrlib.errors import LockError
75
        t = self.get_transport()
76
77
        l = t.lock_write('bogus')
78
        self.failUnlessExists('bogus.write-lock')
79
80
        # Don't wait for the lock, locking an already locked
81
        # file should raise an assert
82
        self.assertRaises(LockError, t.lock_write, 'bogus')
83
84
        l.unlock()
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
85
        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
86
87
        open('something.write-lock', 'wb').write('fake lock\n')
88
        self.assertRaises(LockError, t.lock_write, 'something')
89
        os.remove('something.write-lock')
90
91
        l = t.lock_write('something')
92
93
        l2 = t.lock_write('bogus')
94
95
        l.unlock()
96
        l2.unlock()
97
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
98
    def test_multiple_connections(self):
99
        t = self.get_transport()
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
100
        self.assertEquals(self.server.logs, 
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
101
                ['sftpserver - authorizing: foo'
102
               , 'sftpserver - channel request: session, 1'])
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
103
        self.server.logs = []
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
104
        # The second request should reuse the first connection
105
        # SingleListener only allows for a single connection,
106
        # So the next line fails unless the connection is reused
107
        t2 = self.get_transport()
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
108
        self.assertEquals(self.server.logs, [])
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
109
110
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
111
class SFTPTransportTestRelative(TestCaseWithSFTPServer):
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
112
    """Test the SFTP transport with homedir based relative paths."""
113
114
    def test__remote_path(self):
115
        t = self.get_transport()
116
        # try what is currently used:
117
        # remote path = self._abspath(relpath)
118
        self.assertEqual(self._root + '/relative', t._remote_path('relative'))
119
        # we dont os.path.join because windows gives us the wrong path
120
        root_segments = self._root.split('/')
121
        root_parent = '/'.join(root_segments[:-1])
122
        # .. should be honoured
123
        self.assertEqual(root_parent + '/sibling', t._remote_path('../sibling'))
124
        # /  should be illegal ?
125
        ### FIXME decide and then test for all transports. RBC20051208
126
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
127
128
class SFTPTransportTestRelative(TestCaseWithSFTPServer):
129
    """Test the SFTP transport with homedir based relative paths."""
130
131
    def setUp(self):
132
        self._get_remote_is_absolute = False
133
        super(SFTPTransportTestRelative, self).setUp()
134
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
135
    def test__remote_path_relative_root(self):
136
        # relative paths are preserved
137
        t = self.get_transport('')
138
        self.assertEqual('a', t._remote_path('a'))
139
140
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
141
class FakeSFTPTransport (object):
142
    _sftp = object()
143
fake = FakeSFTPTransport()
144
145
1185.49.14 by John Arbash Meinel
[merge] bzr.dev
146
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
147
    def setUp(self):
148
        TestCase.setUp(self)
149
        if not paramiko_loaded:
150
            raise TestSkipped('you must have paramiko to run this test')
151
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
152
    def test_parse_url(self):
153
        from bzrlib.transport.sftp import SFTPTransport
154
        s = SFTPTransport('sftp://simple.example.com/%2fhome/source', clone_from=fake)
155
        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.
156
        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
157
        self.assertEquals(s._path, '/home/source')
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
158
        self.failUnless(s._password is None)
159
1530.1.3 by Robert Collins
transport implementations now tested consistently.
160
        self.assertEquals(s.base, 'sftp://simple.example.com/%2Fhome/source/')
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
161
        
162
        s = SFTPTransport('sftp://ro%62ey:h%40t@example.com:2222/relative', clone_from=fake)
163
        self.assertEquals(s._host, 'example.com')
164
        self.assertEquals(s._port, 2222)
165
        self.assertEquals(s._username, 'robey')
166
        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
167
        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)
168
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
169
        # Base should not keep track of the password
1530.1.3 by Robert Collins
transport implementations now tested consistently.
170
        self.assertEquals(s.base, 'sftp://robey@example.com:2222/relative/')
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
171
172
        # Double slash should be accepted instead of using %2F
1530.1.3 by Robert Collins
transport implementations now tested consistently.
173
        s = SFTPTransport('sftp://user@example.com:22//absolute/path/', clone_from=fake)
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
174
        self.assertEquals(s._host, 'example.com')
175
        self.assertEquals(s._port, 22)
176
        self.assertEquals(s._username, 'user')
177
        self.assertEquals(s._password, None)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
178
        self.assertEquals(s._path, '/absolute/path/')
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
179
180
        # Also, don't show the port if it is the default 22
1530.1.3 by Robert Collins
transport implementations now tested consistently.
181
        self.assertEquals(s.base, 'sftp://user@example.com:22/%2Fabsolute/path/')
1185.49.23 by John Arbash Meinel
bugreport from Matthieu Moy: relpath was failing, but throwing an unhelpful exception.
182
183
    def test_relpath(self):
184
        from bzrlib.transport.sftp import SFTPTransport
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
185
        from bzrlib.errors import PathNotChild
1185.49.23 by John Arbash Meinel
bugreport from Matthieu Moy: relpath was failing, but throwing an unhelpful exception.
186
187
        s = SFTPTransport('sftp://user@host.com//abs/path', clone_from=fake)
188
        self.assertEquals(s.relpath('sftp://user@host.com//abs/path/sub'), 'sub')
189
        # Can't test this one, because we actually get an AssertionError
190
        # TODO: Consider raising an exception rather than an assert
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
191
        #self.assertRaises(PathNotChild, s.relpath, 'http://user@host.com//abs/path/sub')
192
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user2@host.com//abs/path/sub')
193
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user@otherhost.com//abs/path/sub')
194
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user@host.com:33//abs/path/sub')
195
        self.assertRaises(PathNotChild, s.relpath, 'sftp://user@host.com/abs/path/sub')
1185.49.19 by John Arbash Meinel
Testing that sftp.base gets properly sanitized
196
1185.49.25 by John Arbash Meinel
Added a couple more test cases, just in case.
197
        # Make sure it works when we don't supply a username
198
        s = SFTPTransport('sftp://host.com//abs/path', clone_from=fake)
199
        self.assertEquals(s.relpath('sftp://host.com//abs/path/sub'), 'sub')
200
201
        # Make sure it works when parts of the path will be url encoded
202
        # TODO: These may be incorrect, we might need to urllib.urlencode() before
203
        # we pass the paths into the SFTPTransport constructor
204
        s = SFTPTransport('sftp://host.com/dev/,path', clone_from=fake)
205
        self.assertEquals(s.relpath('sftp://host.com/dev/,path/sub'), 'sub')
206
        s = SFTPTransport('sftp://host.com/dev/%path', clone_from=fake)
207
        self.assertEquals(s.relpath('sftp://host.com/dev/%path/sub'), 'sub')
208
1185.33.58 by Martin Pool
[patch] Better error when sftp urls are given with invalid port numbers (Matthieu Moy)
209
    def test_parse_invalid_url(self):
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
210
        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)
211
        try:
212
            s = SFTPTransport('sftp://lilypond.org:~janneke/public_html/bzr/gub',
213
                              clone_from=fake)
214
            self.fail('expected exception not raised')
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
215
        except TransportError, e:
1185.33.58 by Martin Pool
[patch] Better error when sftp urls are given with invalid port numbers (Matthieu Moy)
216
            self.assertEquals(str(e), 
217
                    '~janneke: invalid port number')
218
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
219
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
220
class SFTPBranchTest(TestCaseWithSFTPServer):
221
    """Test some stuff when accessing a bzr Branch over sftp"""
222
223
    def test_lock_file(self):
224
        """Make sure that a Branch accessed over sftp tries to lock itself."""
225
        b = Branch.initialize(self._sftp_url)
226
        self.failUnlessExists('.bzr/')
227
        self.failUnlessExists('.bzr/branch-format')
228
        self.failUnlessExists('.bzr/branch-lock')
229
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
230
        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
231
        b.lock_write()
232
        self.failUnlessExists('.bzr/branch-lock.write-lock')
233
        b.unlock()
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
234
        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
235
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.
236
    def test_no_working_tree(self):
237
        b = Branch.initialize(self._sftp_url)
238
        self.assertRaises(errors.NoWorkingTree, b.working_tree)
239
240
    def test_push_support(self):
241
        self.build_tree(['a/', 'a/foo'])
242
        b = Branch.initialize('a')
243
        t = b.working_tree()
244
        t.add('foo')
245
        t.commit('foo', rev_id='a1')
246
247
        os.mkdir('b')
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
248
        b2 = Branch.initialize(self._sftp_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.
249
        b2.pull(b)
250
251
        self.assertEquals(b2.revision_history(), ['a1'])
252
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.
253
        open('a/foo', 'wt').write('something new in foo\n')
254
        t.commit('new', rev_id='a2')
255
        b2.pull(b)
256
257
        self.assertEquals(b2.revision_history(), ['a1', 'a2'])
258
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
259