1
# Copyright (C) 2005 Canonical Ltd
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for the urlutils wrapper."""
23
from bzrlib import osutils, urlutils, win32utils
24
from bzrlib.errors import InvalidURL, InvalidURLJoin, InvalidRebaseURLs
25
from bzrlib.tests import TestCaseInTempDir, TestCase, TestSkipped
28
class TestUrlToPath(TestCase):
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'))
37
self.assertEqual('foo', basename('file:///C:/foo'))
38
self.assertEqual('', basename('file:///C:/'))
40
self.assertEqual('foo', basename('file:///foo'))
41
self.assertEqual('', basename('file:///'))
43
self.assertEqual('foo', basename('http://host/path/to/foo'))
44
self.assertEqual('foo', basename('http://host/path/to/foo/'))
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'))
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/'))
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'))
64
def test_normalize_url_files(self):
65
# Test that local paths are properly normalized
66
normalize_url = urlutils.normalize_url
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:'):]
74
url = url[len('file://'):]
76
self.assertEndsWith(url, expected)
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')
82
# Local paths are assumed to *not* be escaped at all
84
u'uni/\xb5'.encode(osutils.get_user_encoding())
86
# locale cannot handle unicode
89
norm_file('uni/%C2%B5', u'uni/\xb5')
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
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', "' ;/?:@&=+$,#")
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
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$'))
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'))
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'))
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/ ')
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
133
:param scheme_and_path: The (scheme, path) that should be matched
134
can be None, to indicate it should not match
136
m = urlutils._url_scheme_re.match(url)
137
if scheme_and_path is None:
138
self.assertEqual(None, m)
140
self.assertEqual(scheme_and_path[0], m.group('scheme'))
141
self.assertEqual(scheme_and_path[1], m.group('path'))
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)
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'))
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'))
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|/'))
173
self.assertEqual('file:///', dirname('file:///foo'))
174
self.assertEqual('file:///', dirname('file:///'))
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'))
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/'))
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'))
201
def test(expected, *args):
202
joined = urlutils.join(*args)
203
self.assertEqual(expected, joined)
205
# Test relative path joining
206
test('foo', 'foo') # relative fragment with nothing is preserved.
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')
213
test('http://foo/baz', 'http://foo/bar/', '../baz')
216
test('http://foo', 'http://foo') # abs url with nothing is preserved.
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')
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/')
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', '')
231
# Cannot go above root
232
# Implicitly at root:
233
self.assertRaises(InvalidURLJoin, urlutils.join,
234
'http://foo', '../baz')
235
self.assertRaises(InvalidURLJoin, urlutils.join,
237
# Joining from a path explicitly under the root.
238
self.assertRaises(InvalidURLJoin, urlutils.join,
239
'http://foo/a', '../../b')
241
def test_joinpath(self):
242
def test(expected, *args):
243
joined = urlutils.joinpath(*args)
244
self.assertEqual(expected, joined)
246
# Test a single element
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')
256
# Test joining to an absolute path
258
test('/foo', '/foo', '.')
259
test('/foo/bar', '/foo', 'bar')
260
test('/', '/foo', '..')
262
# Test joining with an absolute path
263
test('/bar', 'foo', '/bar')
265
# Test joining to a path with a trailing slash
266
test('foo/bar', 'foo/', 'bar')
269
# Cannot go above root
270
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '../baz')
271
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..')
272
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..')
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)
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)
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'))
288
result = to_url(u'/path/to/r\xe4ksm\xf6rg\xe5s')
290
raise TestSkipped("local encoding cannot handle unicode")
292
self.assertEqual('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
293
self.assertFalse(isinstance(result, unicode))
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'))
304
self.assertRaises(InvalidURL, from_url, '/path/to/foo')
306
def test_win32_local_path_to_url(self):
307
to_url = urlutils._win32_local_path_to_url
308
self.assertEqual('file:///C:/path/to/foo',
309
to_url('C:/path/to/foo'))
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 '))
316
self.assertEqual('file:///C:/path/to/f%20oo',
317
to_url('C:/path/to/f oo'))
319
self.assertEqual('file:///', to_url('/'))
322
result = to_url(u'd:/path/to/r\xe4ksm\xf6rg\xe5s')
324
raise TestSkipped("local encoding cannot handle unicode")
326
self.assertEqual('file:///D:/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
327
self.assertFalse(isinstance(result, unicode))
329
def test_win32_unc_path_to_url(self):
330
to_url = urlutils._win32_local_path_to_url
331
self.assertEqual('file://HOST/path',
332
to_url(r'\\HOST\path'))
333
self.assertEqual('file://HOST/path',
334
to_url('//HOST/path'))
337
result = to_url(u'//HOST/path/to/r\xe4ksm\xf6rg\xe5s')
339
raise TestSkipped("local encoding cannot handle unicode")
341
self.assertEqual('file://HOST/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
342
self.assertFalse(isinstance(result, unicode))
344
def test_win32_local_path_from_url(self):
345
from_url = urlutils._win32_local_path_from_url
346
self.assertEqual('C:/path/to/foo',
347
from_url('file:///C|/path/to/foo'))
348
self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
349
from_url('file:///d|/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
350
self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
351
from_url('file:///d:/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
352
self.assertEqual('/', from_url('file:///'))
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')
358
def test_win32_unc_path_from_url(self):
359
from_url = urlutils._win32_local_path_from_url
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
364
self.assertRaises(InvalidURL, from_url, 'file:////HOST/path')
365
self.assertRaises(InvalidURL, from_url, 'file://///HOST/path')
366
self.assertRaises(InvalidURL, from_url, 'file://////HOST/path')
367
# check for file://C:/ instead of file:///C:/
368
self.assertRaises(InvalidURL, from_url, 'file://C:/path')
370
def test_win32_extract_drive_letter(self):
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')
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'))
382
self.assertEqual(('file:///C:/', ''), split('file:///C:/'))
384
self.assertEqual(('file:///', 'foo'), split('file:///foo'))
385
self.assertEqual(('file:///', ''), split('file:///'))
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'))
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/'))
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'))
411
def test_win32_strip_local_trailing_slash(self):
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/'))
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|/'))
425
self.assertEqual('file:///C:/foo', sts('file:///C:/foo'))
426
self.assertEqual('file:///C|/foo', sts('file:///C|/foo/'))
428
self.assertEqual('file:///', sts('file:///'))
429
self.assertEqual('file:///foo', sts('file:///foo'))
430
self.assertEqual('file:///foo', sts('file:///foo/'))
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/'))
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://'))
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/'))
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/'))
454
def test_unescape_for_display_utf8(self):
455
# Test that URLs are converted to nice unicode strings for display
456
def test(expected, url, encoding='utf-8'):
457
disp_url = urlutils.unescape_for_display(url, encoding=encoding)
458
self.assertIsInstance(disp_url, unicode)
459
self.assertEqual(expected, disp_url)
461
test('http://foo', 'http://foo')
462
if sys.platform == 'win32':
463
test('C:/foo/path', 'file:///C|/foo/path')
464
test('C:/foo/path', 'file:///C:/foo/path')
466
test('/foo/path', 'file:///foo/path')
468
test('http://foo/%2Fbaz', 'http://foo/%2Fbaz')
469
test(u'http://host/r\xe4ksm\xf6rg\xe5s',
470
'http://host/r%C3%A4ksm%C3%B6rg%C3%A5s')
472
# Make sure special escaped characters stay escaped
473
test(u'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23',
474
'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23')
476
# Can we handle sections that don't have utf-8 encoding?
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')
480
# Test encoding into output that can handle some characters
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')
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',
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')
495
def test_escape(self):
496
self.assertEqual('%25', urlutils.escape('%'))
497
self.assertEqual('%C3%A5', urlutils.escape(u'\xe5'))
498
self.assertFalse(isinstance(urlutils.escape(u'\xe5'), unicode))
500
def test_escape_tildes(self):
501
self.assertEqual('~foo', urlutils.escape('~foo'))
503
def test_unescape(self):
504
self.assertEqual('%', urlutils.unescape('%25'))
505
self.assertEqual(u'\xe5', urlutils.unescape('%C3%A5'))
507
self.assertRaises(InvalidURL, urlutils.unescape, u'\xe5')
508
self.assertRaises(InvalidURL, urlutils.unescape, '\xe5')
509
self.assertRaises(InvalidURL, urlutils.unescape, '%E5')
511
def test_escape_unescape(self):
512
self.assertEqual(u'\xe5', urlutils.unescape(urlutils.escape(u'\xe5')))
513
self.assertEqual('%', urlutils.unescape(urlutils.escape('%')))
515
def test_relative_url(self):
516
def test(expected, base, other):
517
result = urlutils.relative_url(base, other)
518
self.assertEqual(expected, result)
520
test('a', 'http://host/', 'http://host/a')
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')
525
test('..', 'http://host/branch', 'http://host/')
526
test('http://host2/branch', 'http://host1/branch', 'http://host2/branch')
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')
532
test('../../branch/feature/%2b', 'http://host/home/jelmer/bar/%2b',
533
'http://host/home/jelmer/branch/feature/%2b')
534
test('../../branch/feature/2b', 'http://host/home/jelmer/bar/2b/',
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/')
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')
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':
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')
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]
576
class TestCwdToURL(TestCaseInTempDir):
577
"""Test that local_path_to_url works base on the cwd"""
580
# This test will fail if getcwd is not ascii
584
url = urlutils.local_path_to_url('.')
585
self.assertEndsWith(url, '/mytest')
587
def test_non_ascii(self):
588
if win32utils.winver == 'Windows 98':
589
raise TestSkipped('Windows 98 cannot handle unicode filenames')
594
raise TestSkipped('cannot create unicode directory')
598
# On Mac OSX this directory is actually:
599
# u'/dode\u0301' => '/dode\xcc\x81
600
# but we should normalize it back to
601
# u'/dod\xe9' => '/dod\xc3\xa9'
602
url = urlutils.local_path_to_url('.')
603
self.assertEndsWith(url, '/dod%C3%A9')
606
class TestDeriveToLocation(TestCase):
607
"""Test that the mapping of FROM_LOCATION to TO_LOCATION works."""
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"))
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"))
624
class TestRebaseURL(TestCase):
625
"""Test the behavior of rebase_url."""
627
def test_non_relative(self):
628
result = urlutils.rebase_url('file://foo', 'file://foo',
630
self.assertEqual('file://foo', result)
631
result = urlutils.rebase_url('/foo', 'file://foo',
633
self.assertEqual('/foo', result)
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'")
641
def test_different_hosts(self):
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'"
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'"
653
def test_rebase_success(self):
654
self.assertEqual('../bar', urlutils.rebase_url('bar', 'http://baz/',
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/'))
660
self.assertEqual('qux/bar', urlutils.rebase_url('../bar',
661
'http://baz/qux/foo', 'http://baz/'))
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(
670
self.assertEqual('baz',
671
urlutils.determine_relative_path(
673
self.assertEqual('.', urlutils.determine_relative_path(
677
class TestParseURL(TestCase):
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'))
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'))