~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005 Canonical Ltd
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
16
17
"""Tests for the urlutils wrapper."""
18
19
import os
1830.3.17 by John Arbash Meinel
list_files() with wrong normalized_filename code raises exceptions. Fix this
20
import re
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
21
import sys
22
2279.4.2 by Alexander Belchenko
Don't do normpath after abspath, because this function is called inside abspath
23
from bzrlib import osutils, urlutils, win32utils
3242.3.33 by Aaron Bentley
Handle relative URL stacking cleanly
24
from bzrlib.errors import InvalidURL, InvalidURLJoin, InvalidRebaseURLs
1685.1.75 by Wouter van Heyst
more tests handle LANG=C
25
from bzrlib.tests import TestCaseInTempDir, TestCase, TestSkipped
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
26
27
28
class TestUrlToPath(TestCase):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
29
1685.1.49 by John Arbash Meinel
Added bzrlib.urlutils.split and basename + dirname
30
    def test_basename(self):
31
        # bzrlib.urlutils.basename
32
        # Test bzrlib.urlutils.split()
33
        basename = urlutils.basename
34
        if sys.platform == 'win32':
35
            self.assertRaises(InvalidURL, basename, 'file:///path/to/foo')
36
            self.assertEqual('foo', basename('file:///C|/foo'))
1685.1.78 by Wouter van Heyst
more code cleanup
37
            self.assertEqual('foo', basename('file:///C:/foo'))
38
            self.assertEqual('', basename('file:///C:/'))
1685.1.49 by John Arbash Meinel
Added bzrlib.urlutils.split and basename + dirname
39
        else:
40
            self.assertEqual('foo', basename('file:///foo'))
41
            self.assertEqual('', basename('file:///'))
42
43
        self.assertEqual('foo', basename('http://host/path/to/foo'))
44
        self.assertEqual('foo', basename('http://host/path/to/foo/'))
45
        self.assertEqual('',
46
            basename('http://host/path/to/foo/', exclude_trailing_slash=False))
47
        self.assertEqual('path', basename('http://host/path'))
48
        self.assertEqual('', basename('http://host/'))
49
        self.assertEqual('', basename('http://host'))
50
        self.assertEqual('path', basename('http:///nohost/path'))
51
52
        self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path'))
53
        self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path/'))
54
        self.assertEqual('', basename('random+scheme://user:pass@ahost:port/'))
55
56
        # relative paths
57
        self.assertEqual('foo', basename('path/to/foo'))
58
        self.assertEqual('foo', basename('path/to/foo/'))
59
        self.assertEqual('', basename('path/to/foo/',
60
            exclude_trailing_slash=False))
61
        self.assertEqual('foo', basename('path/../foo'))
62
        self.assertEqual('foo', basename('../path/foo'))
63
1685.1.51 by John Arbash Meinel
Working on getting normalize_url working.
64
    def test_normalize_url_files(self):
65
        # Test that local paths are properly normalized
66
        normalize_url = urlutils.normalize_url
67
68
        def norm_file(expected, path):
69
            url = normalize_url(path)
70
            self.assertStartsWith(url, 'file:///')
71
            if sys.platform == 'win32':
72
                url = url[len('file:///C:'):]
73
            else:
74
                url = url[len('file://'):]
75
1685.1.53 by John Arbash Meinel
Updated normalize_url
76
            self.assertEndsWith(url, expected)
1685.1.51 by John Arbash Meinel
Working on getting normalize_url working.
77
78
        norm_file('path/to/foo', 'path/to/foo')
79
        norm_file('/path/to/foo', '/path/to/foo')
80
        norm_file('path/to/foo', '../path/to/foo')
81
82
        # Local paths are assumed to *not* be escaped at all
1685.1.75 by Wouter van Heyst
more tests handle LANG=C
83
        try:
3224.5.4 by Andrew Bennetts
Fix test suite, mainly weeding out uses of bzrlib.user_encoding.
84
            u'uni/\xb5'.encode(osutils.get_user_encoding())
1685.1.75 by Wouter van Heyst
more tests handle LANG=C
85
        except UnicodeError:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
86
            # locale cannot handle unicode
1685.1.75 by Wouter van Heyst
more tests handle LANG=C
87
            pass
88
        else:
89
            norm_file('uni/%C2%B5', u'uni/\xb5')
90
1685.1.51 by John Arbash Meinel
Working on getting normalize_url working.
91
        norm_file('uni/%25C2%25B5', u'uni/%C2%B5')
92
        norm_file('uni/%20b', u'uni/ b')
93
        # All the crazy characters get escaped in local paths => file:/// urls
1711.4.4 by John Arbash Meinel
Fix some broken tests because of stupid ntpath.abspath behavior
94
        # The ' ' character must not be at the end, because on win32
95
        # it gets stripped off by ntpath.abspath
96
        norm_file('%27%20%3B/%3F%3A%40%26%3D%2B%24%2C%23', "' ;/?:@&=+$,#")
1685.1.51 by John Arbash Meinel
Working on getting normalize_url working.
97
98
    def test_normalize_url_hybrid(self):
99
        # Anything with a scheme:// should be treated as a hybrid url
100
        # which changes what characters get escaped.
101
        normalize_url = urlutils.normalize_url
102
103
        eq = self.assertEqual
104
        eq('file:///foo/', normalize_url(u'file:///foo/'))
105
        eq('file:///foo/%20', normalize_url(u'file:///foo/ '))
106
        eq('file:///foo/%20', normalize_url(u'file:///foo/%20'))
107
        # Don't escape reserved characters
