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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Tests for the urlutils wrapper."""
23
from bzrlib import osutils, urlutils, win32utils
25
from bzrlib.errors import InvalidURL, InvalidURLJoin, InvalidRebaseURLs
26
from bzrlib.tests import TestCaseInTempDir, TestCase, TestSkipped
29
class TestUrlToPath(TestCase):
31
def test_basename(self):
32
# bzrlib.urlutils.basename
33
# Test bzrlib.urlutils.split()
34
basename = urlutils.basename
35
if sys.platform == 'win32':
36
self.assertRaises(InvalidURL, basename, 'file:///path/to/foo')
37
self.assertEqual('foo', basename('file:///C|/foo'))
38
self.assertEqual('foo', basename('file:///C:/foo'))
39
self.assertEqual('', basename('file:///C:/'))
41
self.assertEqual('foo', basename('file:///foo'))
42
self.assertEqual('', basename('file:///'))
44
self.assertEqual('foo', basename('http://host/path/to/foo'))
45
self.assertEqual('foo', basename('http://host/path/to/foo/'))
47
basename('http://host/path/to/foo/', exclude_trailing_slash=False))
48
self.assertEqual('path', basename('http://host/path'))
49
self.assertEqual('', basename('http://host/'))
50
self.assertEqual('', basename('http://host'))
51
self.assertEqual('path', basename('http:///nohost/path'))
53
self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path'))
54
self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path/'))
55
self.assertEqual('', basename('random+scheme://user:pass@ahost:port/'))
58
self.assertEqual('foo', basename('path/to/foo'))
59
self.assertEqual('foo', basename('path/to/foo/'))
60
self.assertEqual('', basename('path/to/foo/',
61
exclude_trailing_slash=False))
62
self.assertEqual('foo', basename('path/../foo'))
63
self.assertEqual('foo', basename('../path/foo'))
65
def test_normalize_url_files(self):
66
# Test that local paths are properly normalized
67
normalize_url = urlutils.normalize_url
69
def norm_file(expected, path):
70
url = normalize_url(path)
71
self.assertStartsWith(url, 'file:///')
72
if sys.platform == 'win32':
73
url = url[len('file:///C:'):]
75
url = url[len('file://'):]
77
self.assertEndsWith(url, expected)
79
norm_file('path/to/foo', 'path/to/foo')
80
norm_file('/path/to/foo', '/path/to/foo')
81
norm_file('path/to/foo', '../path/to/foo')
83
# Local paths are assumed to *not* be escaped at all
85
u'uni/\xb5'.encode(bzrlib.user_encoding)
87
# locale cannot handle unicode
90
norm_file('uni/%C2%B5', u'uni/\xb5')
92
norm_file('uni/%25C2%25B5', u'uni/%C2%B5')
93
norm_file('uni/%20b', u'uni/ b')
94
# All the crazy characters get escaped in local paths => file:/// urls
95
# The ' ' character must not be at the end, because on win32
96
# it gets stripped off by ntpath.abspath
97
norm_file('%27%20%3B/%3F%3A%40%26%3D%2B%24%2C%23', "' ;/?:@&=+$,#")
99
def test_normalize_url_hybrid(self):
100
# Anything with a scheme:// should be treated as a hybrid url
101
# which changes what characters get escaped.
102
normalize_url = urlutils.normalize_url
104
eq = self.assertEqual
105
eq('file:///foo/', normalize_url(u'file:///foo/'))
106
eq('file:///foo/%20', normalize_url(u'file:///foo/ '))
107
eq('file:///foo/%20', normalize_url(u'file:///foo/%20'))
108
# Don't escape reserved characters
109
eq('file:///ab_c.d-e/%f:?g&h=i+j;k,L#M$',
110
normalize_url('file:///ab_c.d-e/%f:?g&h=i+j;k,L#M$'))
111
eq('http://ab_c.d-e/%f:?g&h=i+j;k,L#M$',
112
normalize_url('http://ab_c.d-e/%f:?g&h=i+j;k,L#M$'))
114
# Escape unicode characters, but not already escaped chars
115
eq('http://host/ab/%C2%B5/%C2%B5',
116
normalize_url(u'http://host/ab/%C2%B5/\xb5'))
118
# Unescape characters that don't need to be escaped
119
eq('http://host/~bob%2525-._',
120
normalize_url('http://host/%7Ebob%2525%2D%2E%5F'))
121
eq('http://host/~bob%2525-._',
122
normalize_url(u'http://host/%7Ebob%2525%2D%2E%5F'))
124
# Normalize verifies URLs when they are not unicode
125
# (indicating they did not come from the user)
126
self.assertRaises(InvalidURL, normalize_url, 'http://host/\xb5')
127
self.assertRaises(InvalidURL, normalize_url, 'http://host/ ')
129
def test_url_scheme_re(self):
130
# Test paths that may be URLs
131
def test_one(url, scheme_and_path):
132
"""Assert that _url_scheme_re correctly matches
134
:param scheme_and_path: The (scheme, path) that should be matched
135
can be None, to indicate it should not match
137
m = urlutils._url_scheme_re.match(url)
138
if scheme_and_path is None:
139
self.assertEqual(None, m)
141
self.assertEqual(scheme_and_path[0], m.group('scheme'))
142
self.assertEqual(scheme_and_path[1], m.group('path'))
145
test_one('/path', None)
146
test_one('C:/path', None)
147
test_one('../path/to/foo', None)
148
test_one(u'../path/to/fo\xe5', None)
151
test_one('http://host/path/', ('http', 'host/path/'))
152
test_one('sftp://host/path/to/foo', ('sftp', 'host/path/to/foo'))
153
test_one('file:///usr/bin', ('file', '/usr/bin'))
154
test_one('file:///C:/Windows', ('file', '/C:/Windows'))
155
test_one('file:///C|/Windows', ('file', '/C|/Windows'))
156
test_one(u'readonly+sftp://host/path/\xe5', ('readonly+sftp', u'host/path/\xe5'))
159
# Can't have slashes or colons in the scheme
160
test_one('/path/to/://foo', None)
161
test_one('path:path://foo', None)
162
# Must have more than one character for scheme
163
test_one('C://foo', None)
164
test_one('ab://foo', ('ab', 'foo'))
166
def test_dirname(self):
167
# Test bzrlib.urlutils.dirname()
168
dirname = urlutils.dirname
169
if sys.platform == 'win32':
170
self.assertRaises(InvalidURL, dirname, 'file:///path/to/foo')
171
self.assertEqual('file:///C|/', dirname('file:///C|/foo'))
172
self.assertEqual('file:///C|/', dirname('file:///C|/'))
174
self.assertEqual('file:///', dirname('file:///foo'))
175
self.assertEqual('file:///', dirname('file:///'))
177
self.assertEqual('http://host/path/to', dirname('http://host/path/to/foo'))
178
self.assertEqual('http://host/path/to', dirname('http://host/path/to/foo/'))
179
self.assertEqual('http://host/path/to/foo',
180
dirname('http://host/path/to/foo/', exclude_trailing_slash=False))
181
self.assertEqual('http://host/', dirname('http://host/path'))
182
self.assertEqual('http://host/', dirname('http://host/'))
183
self.assertEqual('http://host', dirname('http://host'))
184
self.assertEqual('http:///nohost', dirname('http:///nohost/path'))
186
self.assertEqual('random+scheme://user:pass@ahost:port/',
187
dirname('random+scheme://user:pass@ahost:port/path'))
188
self.assertEqual('random+scheme://user:pass@ahost:port/',
189
dirname('random+scheme://user:pass@ahost:port/path/'))
190
self.assertEqual('random+scheme://user:pass@ahost:port/',
191
dirname('random+scheme://user:pass@ahost:port/'))
194
self.assertEqual('path/to', dirname('path/to/foo'))
195
self.assertEqual('path/to', dirname('path/to/foo/'))
196
self.assertEqual('path/to/foo',
197
dirname('path/to/foo/', exclude_trailing_slash=False))
198
self.assertEqual('path/..', dirname('path/../foo'))
199
self.assertEqual('../path', dirname('../path/foo'))
202
def test(expected, *args):
203
joined = urlutils.join(*args)
204
self.assertEqual(expected, joined)
206
# Test relative path joining
207
test('foo', 'foo') # relative fragment with nothing is preserved.
208
test('foo/bar', 'foo', 'bar')
209
test('http://foo/bar', 'http://foo', 'bar')
210
test('http://foo/bar', 'http://foo', '.', 'bar')
211
test('http://foo/baz', 'http://foo', 'bar', '../baz')
212
test('http://foo/bar/baz', 'http://foo', 'bar/baz')
213
test('http://foo/baz', 'http://foo', 'bar/../baz')
214
test('http://foo/baz', 'http://foo/bar/', '../baz')
217
test('http://foo', 'http://foo') # abs url with nothing is preserved.
218
test('http://bar', 'http://foo', 'http://bar')
219
test('sftp://bzr/foo', 'http://foo', 'bar', 'sftp://bzr/foo')
220
test('file:///bar', 'foo', 'file:///bar')
221
test('http://bar/', 'http://foo', 'http://bar/')
222
test('http://bar/a', 'http://foo', 'http://bar/a')
223
test('http://bar/a/', 'http://foo', 'http://bar/a/')
226
test('file:///foo', 'file:///', 'foo')
227
test('file:///bar/foo', 'file:///bar/', 'foo')
228
test('http://host/foo', 'http://host/', 'foo')
229
test('http://host/', 'http://host', '')
232
# Cannot go above root
233
# Implicitly at root:
234
self.assertRaises(InvalidURLJoin, urlutils.join,
235
'http://foo', '../baz')
236
self.assertRaises(InvalidURLJoin, urlutils.join,
238
# Joining from a path explicitly under the root.
239
self.assertRaises(InvalidURLJoin, urlutils.join,
240
'http://foo/a', '../../b')
242
def test_joinpath(self):
243
def test(expected, *args):
244
joined = urlutils.joinpath(*args)
245
self.assertEqual(expected, joined)
247
# Test a single element
250
# Test relative path joining
251
test('foo/bar', 'foo', 'bar')
252
test('foo/bar', 'foo', '.', 'bar')
253
test('foo/baz', 'foo', 'bar', '../baz')
254
test('foo/bar/baz', 'foo', 'bar/baz')
255
test('foo/baz', 'foo', 'bar/../baz')
257
# Test joining to an absolute path
259
test('/foo', '/foo', '.')
260
test('/foo/bar', '/foo', 'bar')
261
test('/', '/foo', '..')
263
# Test joining with an absolute path
264
test('/bar', 'foo', '/bar')
266
# Test joining to a path with a trailing slash
267
test('foo/bar', 'foo/', 'bar')
270
# Cannot go above root
271
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '../baz')
272
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..')
273
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..')
275
def test_function_type(self):
276
if sys.platform == 'win32':
277
self.assertEqual(urlutils._win32_local_path_to_url, urlutils.local_path_to_url)
278
self.assertEqual(urlutils._win32_local_path_from_url, urlutils.local_path_from_url)
280
self.assertEqual(urlutils._posix_local_path_to_url, urlutils.local_path_to_url)
281
self.assertEqual(urlutils._posix_local_path_from_url, urlutils.local_path_from_url)
283
def test_posix_local_path_to_url(self):
284
to_url = urlutils._posix_local_path_to_url
285
self.assertEqual('file:///path/to/foo',
286
to_url('/path/to/foo'))
289
result = to_url(u'/path/to/r\xe4ksm\xf6rg\xe5s')
291
raise TestSkipped("local encoding cannot handle unicode")
293
self.assertEqual('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
294
self.assertFalse(isinstance(result, unicode))
296
def test_posix_local_path_from_url(self):
297
from_url = urlutils._posix_local_path_from_url
298
self.assertEqual('/path/to/foo',
299
from_url('file:///path/to/foo'))
300
self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
301
from_url('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
302
self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
303
from_url('file:///path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
305
self.assertRaises(InvalidURL, from_url, '/path/to/foo')
307
def test_win32_local_path_to_url(self):
308
to_url = urlutils._win32_local_path_to_url
309
self.assertEqual('file:///C:/path/to/foo',
310
to_url('C:/path/to/foo'))
311
# BOGUS: on win32, ntpath.abspath will strip trailing
312
# whitespace, so this will always fail
313
# Though under linux, it fakes abspath support
314
# and thus will succeed
315
# self.assertEqual('file:///C:/path/to/foo%20',
316
# to_url('C:/path/to/foo '))
317
self.assertEqual('file:///C:/path/to/f%20oo',
318
to_url('C:/path/to/f oo'))
320
self.assertEqual('file:///', to_url('/'))
323
result = to_url(u'd:/path/to/r\xe4ksm\xf6rg\xe5s')
325
raise TestSkipped("local encoding cannot handle unicode")
327
self.assertEqual('file:///D:/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
328
self.assertFalse(isinstance(result, unicode))
330
def test_win32_unc_path_to_url(self):
331
to_url = urlutils._win32_local_path_to_url
332
self.assertEqual('file://HOST/path',
333
to_url(r'\\HOST\path'))
334
self.assertEqual('file://HOST/path',
335
to_url('//HOST/path'))
338
result = to_url(u'//HOST/path/to/r\xe4ksm\xf6rg\xe5s')
340
raise TestSkipped("local encoding cannot handle unicode")
342
self.assertEqual('file://HOST/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
343
self.assertFalse(isinstance(result, unicode))
345
def test_win32_local_path_from_url(self):
346
from_url = urlutils._win32_local_path_from_url
347
self.assertEqual('C:/path/to/foo',
348
from_url('file:///C|/path/to/foo'))
349
self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
350
from_url('file:///d|/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
351
self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
352
from_url('file:///d:/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
353
self.assertEqual('/', from_url('file:///'))
355
self.assertRaises(InvalidURL, from_url, '/path/to/foo')
356
# Not a valid _win32 url, no drive letter
357
self.assertRaises(InvalidURL, from_url, 'file:///path/to/foo')
359
def test_win32_unc_path_from_url(self):
360
from_url = urlutils._win32_local_path_from_url
361
self.assertEqual('//HOST/path', from_url('file://HOST/path'))
362
# despite IE allows 2, 4, 5 and 6 slashes in URL to another machine
363
# we want to use only 2 slashes
364
# Firefox understand only 5 slashes in URL, but it's ugly
365
self.assertRaises(InvalidURL, from_url, 'file:////HOST/path')
366
self.assertRaises(InvalidURL, from_url, 'file://///HOST/path')
367
self.assertRaises(InvalidURL, from_url, 'file://////HOST/path')
368
# check for file://C:/ instead of file:///C:/
369
self.assertRaises(InvalidURL, from_url, 'file://C:/path')
371
def test_win32_extract_drive_letter(self):
372
extract = urlutils._win32_extract_drive_letter
373
self.assertEqual(('file:///C:', '/foo'), extract('file://', '/C:/foo'))
374
self.assertEqual(('file:///d|', '/path'), extract('file://', '/d|/path'))
375
self.assertRaises(InvalidURL, extract, 'file://', '/path')
377
def test_split(self):
378
# Test bzrlib.urlutils.split()
379
split = urlutils.split
380
if sys.platform == 'win32':
381
self.assertRaises(InvalidURL, split, 'file:///path/to/foo')
382
self.assertEqual(('file:///C|/', 'foo'), split('file:///C|/foo'))
383
self.assertEqual(('file:///C:/', ''), split('file:///C:/'))
385
self.assertEqual(('file:///', 'foo'), split('file:///foo'))
386
self.assertEqual(('file:///', ''), split('file:///'))
388
self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo'))
389
self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo/'))
390
self.assertEqual(('http://host/path/to/foo', ''),
391
split('http://host/path/to/foo/', exclude_trailing_slash=False))
392
self.assertEqual(('http://host/', 'path'), split('http://host/path'))
393
self.assertEqual(('http://host/', ''), split('http://host/'))
394
self.assertEqual(('http://host', ''), split('http://host'))
395
self.assertEqual(('http:///nohost', 'path'), split('http:///nohost/path'))
397
self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
398
split('random+scheme://user:pass@ahost:port/path'))
399
self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
400
split('random+scheme://user:pass@ahost:port/path/'))
401
self.assertEqual(('random+scheme://user:pass@ahost:port/', ''),
402
split('random+scheme://user:pass@ahost:port/'))
405
self.assertEqual(('path/to', 'foo'), split('path/to/foo'))
406
self.assertEqual(('path/to', 'foo'), split('path/to/foo/'))
407
self.assertEqual(('path/to/foo', ''),
408
split('path/to/foo/', exclude_trailing_slash=False))
409
self.assertEqual(('path/..', 'foo'), split('path/../foo'))
410
self.assertEqual(('../path', 'foo'), split('../path/foo'))
412
def test_win32_strip_local_trailing_slash(self):
413
strip = urlutils._win32_strip_local_trailing_slash
414
self.assertEqual('file://', strip('file://'))
415
self.assertEqual('file:///', strip('file:///'))
416
self.assertEqual('file:///C', strip('file:///C'))
417
self.assertEqual('file:///C:', strip('file:///C:'))
418
self.assertEqual('file:///d|', strip('file:///d|'))
419
self.assertEqual('file:///C:/', strip('file:///C:/'))
420
self.assertEqual('file:///C:/a', strip('file:///C:/a/'))
422
def test_strip_trailing_slash(self):
423
sts = urlutils.strip_trailing_slash
424
if sys.platform == 'win32':
425
self.assertEqual('file:///C|/', sts('file:///C|/'))
426
self.assertEqual('file:///C:/foo', sts('file:///C:/foo'))
427
self.assertEqual('file:///C|/foo', sts('file:///C|/foo/'))
429
self.assertEqual('file:///', sts('file:///'))
430
self.assertEqual('file:///foo', sts('file:///foo'))
431
self.assertEqual('file:///foo', sts('file:///foo/'))
433
self.assertEqual('http://host/', sts('http://host/'))
434
self.assertEqual('http://host/foo', sts('http://host/foo'))
435
self.assertEqual('http://host/foo', sts('http://host/foo/'))
437
# No need to fail just because the slash is missing
438
self.assertEqual('http://host', sts('http://host'))
439
# TODO: jam 20060502 Should this raise InvalidURL?
440
self.assertEqual('file://', sts('file://'))
442
self.assertEqual('random+scheme://user:pass@ahost:port/path',
443
sts('random+scheme://user:pass@ahost:port/path'))
444
self.assertEqual('random+scheme://user:pass@ahost:port/path',
445
sts('random+scheme://user:pass@ahost:port/path/'))
446
self.assertEqual('random+scheme://user:pass@ahost:port/',
447
sts('random+scheme://user:pass@ahost:port/'))
449
# Make sure relative paths work too
450
self.assertEqual('path/to/foo', sts('path/to/foo'))
451
self.assertEqual('path/to/foo', sts('path/to/foo/'))
452
self.assertEqual('../to/foo', sts('../to/foo/'))
453
self.assertEqual('path/../foo', sts('path/../foo/'))
455
def test_unescape_for_display_utf8(self):
456
# Test that URLs are converted to nice unicode strings for display
457
def test(expected, url, encoding='utf-8'):
458
disp_url = urlutils.unescape_for_display(url, encoding=encoding)
459
self.assertIsInstance(disp_url, unicode)
460
self.assertEqual(expected, disp_url)
462
test('http://foo', 'http://foo')
463
if sys.platform == 'win32':
464
test('C:/foo/path', 'file:///C|/foo/path')
465
test('C:/foo/path', 'file:///C:/foo/path')
467
test('/foo/path', 'file:///foo/path')
469
test('http://foo/%2Fbaz', 'http://foo/%2Fbaz')
470
test(u'http://host/r\xe4ksm\xf6rg\xe5s',
471
'http://host/r%C3%A4ksm%C3%B6rg%C3%A5s')
473
# Make sure special escaped characters stay escaped
474
test(u'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23',
475
'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23')
477
# Can we handle sections that don't have utf-8 encoding?
478
test(u'http://host/%EE%EE%EE/r\xe4ksm\xf6rg\xe5s',
479
'http://host/%EE%EE%EE/r%C3%A4ksm%C3%B6rg%C3%A5s')
481
# Test encoding into output that can handle some characters
482
test(u'http://host/%EE%EE%EE/r\xe4ksm\xf6rg\xe5s',
483
'http://host/%EE%EE%EE/r%C3%A4ksm%C3%B6rg%C3%A5s',
484
encoding='iso-8859-1')
486
# This one can be encoded into utf8
487
test(u'http://host/\u062c\u0648\u062c\u0648',
488
'http://host/%d8%ac%d9%88%d8%ac%d9%88',
491
# This can't be put into 8859-1 and so stays as escapes
492
test(u'http://host/%d8%ac%d9%88%d8%ac%d9%88',
493
'http://host/%d8%ac%d9%88%d8%ac%d9%88',
494
encoding='iso-8859-1')
496
def test_escape(self):
497
self.assertEqual('%25', urlutils.escape('%'))
498
self.assertEqual('%C3%A5', urlutils.escape(u'\xe5'))
499
self.assertFalse(isinstance(urlutils.escape(u'\xe5'), unicode))
501
def test_unescape(self):
502
self.assertEqual('%', urlutils.unescape('%25'))
503
self.assertEqual(u'\xe5', urlutils.unescape('%C3%A5'))
505
self.assertRaises(InvalidURL, urlutils.unescape, u'\xe5')
506
self.assertRaises(InvalidURL, urlutils.unescape, '\xe5')
507
self.assertRaises(InvalidURL, urlutils.unescape, '%E5')
509
def test_escape_unescape(self):
510
self.assertEqual(u'\xe5', urlutils.unescape(urlutils.escape(u'\xe5')))
511
self.assertEqual('%', urlutils.unescape(urlutils.escape('%')))
513
def test_relative_url(self):
514
def test(expected, base, other):
515
result = urlutils.relative_url(base, other)
516
self.assertEqual(expected, result)
518
test('a', 'http://host/', 'http://host/a')
519
test('http://entirely/different', 'sftp://host/branch',
520
'http://entirely/different')
521
test('../person/feature', 'http://host/branch/mainline',
522
'http://host/branch/person/feature')
523
test('..', 'http://host/branch', 'http://host/')
524
test('http://host2/branch', 'http://host1/branch', 'http://host2/branch')
525
test('.', 'http://host1/branch', 'http://host1/branch')
526
test('../../../branch/2b', 'file:///home/jelmer/foo/bar/2b',
527
'file:///home/jelmer/branch/2b')
528
test('../../branch/2b', 'sftp://host/home/jelmer/bar/2b',
529
'sftp://host/home/jelmer/branch/2b')
530
test('../../branch/feature/%2b', 'http://host/home/jelmer/bar/%2b',
531
'http://host/home/jelmer/branch/feature/%2b')
532
test('../../branch/feature/2b', 'http://host/home/jelmer/bar/2b/',
533
'http://host/home/jelmer/branch/feature/2b')
534
# relative_url should preserve a trailing slash
535
test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b/',
536
'http://host/home/jelmer/branch/feature/2b/')
537
test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b',
538
'http://host/home/jelmer/branch/feature/2b/')
540
# TODO: treat http://host as http://host/
541
# relative_url is typically called from a branch.base or
542
# transport.base which always ends with a /
543
#test('a', 'http://host', 'http://host/a')
544
test('http://host/a', 'http://host', 'http://host/a')
545
#test('.', 'http://host', 'http://host/')
546
test('http://host/', 'http://host', 'http://host/')
547
#test('.', 'http://host/', 'http://host')
548
test('http://host', 'http://host/', 'http://host')
550
# On Windows file:///C:/path/to and file:///D:/other/path
551
# should not use relative url over the non-existent '/' directory.
552
if sys.platform == 'win32':
554
test('../../other/path',
555
'file:///C:/path/to', 'file:///C:/other/path')
556
#~next two tests is failed, i.e. urlutils.relative_url expects
557
#~to see normalized file URLs?
558
#~test('../../other/path',
559
#~ 'file:///C:/path/to', 'file:///c:/other/path')
560
#~test('../../other/path',
561
#~ 'file:///C:/path/to', 'file:///C|/other/path')
563
# check UNC paths too
564
test('../../other/path',
565
'file://HOST/base/path/to', 'file://HOST/base/other/path')
566
# on different drives
567
test('file:///D:/other/path',
568
'file:///C:/path/to', 'file:///D:/other/path')
569
# TODO: strictly saying in UNC path //HOST/base is full analog
570
# of drive letter for hard disk, and this situation is also
571
# should be exception from rules. [bialix 20071221]
574
class TestCwdToURL(TestCaseInTempDir):
575
"""Test that local_path_to_url works base on the cwd"""
578
# This test will fail if getcwd is not ascii
582
url = urlutils.local_path_to_url('.')
583
self.assertEndsWith(url, '/mytest')
585
def test_non_ascii(self):
586
if win32utils.winver == 'Windows 98':
587
raise TestSkipped('Windows 98 cannot handle unicode filenames')
592
raise TestSkipped('cannot create unicode directory')
596
# On Mac OSX this directory is actually:
597
# u'/dode\u0301' => '/dode\xcc\x81
598
# but we should normalize it back to
599
# u'/dod\xe9' => '/dod\xc3\xa9'
600
url = urlutils.local_path_to_url('.')
601
self.assertEndsWith(url, '/dod%C3%A9')
604
class TestDeriveToLocation(TestCase):
605
"""Test that the mapping of FROM_LOCATION to TO_LOCATION works."""
607
def test_to_locations_derived_from_paths(self):
608
derive = urlutils.derive_to_location
609
self.assertEqual("bar", derive("bar"))
610
self.assertEqual("bar", derive("../bar"))
611
self.assertEqual("bar", derive("/foo/bar"))
612
self.assertEqual("bar", derive("c:/foo/bar"))
613
self.assertEqual("bar", derive("c:bar"))
615
def test_to_locations_derived_from_urls(self):
616
derive = urlutils.derive_to_location
617
self.assertEqual("bar", derive("http://foo/bar"))
618
self.assertEqual("bar", derive("bzr+ssh://foo/bar"))
619
self.assertEqual("foo-bar", derive("lp:foo-bar"))
622
class TestRebaseURL(TestCase):
623
"""Test the behavior of rebase_url."""
625
def test_non_relative(self):
626
result = urlutils.rebase_url('file://foo', 'file://foo',
628
self.assertEqual('file://foo', result)
629
result = urlutils.rebase_url('/foo', 'file://foo',
631
self.assertEqual('/foo', result)
633
def test_different_ports(self):
634
e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
635
'foo', 'http://bar:80', 'http://bar:81')
636
self.assertEqual(str(e), "URLs differ by more than path:"
637
" 'http://bar:80' and 'http://bar:81'")
639
def test_different_hosts(self):
640
e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
641
'foo', 'http://bar', 'http://baz')
642
self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
645
def test_different_protocol(self):
646
e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
647
'foo', 'http://bar', 'ftp://bar')
648
self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
651
def test_rebase_success(self):
652
self.assertEqual('../bar', urlutils.rebase_url('bar', 'http://baz/',
654
self.assertEqual('qux/bar', urlutils.rebase_url('bar',
655
'http://baz/qux', 'http://baz/'))
656
self.assertEqual('.', urlutils.rebase_url('foo',
657
'http://bar/', 'http://bar/foo/'))
658
self.assertEqual('qux/bar', urlutils.rebase_url('../bar',
659
'http://baz/qux/foo', 'http://baz/'))
661
def test_determine_relative_path(self):
662
self.assertEqual('../../baz/bar',
663
urlutils.determine_relative_path(
664
'/qux/quxx', '/baz/bar'))
665
self.assertEqual('..',
666
urlutils.determine_relative_path(
668
self.assertEqual('baz',
669
urlutils.determine_relative_path(
671
self.assertEqual('.', urlutils.determine_relative_path(