108
        eq('file:///ab_c.d-e/%f:?g&h=i+j;k,L#M$',
109
            normalize_url('file:///ab_c.d-e/%f:?g&h=i+j;k,L#M$'))
110
        eq('http://ab_c.d-e/%f:?g&h=i+j;k,L#M$',
111
            normalize_url('http://ab_c.d-e/%f:?g&h=i+j;k,L#M$'))
112
113
        # Escape unicode characters, but not already escaped chars
114
        eq('http://host/ab/%C2%B5/%C2%B5',
115
            normalize_url(u'http://host/ab/%C2%B5/\xb5'))
116
2208.4.1 by Andrew Bennetts
normalize_url should normalise escaping of unreserved characters, like '~'.
117
        # Unescape characters that don't need to be escaped
118
        eq('http://host/~bob%2525-._',
119
                normalize_url('http://host/%7Ebob%2525%2D%2E%5F'))
120
        eq('http://host/~bob%2525-._',
121
                normalize_url(u'http://host/%7Ebob%2525%2D%2E%5F'))
122
1685.1.51 by John Arbash Meinel
Working on getting normalize_url working.
123
        # Normalize verifies URLs when they are not unicode
124
        # (indicating they did not come from the user)
125
        self.assertRaises(InvalidURL, normalize_url, 'http://host/\xb5')
126
        self.assertRaises(InvalidURL, normalize_url, 'http://host/ ')
1685.1.50 by John Arbash Meinel
Added an re for handling scheme paths.
127
128
    def test_url_scheme_re(self):
129
        # Test paths that may be URLs
130
        def test_one(url, scheme_and_path):
131
            """Assert that _url_scheme_re correctly matches
132
133
            :param scheme_and_path: The (scheme, path) that should be matched
134
                can be None, to indicate it should not match
135
            """
136
            m = urlutils._url_scheme_re.match(url)
137
            if scheme_and_path is None:
138
                self.assertEqual(None, m)
139
            else:
140
                self.assertEqual(scheme_and_path[0], m.group('scheme'))
141
                self.assertEqual(scheme_and_path[1], m.group('path'))
142
143
        # Local paths
144
        test_one('/path', None)
145
        test_one('C:/path', None)
146
        test_one('../path/to/foo', None)
147
        test_one(u'../path/to/fo\xe5', None)
148
149
        # Real URLS
150
        test_one('http://host/path/', ('http', 'host/path/'))
151
        test_one('sftp://host/path/to/foo', ('sftp', 'host/path/to/foo'))
152
        test_one('file:///usr/bin', ('file', '/usr/bin'))
153
        test_one('file:///C:/Windows', ('file', '/C:/Windows'))
154
        test_one('file:///C|/Windows', ('file', '/C|/Windows'))
155
        test_one(u'readonly+sftp://host/path/\xe5', ('readonly+sftp', u'host/path/\xe5'))
156
157
        # Weird stuff
158
        # Can't have slashes or colons in the scheme
159
        test_one('/path/to/://foo', None)
160
        test_one('path:path://foo', None)
161
        # Must have more than one character for scheme
162
        test_one('C://foo', None)
163
        test_one('ab://foo', ('ab', 'foo'))
164
1685.1.49 by John Arbash Meinel
Added bzrlib.urlutils.split and basename + dirname
165
    def test_dirname(self):
166
        # Test bzrlib.urlutils.dirname()
167
        dirname = urlutils.dirname
168
        if sys.platform == 'win32':
169
            self.assertRaises(InvalidURL, dirname, 'file:///path/to/foo')
170
            self.assertEqual('file:///C|/', dirname('file:///C|/foo'))
171
            self.assertEqual('file:///C|/', dirname('file:///C|/'))
172
        else:
173
            self.assertEqual('file:///', dirname('file:///foo'))
174
            self.assertEqual('file:///', dirname('file:///'))
175
176
        self.assertEqual('http://host/path/to', dirname('http://host/path/to/foo'))
177
        self.assertEqual('http://host/path/to', dirname('http://host/path/to/foo/'))
178
        self.assertEqual('http://host/path/to/foo',
179
            dirname('http://host/path/to/foo/', exclude_trailing_slash=False))
180
        self.assertEqual('http://host/', dirname('http://host/path'))
181
        self.assertEqual('http://host/', dirname('http://host/'))
182
        self.assertEqual('http://host', dirname('http://host'))
183
        self.assertEqual('http:///nohost', dirname('http:///nohost/path'))
184
185
        self.assertEqual('random+scheme://user:pass@ahost:port/',
186
            dirname('random+scheme://user:pass@ahost:port/path'))
187
        self.assertEqual('random+scheme://user:pass@ahost:port/',
188
            dirname('random+scheme://user:pass@ahost:port/path/'))
189
        self.assertEqual('random+scheme://user:pass@ahost:port/',
190
            dirname('random+scheme://user:pass@ahost:port/'))
191
192
        # relative paths
193
        self.assertEqual('path/to', dirname('path/to/foo'))
194
        self.assertEqual('path/to', dirname('path/to/foo/'))
195
        self.assertEqual('path/to/foo',
196
            dirname('path/to/foo/', exclude_trailing_slash=False))
197
        self.assertEqual('path/..', dirname('path/../foo'))
198
        self.assertEqual('../path', dirname('../path/foo'))
199
1685.1.55 by John Arbash Meinel
Adding bzrlib.urlutils.join() to handle joining URLs
200
    def test_join(self):
201
        def test(expected, *args):
202
            joined = urlutils.join(*args)
203
            self.assertEqual(expected, joined)
204
205
        # Test relative path joining
2018.5.100 by Andrew Bennetts
Fix IndexError in urlutils.join with 'http://host/a' and '../../b'.
206
        test('foo', 'foo') # relative fragment with nothing is preserved.
1685.1.55 by John Arbash Meinel
Adding bzrlib.urlutils.join() to handle joining URLs
207
        test('foo/bar', 'foo', 'bar')
208
        test('http://foo/bar', 'http://foo', 'bar')
209
        test('http://foo/bar', 'http://foo', '.', 'bar')
210
        test('http://foo/baz', 'http://foo', 'bar', '../baz')
211
        test('http://foo/bar/baz', 'http://foo', 'bar/baz')
212
        test('http://foo/baz', 'http://foo', 'bar/../baz')
2018.5.93 by Andrew Bennetts
Fix another bug in urlutils.join.
213
        test('http://foo/baz', 'http://foo/bar/', '../baz')
1685.1.55 by John Arbash Meinel
Adding bzrlib.urlutils.join() to handle joining URLs
214
215
        # Absolute paths
2018.5.100 by Andrew Bennetts
Fix IndexError in urlutils.join with 'http://host/a' and '../../b'.
216
        test('http://foo', 'http://foo') # abs url with nothing is preserved.
1685.1.55 by John Arbash Meinel
Adding bzrlib.urlutils.join() to handle joining URLs
217
        test('http://bar', 'http://foo', 'http://bar')
218
        test('sftp://bzr/foo', 'http://foo', 'bar', 'sftp://bzr/foo')
219
        test('file:///bar', 'foo', 'file:///bar')
2018.5.92 by Andrew Bennetts
Small bugfix to urlutils.join: join('anything', 'http://bar/a/') should not strip the trailing slash.
220
        test('http://bar/', 'http://foo', 'http://bar/')
221
        test('http://bar/a', 'http://foo', 'http://bar/a')
222
        test('http://bar/a/', 'http://foo', 'http://bar/a/')
1711.2.49 by John Arbash Meinel
urlutils.join should work for root paths.
223
224
        # From a base path
225
        test('file:///foo', 'file:///', 'foo')
226
        test('file:///bar/foo', 'file:///bar/', 'foo')
227
        test('http://host/foo', 'http://host/', 'foo')
228
        test('http://host/', 'http://host', '')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
229
1685.1.55 by John Arbash Meinel
Adding bzrlib.urlutils.join() to handle joining URLs
230
        # Invalid joinings
231
        # Cannot go above root
2018.5.100 by Andrew Bennetts
Fix IndexError in urlutils.join with 'http://host/a' and '../../b'.
232
        # Implicitly at root:
1685.1.55 by John Arbash Meinel
Adding bzrlib.urlutils.join() to handle joining URLs
233
        self.assertRaises(InvalidURLJoin, urlutils.join,
234
                'http://foo', '../baz')
2018.5.54 by Andrew Bennetts
Fix ChrootTransportDecorator's abspath method to be consistent with its clone
235
        self.assertRaises(InvalidURLJoin, urlutils.join,
236
                'http://foo', '/..')
2018.5.100 by Andrew Bennetts
Fix IndexError in urlutils.join with 'http://host/a' and '../../b'.
237
        # Joining from a path explicitly under the root.
238
        self.assertRaises(InvalidURLJoin, urlutils.join,
239
                'http://foo/a', '../../b')
2379.1.1 by Robert Collins
urlutils improvements from hpss.
240
2018.5.46 by Andrew Bennetts
Fix ChrootTransportDecorator's clone to pass less surprising offsets to the decorated transport's clone.
241
    def test_joinpath(self):
242
        def test(expected, *args):
243
            joined = urlutils.joinpath(*args)
244
            self.assertEqual(expected, joined)
245
246
        # Test a single element
247
        test('foo', 'foo')
248
249
        # Test relative path joining
250
        test('foo/bar', 'foo', 'bar')
251
        test('foo/bar', 'foo', '.', 'bar')
252
        test('foo/baz', 'foo', 'bar', '../baz')
253
        test('foo/bar/baz', 'foo', 'bar/baz')
254
        test('foo/baz', 'foo', 'bar/../baz')
255
256
        # Test joining to an absolute path
257
        test('/foo', '/foo')
258
        test('/foo', '/foo', '.')
259
        test('/foo/bar', '/foo', 'bar')
260
        test('/', '/foo', '..')
261
262
        # Test joining with an absolute path
263
        test('/bar', 'foo', '/bar')
2018.5.53 by Andrew Bennetts
Small fix to urlutils.joinpath that was causing a misbehaviour in
264
265
        # Test joining to a path with a trailing slash
266
        test('foo/bar', 'foo/', 'bar')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
267
2018.5.46 by Andrew Bennetts
Fix ChrootTransportDecorator's clone to pass less surprising offsets to the decorated transport's clone.
268
        # Invalid joinings
269
        # Cannot go above root
270
        self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '../baz')
2018.5.54 by Andrew Bennetts
Fix ChrootTransportDecorator's abspath method to be consistent with its clone
271
        self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..')
272
        self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..')
2018.5.46 by Andrew Bennetts
Fix ChrootTransportDecorator's clone to pass less surprising offsets to the decorated transport's clone.
273
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
274
    def test_function_type(self):
275
        if sys.platform == 'win32':
276
            self.assertEqual(urlutils._win32_local_path_to_url, urlutils.local_path_to_url)
277
            self.assertEqual(urlutils._win32_local_path_from_url, urlutils.local_path_from_url)
278
        else:
279
            self.assertEqual(urlutils._posix_local_path_to_url, urlutils.local_path_to_url)
280
            self.assertEqual(urlutils._posix_local_path_from_url, urlutils.local_path_from_url)
281
282
    def test_posix_local_path_to_url(self):
283
        to_url = urlutils._posix_local_path_to_url
284
        self.assertEqual('file:///path/to/foo',
285
            to_url('/path/to/foo'))
1685.1.75 by Wouter van Heyst
more tests handle LANG=C
286
287
        try:
288
            result = to_url(u'/path/to/r\xe4ksm\xf6rg\xe5s')
289
        except UnicodeError:
290
            raise TestSkipped("local encoding cannot handle unicode")
291
292
        self.assertEqual('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
3234.3.1 by Alexander Belchenko
ensure that local_path_to_url() always returns plain string, not unicode.
293
        self.assertFalse(isinstance(result, unicode))
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
294
295
    def test_posix_local_path_from_url(self):
296
        from_url = urlutils._posix_local_path_from_url
297
        self.assertEqual('/path/to/foo',
298
            from_url('file:///path/to/foo'))
299
        self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
300
            from_url('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
301
        self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
302
            from_url('file:///path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
303
304
        self.assertRaises(InvalidURL, from_url, '/path/to/foo')
305
306
    def test_win32_local_path_to_url(self):
307
        to_url = urlutils._win32_local_path_to_url
1711.4.28 by John Arbash Meinel
Update the test suite for Alexander's patch.
308
        self.assertEqual('file:///C:/path/to/foo',
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
309
            to_url('C:/path/to/foo'))
1711.4.4 by John Arbash Meinel
Fix some broken tests because of stupid ntpath.abspath behavior
310
        # BOGUS: on win32, ntpath.abspath will strip trailing
311
        #       whitespace, so this will always fail
312
        #       Though under linux, it fakes abspath support
313
        #       and thus will succeed
314
        # self.assertEqual('file:///C:/path/to/foo%20',
315
        #     to_url('C:/path/to/foo '))
1711.4.28 by John Arbash Meinel
Update the test suite for Alexander's patch.
316
        self.assertEqual('file:///C:/path/to/f%20oo',
1711.4.4 by John Arbash Meinel
Fix some broken tests because of stupid ntpath.abspath behavior
317
            to_url('C:/path/to/f oo'))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
318
3503.1.2 by adwi2
Permits Windows to serve all paths on all drives.
319
        self.assertEqual('file:///', to_url('/'))
1685.1.75 by Wouter van Heyst
more tests handle LANG=C
320
321
        try:
322
            result = to_url(u'd:/path/to/r\xe4ksm\xf6rg\xe5s')
323
        except UnicodeError:
324
            raise TestSkipped("local encoding cannot handle unicode")
325
1711.4.28 by John Arbash Meinel
Update the test suite for Alexander's patch.
326
        self.assertEqual('file:///D:/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
3234.3.1 by Alexander Belchenko
ensure that local_path_to_url() always returns plain string, not unicode.
327
        self.assertFalse(isinstance(result, unicode))
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
328
2162.2.1 by Alexander Belchenko
Testing UNC path conversion
329
    def test_win32_unc_path_to_url(self):
330
        to_url = urlutils._win32_local_path_to_url
2162.2.6 by Alexander Belchenko
Change tests for win32 UNC path to new file://HOST/path scheme
331
        self.assertEqual('file://HOST/path',
2162.2.1 by Alexander Belchenko
Testing UNC path conversion
332
            to_url(r'\\HOST\path'))
2162.2.6 by Alexander Belchenko
Change tests for win32 UNC path to new file://HOST/path scheme
333
        self.assertEqual('file://HOST/path',
2162.2.1 by Alexander Belchenko
Testing UNC path conversion
334
            to_url('//HOST/path'))
335
2162.2.5 by Alexander Belchenko
Expand test_win32_unc_path_to_url to check unicode
336
        try:
337
            result = to_url(u'//HOST/path/to/r\xe4ksm\xf6rg\xe5s')
338
        except UnicodeError:
339
            raise TestSkipped("local encoding cannot handle unicode")
340
2162.2.6 by Alexander Belchenko
Change tests for win32 UNC path to new file://HOST/path scheme
341
        self.assertEqual('file://HOST/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
3234.3.1 by Alexander Belchenko
ensure that local_path_to_url() always returns plain string, not unicode.
342
        self.assertFalse(isinstance(result, unicode))
2162.2.5 by Alexander Belchenko
Expand test_win32_unc_path_to_url to check unicode
343
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
344
    def test_win32_local_path_from_url(self):
345
        from_url = urlutils._win32_local_path_from_url
1711.4.28 by John Arbash Meinel
Update the test suite for Alexander's patch.
346
        self.assertEqual('C:/path/to/foo',
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
347
            from_url('file:///C|/path/to/foo'))
1711.4.28 by John Arbash Meinel
Update the test suite for Alexander's patch.
348
        self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
349
            from_url('file:///d|/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
1711.4.28 by John Arbash Meinel
Update the test suite for Alexander's patch.
350
        self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
1685.1.79 by Wouter van Heyst
cleanup urlutils
351
            from_url('file:///d:/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
3503.1.2 by adwi2
Permits Windows to serve all paths on all drives.
352
        self.assertEqual('/', from_url('file:///'))
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
353
354
        self.assertRaises(InvalidURL, from_url, '/path/to/foo')
355
        # Not a valid _win32 url, no drive letter
356
        self.assertRaises(InvalidURL, from_url, 'file:///path/to/foo')
357
2162.2.1 by Alexander Belchenko
Testing UNC path conversion
358
    def test_win32_unc_path_from_url(self):
359
        from_url = urlutils._win32_local_path_from_url
2162.2.6 by Alexander Belchenko
Change tests for win32 UNC path to new file://HOST/path scheme
360
        self.assertEqual('//HOST/path', from_url('file://HOST/path'))
361
        # despite IE allows 2, 4, 5 and 6 slashes in URL to another machine
362
        # we want to use only 2 slashes
363
        # Firefox understand only 5 slashes in URL, but it's ugly
2162.2.1 by Alexander Belchenko
Testing UNC path conversion
364
        self.assertRaises(InvalidURL, from_url, 'file:////HOST/path')
2162.2.6 by Alexander Belchenko
Change tests for win32 UNC path to new file://HOST/path scheme
365
        self.assertRaises(InvalidURL, from_url, 'file://///HOST/path')
2162.2.1 by Alexander Belchenko
Testing UNC path conversion
366
        self.assertRaises(InvalidURL, from_url, 'file://////HOST/path')
2162.2.6 by Alexander Belchenko
Change tests for win32 UNC path to new file://HOST/path scheme
367
        # check for file://C:/ instead of file:///C:/
368
        self.assertRaises(InvalidURL, from_url, 'file://C:/path')
2162.2.1 by Alexander Belchenko
Testing UNC path conversion
369
2162.2.6 by Alexander Belchenko
Change tests for win32 UNC path to new file://HOST/path scheme
370
    def test_win32_extract_drive_letter(self):
1711.2.43 by John Arbash Meinel
Split out win32 specific code so that it can be tested on all platforms.
371
        extract = urlutils._win32_extract_drive_letter
372
        self.assertEqual(('file:///C:', '/foo'), extract('file://', '/C:/foo'))
373
        self.assertEqual(('file:///d|', '/path'), extract('file://', '/d|/path'))
374
        self.assertRaises(InvalidURL, extract, 'file://', '/path')
375
1685.1.49 by John Arbash Meinel
Added bzrlib.urlutils.split and basename + dirname
376
    def test_split(self):
377
        # Test bzrlib.urlutils.split()
378
        split = urlutils.split
379
        if sys.platform == 'win32':
380
            self.assertRaises(InvalidURL, split, 'file:///path/to/foo')
381
            self.assertEqual(('file:///C|/', 'foo'), split('file:///C|/foo'))
1685.1.79 by Wouter van Heyst
cleanup urlutils
382
            self.assertEqual(('file:///C:/', ''), split('file:///C:/'))
1685.1.49 by John Arbash Meinel
Added bzrlib.urlutils.split and basename + dirname
383
        else:
384
            self.assertEqual(('file:///', 'foo'), split('file:///foo'))
385
            self.assertEqual(('file:///', ''), split('file:///'))
386
387
        self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo'))
388
        self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo/'))
389
        self.assertEqual(('http://host/path/to/foo', ''),
390
            split('http://host/path/to/foo/', exclude_trailing_slash=False))
391
        self.assertEqual(('http://host/', 'path'), split('http://host/path'))
392
        self.assertEqual(('http://host/', ''), split('http://host/'))
393
        self.assertEqual(('http://host', ''), split('http://host'))
394
        self.assertEqual(('http:///nohost', 'path'), split('http:///nohost/path'))
395
396
        self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
397
            split('random+scheme://user:pass@ahost:port/path'))
398
        self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
399
            split('random+scheme://user:pass@ahost:port/path/'))
400
        self.assertEqual(('random+scheme://user:pass@ahost:port/', ''),
401
            split('random+scheme://user:pass@ahost:port/'))
402
403
        # relative paths
404
        self.assertEqual(('path/to', 'foo'), split('path/to/foo'))
405
        self.assertEqual(('path/to', 'foo'), split('path/to/foo/'))
406
        self.assertEqual(('path/to/foo', ''),
407
            split('path/to/foo/', exclude_trailing_slash=False))
408
        self.assertEqual(('path/..', 'foo'), split('path/../foo'))
409
        self.assertEqual(('../path', 'foo'), split('../path/foo'))
410
2162.2.6 by Alexander Belchenko
Change tests for win32 UNC path to new file://HOST/path scheme
411
    def test_win32_strip_local_trailing_slash(self):
1711.2.44 by John Arbash Meinel
Factor out another win32 special case and add platform independent tests for it.
412
        strip = urlutils._win32_strip_local_trailing_slash
413
        self.assertEqual('file://', strip('file://'))
414
        self.assertEqual('file:///', strip('file:///'))
415
        self.assertEqual('file:///C', strip('file:///C'))
416
        self.assertEqual('file:///C:', strip('file:///C:'))
417
        self.assertEqual('file:///d|', strip('file:///d|'))
418
        self.assertEqual('file:///C:/', strip('file:///C:/'))
419
        self.assertEqual('file:///C:/a', strip('file:///C:/a/'))
420
1685.1.48 by John Arbash Meinel
Updated strip_trailing_slash to support lots more url stuff, added tests
421
    def test_strip_trailing_slash(self):
422
        sts = urlutils.strip_trailing_slash
423
        if sys.platform == 'win32':
424
            self.assertEqual('file:///C|/', sts('file:///C|/'))
1685.1.79 by Wouter van Heyst
cleanup urlutils
425
            self.assertEqual('file:///C:/foo', sts('file:///C:/foo'))
1685.1.48 by John Arbash Meinel
Updated strip_trailing_slash to support lots more url stuff, added tests
426
            self.assertEqual('file:///C|/foo', sts('file:///C|/foo/'))
427
        else:
428
            self.assertEqual('file:///', sts('file:///'))
429
            self.assertEqual('file:///foo', sts('file:///foo'))
430
            self.assertEqual('file:///foo', sts('file:///foo/'))
431
432
        self.assertEqual('http://host/', sts('http://host/'))
433
        self.assertEqual('http://host/foo', sts('http://host/foo'))
434
        self.assertEqual('http://host/foo', sts('http://host/foo/'))
435
436
        # No need to fail just because the slash is missing
437
        self.assertEqual('http://host', sts('http://host'))
438
        # TODO: jam 20060502 Should this raise InvalidURL?
439
        self.assertEqual('file://', sts('file://'))
440
441
        self.assertEqual('random+scheme://user:pass@ahost:port/path',
442
            sts('random+scheme://user:pass@ahost:port/path'))
443
        self.assertEqual('random+scheme://user:pass@ahost:port/path',
444
            sts('random+scheme://user:pass@ahost:port/path/'))
445
        self.assertEqual('random+scheme://user:pass@ahost:port/',
446
            sts('random+scheme://user:pass@ahost:port/'))
447
448
        # Make sure relative paths work too
449
        self.assertEqual('path/to/foo', sts('path/to/foo'))
450
        self.assertEqual('path/to/foo', sts('path/to/foo/'))
451
        self.assertEqual('../to/foo', sts('../to/foo/'))
452
        self.assertEqual('path/../foo', sts('path/../foo/'))
453
1685.1.54 by John Arbash Meinel
url_for_display now makes sure output can be properly encoded.
454
    def test_unescape_for_display_utf8(self):
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
455
        # Test that URLs are converted to nice unicode strings for display
1685.1.54 by John Arbash Meinel
url_for_display now makes sure output can be properly encoded.
456
        def test(expected, url, encoding='utf-8'):
457
            disp_url = urlutils.unescape_for_display(url, encoding=encoding)
1685.1.58 by Martin Pool
urlutils.unescape_for_display should return Unicode
458
            self.assertIsInstance(disp_url, unicode)
1685.1.54 by John Arbash Meinel
url_for_display now makes sure output can be properly encoded.
459
            self.assertEqual(expected, disp_url)
1685.1.79 by Wouter van Heyst
cleanup urlutils
460
1685.1.54 by John Arbash Meinel
url_for_display now makes sure output can be properly encoded.
461
        test('http://foo', 'http://foo')
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
462
        if sys.platform == 'win32':
1711.7.13 by John Arbash Meinel
Fix expected value for urlutils tests
463
            test('C:/foo/path', 'file:///C|/foo/path')
464
            test('C:/foo/path', 'file:///C:/foo/path')
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
465
        else:
1685.1.54 by John Arbash Meinel
url_for_display now makes sure output can be properly encoded.
466
            test('/foo/path', 'file:///foo/path')
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
467
1685.1.54 by John Arbash Meinel
url_for_display now makes sure output can be properly encoded.
468
        test('http://foo/%2Fbaz', 'http://foo/%2Fbaz')
1685.1.58 by Martin Pool
urlutils.unescape_for_display should return Unicode
469
        test(u'http://host/r\xe4ksm\xf6rg\xe5s',
470
             'http://host/r%C3%A4ksm%C3%B6rg%C3%A5s')
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
471
472
        # Make sure special escaped characters stay escaped
1685.1.54 by John Arbash Meinel
url_for_display now makes sure output can be properly encoded.
473
        test(u'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23',
1685.1.58 by Martin Pool
urlutils.unescape_for_display should return Unicode
474
             'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23')
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
475
476
        # Can we handle sections that don't have utf-8 encoding?
1685.1.58 by Martin Pool
urlutils.unescape_for_display should return Unicode
477
        test(u'http://host/%EE%EE%EE/r\xe4ksm\xf6rg\xe5s',
478
             'http://host/%EE%EE%EE/r%C3%A4ksm%C3%B6rg%C3%A5s')
1685.1.54 by John Arbash Meinel
url_for_display now makes sure output can be properly encoded.
479
480
        # Test encoding into output that can handle some characters
1685.1.58 by Martin Pool
urlutils.unescape_for_display should return Unicode
481
        test(u'http://host/%EE%EE%EE/r\xe4ksm\xf6rg\xe5s',
482
             'http://host/%EE%EE%EE/r%C3%A4ksm%C3%B6rg%C3%A5s',
483
             encoding='iso-8859-1')
484
485
        # This one can be encoded into utf8
486
        test(u'http://host/\u062c\u0648\u062c\u0648',
487
             'http://host/%d8%ac%d9%88%d8%ac%d9%88',
488
             encoding='utf-8')
489
490
        # This can't be put into 8859-1 and so stays as escapes
491
        test(u'http://host/%d8%ac%d9%88%d8%ac%d9%88',
492
             'http://host/%d8%ac%d9%88%d8%ac%d9%88',
493
             encoding='iso-8859-1')
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
494
495
    def test_escape(self):
496
        self.assertEqual('%25', urlutils.escape('%'))
497
        self.assertEqual('%C3%A5', urlutils.escape(u'\xe5'))
3234.3.1 by Alexander Belchenko
ensure that local_path_to_url() always returns plain string, not unicode.
498
        self.assertFalse(isinstance(urlutils.escape(u'\xe5'), unicode))
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
499
4098.3.1 by Jonathan Lange
Don't escape tildes
500
    def test_escape_tildes(self):
501
        self.assertEqual('~foo', urlutils.escape('~foo'))
502
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
503
    def test_unescape(self):
504
        self.assertEqual('%', urlutils.unescape('%25'))
505
        self.assertEqual(u'\xe5', urlutils.unescape('%C3%A5'))
506
507
        self.assertRaises(InvalidURL, urlutils.unescape, u'\xe5')
508
        self.assertRaises(InvalidURL, urlutils.unescape, '\xe5')
509
        self.assertRaises(InvalidURL, urlutils.unescape, '%E5')
510
511
    def test_escape_unescape(self):
512
        self.assertEqual(u'\xe5', urlutils.unescape(urlutils.escape(u'\xe5')))
513
        self.assertEqual('%', urlutils.unescape(urlutils.escape('%')))
514
1685.1.70 by Wouter van Heyst
working on get_parent, set_parent and relative urls, broken
515
    def test_relative_url(self):
516
        def test(expected, base, other):
517
            result = urlutils.relative_url(base, other)
518
            self.assertEqual(expected, result)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
519
1685.1.70 by Wouter van Heyst
working on get_parent, set_parent and relative urls, broken
520
        test('a', 'http://host/', 'http://host/a')
1685.1.71 by Wouter van Heyst
change branch.{get,set}_parent to store a relative path but return full urls
521
        test('http://entirely/different', 'sftp://host/branch',
522
                    'http://entirely/different')
523
        test('../person/feature', 'http://host/branch/mainline',
524
                    'http://host/branch/person/feature')
1685.1.70 by Wouter van Heyst
working on get_parent, set_parent and relative urls, broken
525
        test('..', 'http://host/branch', 'http://host/')
526
        test('http://host2/branch', 'http://host1/branch', 'http://host2/branch')
1685.1.71 by Wouter van Heyst
change branch.{get,set}_parent to store a relative path but return full urls
527
        test('.', 'http://host1/branch', 'http://host1/branch')
528
        test('../../../branch/2b', 'file:///home/jelmer/foo/bar/2b',
529
                    'file:///home/jelmer/branch/2b')
530
        test('../../branch/2b', 'sftp://host/home/jelmer/bar/2b',
531
                    'sftp://host/home/jelmer/branch/2b')
1685.1.79 by Wouter van Heyst
cleanup urlutils
532
        test('../../branch/feature/%2b', 'http://host/home/jelmer/bar/%2b',
533
                    'http://host/home/jelmer/branch/feature/%2b')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
534
        test('../../branch/feature/2b', 'http://host/home/jelmer/bar/2b/',
1685.1.71 by Wouter van Heyst
change branch.{get,set}_parent to store a relative path but return full urls
535
                    'http://host/home/jelmer/branch/feature/2b')
536
        # relative_url should preserve a trailing slash
537
        test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b/',
538
                    'http://host/home/jelmer/branch/feature/2b/')
539
        test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b',
540
                    'http://host/home/jelmer/branch/feature/2b/')
541
542
        # TODO: treat http://host as http://host/
543
        #       relative_url is typically called from a branch.base or
544
        #       transport.base which always ends with a /
545
        #test('a', 'http://host', 'http://host/a')
546
        test('http://host/a', 'http://host', 'http://host/a')
547
        #test('.', 'http://host', 'http://host/')
548
        test('http://host/', 'http://host', 'http://host/')
549
        #test('.', 'http://host/', 'http://host')
550
        test('http://host', 'http://host/', 'http://host')
1830.3.13 by John Arbash Meinel
Add a test that urlutils creates normalized paths
551
3139.2.1 by Alexander Belchenko
bugfix #90847: fix problem with parent location on another logical drive
552
        # On Windows file:///C:/path/to and file:///D:/other/path
553
        # should not use relative url over the non-existent '/' directory.
554
        if sys.platform == 'win32':
555
            # on the same drive
556
            test('../../other/path',
557
                'file:///C:/path/to', 'file:///C:/other/path')
558
            #~next two tests is failed, i.e. urlutils.relative_url expects
559
            #~to see normalized file URLs?
560
            #~test('../../other/path',
561
            #~    'file:///C:/path/to', 'file:///c:/other/path')
562
            #~test('../../other/path',
563
            #~    'file:///C:/path/to', 'file:///C|/other/path')
564
565
            # check UNC paths too
566
            test('../../other/path',
567
                'file://HOST/base/path/to', 'file://HOST/base/other/path')
568
            # on different drives
569
            test('file:///D:/other/path',
570
                'file:///C:/path/to', 'file:///D:/other/path')
571
            # TODO: strictly saying in UNC path //HOST/base is full analog
572
            # of drive letter for hard disk, and this situation is also
573
            # should be exception from rules. [bialix 20071221]
574
1830.3.13 by John Arbash Meinel
Add a test that urlutils creates normalized paths
575
576
class TestCwdToURL(TestCaseInTempDir):
577
    """Test that local_path_to_url works base on the cwd"""
578
579
    def test_dot(self):
580
        # This test will fail if getcwd is not ascii
1830.3.17 by John Arbash Meinel
list_files() with wrong normalized_filename code raises exceptions. Fix this
581
        os.mkdir('mytest')
582
        os.chdir('mytest')
1830.3.13 by John Arbash Meinel
Add a test that urlutils creates normalized paths
583
584
        url = urlutils.local_path_to_url('.')
1830.3.17 by John Arbash Meinel
list_files() with wrong normalized_filename code raises exceptions. Fix this
585
        self.assertEndsWith(url, '/mytest')
1830.3.13 by John Arbash Meinel
Add a test that urlutils creates normalized paths
586
587
    def test_non_ascii(self):
2279.4.2 by Alexander Belchenko
Don't do normpath after abspath, because this function is called inside abspath
588
        if win32utils.winver == 'Windows 98':
589
            raise TestSkipped('Windows 98 cannot handle unicode filenames')
590
1830.3.13 by John Arbash Meinel
Add a test that urlutils creates normalized paths
591
        try:
592
            os.mkdir(u'dod\xe9')
593
        except UnicodeError:
594
            raise TestSkipped('cannot create unicode directory')
595
596
        os.chdir(u'dod\xe9')
597
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
598
        # On Mac OSX this directory is actually:
1830.3.13 by John Arbash Meinel
Add a test that urlutils creates normalized paths
599
        #   u'/dode\u0301' => '/dode\xcc\x81
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
600
        # but we should normalize it back to
1830.3.13 by John Arbash Meinel
Add a test that urlutils creates normalized paths
601
        #   u'/dod\xe9' => '/dod\xc3\xa9'
602
        url = urlutils.local_path_to_url('.')
603
        self.assertEndsWith(url, '/dod%C3%A9')
2512.4.1 by Ian Clatworthy
Fixes #115491 - 'branch lp:projname' now creates ./projname as exected
604
605
606
class TestDeriveToLocation(TestCase):
607
    """Test that the mapping of FROM_LOCATION to TO_LOCATION works."""
608
609
    def test_to_locations_derived_from_paths(self):
610
        derive = urlutils.derive_to_location
611
        self.assertEqual("bar", derive("bar"))
612
        self.assertEqual("bar", derive("../bar"))
613
        self.assertEqual("bar", derive("/foo/bar"))
614
        self.assertEqual("bar", derive("c:/foo/bar"))
615
        self.assertEqual("bar", derive("c:bar"))
616
617
    def test_to_locations_derived_from_urls(self):
618
        derive = urlutils.derive_to_location
619
        self.assertEqual("bar", derive("http://foo/bar"))
620
        self.assertEqual("bar", derive("bzr+ssh://foo/bar"))
621
        self.assertEqual("foo-bar", derive("lp:foo-bar"))
3242.3.26 by Aaron Bentley
Implement rebase_url
622
623
624
class TestRebaseURL(TestCase):
625
    """Test the behavior of rebase_url."""
626
627
    def test_non_relative(self):
628
        result = urlutils.rebase_url('file://foo', 'file://foo',
629
                                     'file://foo/bar')
630
        self.assertEqual('file://foo', result)
631
        result = urlutils.rebase_url('/foo', 'file://foo',
632
                                     'file://foo/bar')
633
        self.assertEqual('/foo', result)
634
3242.3.36 by Aaron Bentley
Updates from review comments
635
    def test_different_ports(self):
636
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
637
                              'foo', 'http://bar:80', 'http://bar:81')
638
        self.assertEqual(str(e), "URLs differ by more than path:"
639
                         " 'http://bar:80' and 'http://bar:81'")
640
641
    def test_different_hosts(self):
3242.3.33 by Aaron Bentley
Handle relative URL stacking cleanly
642
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
643
                              'foo', 'http://bar', 'http://baz')
644
        self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
645
                         " and 'http://baz'")
3242.3.26 by Aaron Bentley
Implement rebase_url
646
3242.3.36 by Aaron Bentley
Updates from review comments
647
    def test_different_protocol(self):
648
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
649
                              'foo', 'http://bar', 'ftp://bar')
650
        self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
651
                         " and 'ftp://bar'")
652
3242.3.26 by Aaron Bentley
Implement rebase_url
653
    def test_rebase_success(self):
654
        self.assertEqual('../bar', urlutils.rebase_url('bar', 'http://baz/',
655
                         'http://baz/qux'))
656
        self.assertEqual('qux/bar', urlutils.rebase_url('bar',
657
                         'http://baz/qux', 'http://baz/'))
658
        self.assertEqual('.', urlutils.rebase_url('foo',
659
                         'http://bar/', 'http://bar/foo/'))
3567.2.1 by Michael Hudson
urlutils.rebase_url handles '..' path segments in 'url'
660
        self.assertEqual('qux/bar', urlutils.rebase_url('../bar',
661
                         'http://baz/qux/foo', 'http://baz/'))
3242.3.26 by Aaron Bentley
Implement rebase_url
662
663
    def test_determine_relative_path(self):
664
        self.assertEqual('../../baz/bar',
665
                         urlutils.determine_relative_path(
666
                         '/qux/quxx', '/baz/bar'))
667
        self.assertEqual('..',
668
                         urlutils.determine_relative_path(
669
                         '/bar/baz', '/bar'))
670
        self.assertEqual('baz',
671
                         urlutils.determine_relative_path(
672
                         '/bar', '/bar/baz'))
673
        self.assertEqual('.', urlutils.determine_relative_path(
674
                         '/bar', '/bar'))
3873.3.1 by Martin Pool
Move Transport._split_url to urlutils, and ad a simple test
675
676
677
class TestParseURL(TestCase):
678
679
    def test_parse_url(self):
680
        self.assertEqual(urlutils.parse_url('http://example.com:80/one'),
681
            ('http', None, None, 'example.com', 80, '/one'))
3873.3.2 by Martin Pool
Accept ipv6 literals in URLs
682
        self.assertEqual(urlutils.parse_url('http://[1:2:3::40]/one'),
683
                ('http', None, None, '1:2:3::40', None, '/one'))
684
        self.assertEqual(urlutils.parse_url('http://[1:2:3::40]:80/one'),
685
                ('http', None, None, '1:2:3::40', 80, '/one'))