1
# Copyright (C) 2006-2010 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."""
22
from bzrlib import osutils, urlutils, win32utils
23
from bzrlib.errors import InvalidURL, InvalidURLJoin, InvalidRebaseURLs
24
from bzrlib.tests import TestCaseInTempDir, TestCase, TestSkipped
27
class TestUrlToPath(TestCase):
29
def test_basename(self):
30
# bzrlib.urlutils.basename
31
# Test bzrlib.urlutils.split()
32
basename = urlutils.basename
33
if sys.platform == 'win32':
34
self.assertRaises(InvalidURL, basename, 'file:///path/to/foo')
35
self.assertEqual('foo', basename('file:///C|/foo'))
36
self.assertEqual('foo', basename('file:///C:/foo'))
37
self.assertEqual('', basename('file:///C:/'))
39
self.assertEqual('foo', basename('file:///foo'))
40
self.assertEqual('', basename('file:///'))
42
self.assertEqual('foo', basename('http://host/path/to/foo'))
43
self.assertEqual('foo', basename('http://host/path/to/foo/'))
45
basename('http://host/path/to/foo/', exclude_trailing_slash=False))
46
self.assertEqual('path', basename('http://host/path'))
47
self.assertEqual('', basename('http://host/'))
48
self.assertEqual('', basename('http://host'))
49
self.assertEqual('path', basename('http:///nohost/path'))
51
self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path'))
52
self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path/'))
53
self.assertEqual('', basename('random+scheme://user:pass@ahost:port/'))
56
self.assertEqual('foo', basename('path/to/foo'))
57
self.assertEqual('foo', basename('path/to/foo/'))
58
self.assertEqual('', basename('path/to/foo/',
59
exclude_trailing_slash=False))
60
self.assertEqual('foo', basename('path/../foo'))
61
self.assertEqual('foo', basename('../path/foo'))
63
def test_normalize_url_files(self):
64
# Test that local paths are properly normalized
65
normalize_url = urlutils.normalize_url
67
def norm_file(expected, path):
68
url = normalize_url(path)
69
self.assertStartsWith(url, 'file:///')
70
if sys.platform == 'win32':
71
url = url[len('file:///C:'):]
73
url = url[len('file://'):]
75
self.assertEndsWith(url, expected)
77
norm_file('path/to/foo', 'path/to/foo')
78
norm_file('/path/to/foo', '/path/to/foo')
79
norm_file('path/to/foo', '../path/to/foo')
81
# Local paths are assumed to *not* be escaped at all
83
u'uni/\xb5'.encode(osutils.get_user_encoding())
85
# locale cannot handle unicode
88
norm_file('uni/%C2%B5', u'uni/\xb5')
90
norm_file('uni/%25C2%25B5', u'uni/%C2%B5')
91
norm_file('uni/%20b', u'uni/ b')
92
# All the crazy characters get escaped in local paths => file:/// urls
93
# The ' ' character must not be at the end, because on win32
94
# it gets stripped off by ntpath.abspath
95
norm_file('%27%20%3B/%3F%3A%40%26%3D%2B%24%2C%23', "' ;/?:@&=+$,#")
97
def test_normalize_url_hybrid(self):
98
# Anything with a scheme:// should be treated as a hybrid url
99
# which changes what characters get escaped.
100
normalize_url = urlutils.normalize_url
102
eq = self.assertEqual
103
eq('file:///foo/', normalize_url(u'file:///foo/'))
104
eq('file:///foo/%20', normalize_url(u'file:///foo/ '))
105
eq('file:///foo/%20', normalize_url(u'file:///foo/%20'))
106
# Don't escape reserved characters
107
eq('file:///ab_c.d-e/%f:?g&h=i+j;k,L#M$',
108
normalize_url('file:///ab_c.d-e/%f:?g&h=i+j;k,L#M$'))
109
eq('http://ab_c.d-e/%f:?g&h=i+j;k,L#M$',
110
normalize_url('http://ab_c.d-e/%f:?g&h=i+j;k,L#M$'))
112
# Escape unicode characters, but not already escaped chars
113
eq('http://host/ab/%C2%B5/%C2%B5',
114
normalize_url(u'http://host/ab/%C2%B5/\xb5'))
116
# Unescape characters that don't need to be escaped
117
eq('http://host/~bob%2525-._',
118
normalize_url('http://host/%7Ebob%2525%2D%2E%5F'))
119
eq('http://host/~bob%2525-._',
120
normalize_url(u'http://host/%7Ebob%2525%2D%2E%5F'))
122
# Normalize verifies URLs when they are not unicode
123
# (indicating they did not come from the user)
124
self.assertRaises(InvalidURL, normalize_url, 'http://host/\xb5')
125
self.assertRaises(InvalidURL, normalize_url, 'http://host/ ')
127
def test_url_scheme_re(self):
128
# Test paths that may be URLs
129
def test_one(url, scheme_and_path):
130
"""Assert that _url_scheme_re correctly matches
132
:param scheme_and_path: The (scheme, path) that should be matched
133
can be None, to indicate it should not match
135
m = urlutils._url_scheme_re.match(url)
136
if scheme_and_path is None:
137
self.assertEqual(None, m)
139
self.assertEqual(scheme_and_path[0], m.group('scheme'))
140
self.assertEqual(scheme_and_path[1], m.group('path'))
143
test_one('/path', None)
144
test_one('C:/path', None)
145
test_one('../path/to/foo', None)
146
test_one(u'../path/to/fo\xe5', None)
149
test_one('http://host/path/', ('http', 'host/path/'))
150
test_one('sftp://host/path/to/foo', ('sftp', 'host/path/to/foo'))
151
test_one('file:///usr/bin', ('file', '/usr/bin'))
152
test_one('file:///C:/Windows', ('file', '/C:/Windows'))
153
test_one('file:///C|/Windows', ('file', '/C|/Windows'))
154
test_one(u'readonly+sftp://host/path/\xe5', ('readonly+sftp', u'host/path/\xe5'))
157
# Can't have slashes or colons in the scheme
158
test_one('/path/to/://foo', None)
159
test_one('scheme:stuff://foo', ('scheme', 'stuff://foo'))
160
# Must have more than one character for scheme
161
test_one('C://foo', None)
162
test_one('ab://foo', ('ab', 'foo'))
164
def test_dirname(self):
165
# Test bzrlib.urlutils.dirname()
166
dirname = urlutils.dirname
167
if sys.platform == 'win32':
168
self.assertRaises(InvalidURL, dirname, 'file:///path/to/foo')
169
self.assertEqual('file:///C|/', dirname('file:///C|/foo'))
170
self.assertEqual('file:///C|/', dirname('file:///C|/'))
172
self.assertEqual('file:///', dirname('file:///foo'))
173
self.assertEqual('file:///', dirname('file:///'))
175
self.assertEqual('http://host/path/to', dirname('http://host/path/to/foo'))
176
self.assertEqual('http://host/path/to', dirname('http://host/path/to/foo/'))
177
self.assertEqual('http://host/path/to/foo',
178
dirname('http://host/path/to/foo/', exclude_trailing_slash=False))
179
self.assertEqual('http://host/', dirname('http://host/path'))
180
self.assertEqual('http://host/', dirname('http://host/'))
181
self.assertEqual('http://host', dirname('http://host'))
182
self.assertEqual('http:///nohost', dirname('http:///nohost/path'))
184
self.assertEqual('random+scheme://user:pass@ahost:port/',
185
dirname('random+scheme://user:pass@ahost:port/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/'))
192
self.assertEqual('path/to', dirname('path/to/foo'))
193
self.assertEqual('path/to', dirname('path/to/foo/'))
194
self.assertEqual('path/to/foo',
195
dirname('path/to/foo/', exclude_trailing_slash=False))
196
self.assertEqual('path/..', dirname('path/../foo'))
197
self.assertEqual('../path', dirname('../path/foo'))
199
def test_is_url(self):
200
self.assertTrue(urlutils.is_url('http://foo/bar'))
201
self.assertTrue(urlutils.is_url('bzr+ssh://foo/bar'))
202
self.assertTrue(urlutils.is_url('lp:foo/bar'))
203
self.assertTrue(urlutils.is_url('file:///foo/bar'))
204
self.assertFalse(urlutils.is_url(''))
205
self.assertFalse(urlutils.is_url('foo'))
206
self.assertFalse(urlutils.is_url('foo/bar'))
207
self.assertFalse(urlutils.is_url('/foo'))
208
self.assertFalse(urlutils.is_url('/foo/bar'))
209
self.assertFalse(urlutils.is_url('C:/'))
210
self.assertFalse(urlutils.is_url('C:/foo'))
211
self.assertFalse(urlutils.is_url('C:/foo/bar'))
214
def test(expected, *args):
215
joined = urlutils.join(*args)
216
self.assertEqual(expected, joined)
218
# Test relative path joining
219
test('foo', 'foo') # relative fragment with nothing is preserved.
220
test('foo/bar', 'foo', 'bar')
221
test('http://foo/bar', 'http://foo', 'bar')
222
test('http://foo/bar', 'http://foo', '.', 'bar')
223
test('http://foo/baz', 'http://foo', 'bar', '../baz')
224
test('http://foo/bar/baz', 'http://foo', 'bar/baz')
225
test('http://foo/baz', 'http://foo', 'bar/../baz')
226
test('http://foo/baz', 'http://foo/bar/', '../baz')
227
test('lp:foo/bar', 'lp:foo', 'bar')
228
test('lp:foo/bar/baz', 'lp:foo', 'bar/baz')
231
test('http://foo', 'http://foo') # abs url with nothing is preserved.
232
test('http://bar', 'http://foo', 'http://bar')
233
test('sftp://bzr/foo', 'http://foo', 'bar', 'sftp://bzr/foo')
234
test('file:///bar', 'foo', 'file:///bar')
235
test('http://bar/', 'http://foo', 'http://bar/')
236
test('http://bar/a', 'http://foo', 'http://bar/a')
237
test('http://bar/a/', 'http://foo', 'http://bar/a/')
238
test('lp:bar', 'http://foo', 'lp:bar')
239
test('lp:bar', 'lp:foo', 'lp:bar')
240
test('file:///stuff', 'lp:foo', 'file:///stuff')
243
test('file:///foo', 'file:///', 'foo')
244
test('file:///bar/foo', 'file:///bar/', 'foo')
245
test('http://host/foo', 'http://host/', 'foo')
246
test('http://host/', 'http://host', '')
249
# Cannot go above root
250
# Implicitly at root:
251
self.assertRaises(InvalidURLJoin, urlutils.join,
252
'http://foo', '../baz')
253
self.assertRaises(InvalidURLJoin, urlutils.join,
255
# Joining from a path explicitly under the root.
256
self.assertRaises(InvalidURLJoin, urlutils.join,
257
'http://foo/a', '../../b')
259
def test_joinpath(self):
260
def test(expected, *args):
261
joined = urlutils.joinpath(*args)
262
self.assertEqual(expected, joined)
264
# Test a single element
267
# Test relative path joining
268
test('foo/bar', 'foo', 'bar')
269
test('foo/bar', 'foo', '.', 'bar')
270
test('foo/baz', 'foo', 'bar', '../baz')
271
test('foo/bar/baz', 'foo', 'bar/baz')
272
test('foo/baz', 'foo', 'bar/../baz')
274
# Test joining to an absolute path
276
test('/foo', '/foo', '.')
277
test('/foo/bar', '/foo', 'bar')
278
test('/', '/foo', '..')
280
# Test joining with an absolute path
281
test('/bar', 'foo', '/bar')
283
# Test joining to a path with a trailing slash
284
test('foo/bar', 'foo/', 'bar')
287
# Cannot go above root
288
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '../baz')
289
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..')
290
self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..')
292
def test_join_segment_parameters_raw(self):
293
join_segment_parameters_raw = urlutils.join_segment_parameters_raw
294
self.assertEquals("/somedir/path",
295
join_segment_parameters_raw("/somedir/path"))
296
self.assertEquals("/somedir/path,rawdata",
297
join_segment_parameters_raw("/somedir/path", "rawdata"))
298
self.assertRaises(InvalidURLJoin,
299
join_segment_parameters_raw, "/somedir/path",
300
"rawdata1,rawdata2,rawdata3")
301
self.assertEquals("/somedir/path,bla,bar",
302
join_segment_parameters_raw("/somedir/path", "bla", "bar"))
303
self.assertEquals("/somedir,exist=some/path,bla,bar",
304
join_segment_parameters_raw("/somedir,exist=some/path",
306
self.assertRaises(TypeError, join_segment_parameters_raw,
309
def test_join_segment_parameters(self):
310
join_segment_parameters = urlutils.join_segment_parameters
311
self.assertEquals("/somedir/path",
312
join_segment_parameters("/somedir/path", {}))
313
self.assertEquals("/somedir/path,key1=val1",
314
join_segment_parameters("/somedir/path", {"key1": "val1"}))
315
self.assertRaises(InvalidURLJoin,
316
join_segment_parameters, "/somedir/path",
317
{"branch": "brr,brr,brr"})
318
self.assertRaises(InvalidURLJoin,
319
join_segment_parameters, "/somedir/path", {"key1=val1": "val2"})
320
self.assertEquals("/somedir/path,key1=val1,key2=val2",
321
join_segment_parameters("/somedir/path", {
322
"key1": "val1", "key2": "val2"}))
323
self.assertEquals("/somedir/path,key1=val1,key2=val2",
324
join_segment_parameters("/somedir/path,key1=val1", {
326
self.assertEquals("/somedir/path,key1=val2",
327
join_segment_parameters("/somedir/path,key1=val1", {
329
self.assertEquals("/somedir,exist=some/path,key1=val1",
330
join_segment_parameters("/somedir,exist=some/path",
332
self.assertEquals("/,key1=val1,key2=val2",
333
join_segment_parameters("/,key1=val1", {"key2": "val2"}))
334
self.assertRaises(TypeError,
335
join_segment_parameters, "/,key1=val1", {"foo": 42})
337
def test_function_type(self):
338
if sys.platform == 'win32':
339
self.assertEqual(urlutils._win32_local_path_to_url, urlutils.local_path_to_url)
340
self.assertEqual(urlutils._win32_local_path_from_url, urlutils.local_path_from_url)
342
self.assertEqual(urlutils._posix_local_path_to_url, urlutils.local_path_to_url)
343
self.assertEqual(urlutils._posix_local_path_from_url, urlutils.local_path_from_url)
345
def test_posix_local_path_to_url(self):
346
to_url = urlutils._posix_local_path_to_url
347
self.assertEqual('file:///path/to/foo',
348
to_url('/path/to/foo'))
351
result = to_url(u'/path/to/r\xe4ksm\xf6rg\xe5s')
353
raise TestSkipped("local encoding cannot handle unicode")
355
self.assertEqual('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
356
self.assertFalse(isinstance(result, unicode))
358
def test_posix_local_path_from_url(self):
359
from_url = urlutils._posix_local_path_from_url
360
self.assertEqual('/path/to/foo',
361
from_url('file:///path/to/foo'))
362
self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
363
from_url('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
364
self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
365
from_url('file:///path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
366
self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
367
from_url('file://localhost/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
369
self.assertRaises(InvalidURL, from_url, '/path/to/foo')
371
InvalidURL, from_url,
372
'file://remotehost/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s')
374
def test_win32_local_path_to_url(self):
375
to_url = urlutils._win32_local_path_to_url
376
self.assertEqual('file:///C:/path/to/foo',
377
to_url('C:/path/to/foo'))
378
# BOGUS: on win32, ntpath.abspath will strip trailing
379
# whitespace, so this will always fail
380
# Though under linux, it fakes abspath support
381
# and thus will succeed
382
# self.assertEqual('file:///C:/path/to/foo%20',
383
# to_url('C:/path/to/foo '))
384
self.assertEqual('file:///C:/path/to/f%20oo',
385
to_url('C:/path/to/f oo'))
387
self.assertEqual('file:///', to_url('/'))
390
result = to_url(u'd:/path/to/r\xe4ksm\xf6rg\xe5s')
392
raise TestSkipped("local encoding cannot handle unicode")
394
self.assertEqual('file:///D:/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
395
self.assertFalse(isinstance(result, unicode))
397
def test_win32_unc_path_to_url(self):
398
to_url = urlutils._win32_local_path_to_url
399
self.assertEqual('file://HOST/path',
400
to_url(r'\\HOST\path'))
401
self.assertEqual('file://HOST/path',
402
to_url('//HOST/path'))
405
result = to_url(u'//HOST/path/to/r\xe4ksm\xf6rg\xe5s')
407
raise TestSkipped("local encoding cannot handle unicode")
409
self.assertEqual('file://HOST/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
410
self.assertFalse(isinstance(result, unicode))
412
def test_win32_local_path_from_url(self):
413
from_url = urlutils._win32_local_path_from_url
414
self.assertEqual('C:/path/to/foo',
415
from_url('file:///C|/path/to/foo'))
416
self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
417
from_url('file:///d|/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
418
self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
419
from_url('file:///d:/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
420
self.assertEqual('/', from_url('file:///'))
422
self.assertRaises(InvalidURL, from_url, 'file:///C:')
423
self.assertRaises(InvalidURL, from_url, 'file:///c')
424
self.assertRaises(InvalidURL, from_url, '/path/to/foo')
425
# Not a valid _win32 url, no drive letter
426
self.assertRaises(InvalidURL, from_url, 'file:///path/to/foo')
428
def test_win32_unc_path_from_url(self):
429
from_url = urlutils._win32_local_path_from_url
430
self.assertEqual('//HOST/path', from_url('file://HOST/path'))
431
# despite IE allows 2, 4, 5 and 6 slashes in URL to another machine
432
# we want to use only 2 slashes
433
# Firefox understand only 5 slashes in URL, but it's ugly
434
self.assertRaises(InvalidURL, from_url, 'file:////HOST/path')
435
self.assertRaises(InvalidURL, from_url, 'file://///HOST/path')
436
self.assertRaises(InvalidURL, from_url, 'file://////HOST/path')
437
# check for file://C:/ instead of file:///C:/
438
self.assertRaises(InvalidURL, from_url, 'file://C:/path')
440
def test_win32_extract_drive_letter(self):
441
extract = urlutils._win32_extract_drive_letter
442
self.assertEqual(('file:///C:', '/foo'), extract('file://', '/C:/foo'))
443
self.assertEqual(('file:///d|', '/path'), extract('file://', '/d|/path'))
444
self.assertRaises(InvalidURL, extract, 'file://', '/path')
446
def test_split(self):
447
# Test bzrlib.urlutils.split()
448
split = urlutils.split
449
if sys.platform == 'win32':
450
self.assertRaises(InvalidURL, split, 'file:///path/to/foo')
451
self.assertEqual(('file:///C|/', 'foo'), split('file:///C|/foo'))
452
self.assertEqual(('file:///C:/', ''), split('file:///C:/'))
454
self.assertEqual(('file:///', 'foo'), split('file:///foo'))
455
self.assertEqual(('file:///', ''), split('file:///'))
457
self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo'))
458
self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo/'))
459
self.assertEqual(('http://host/path/to/foo', ''),
460
split('http://host/path/to/foo/', exclude_trailing_slash=False))
461
self.assertEqual(('http://host/', 'path'), split('http://host/path'))
462
self.assertEqual(('http://host/', ''), split('http://host/'))
463
self.assertEqual(('http://host', ''), split('http://host'))
464
self.assertEqual(('http:///nohost', 'path'), split('http:///nohost/path'))
466
self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
467
split('random+scheme://user:pass@ahost:port/path'))
468
self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
469
split('random+scheme://user:pass@ahost:port/path/'))
470
self.assertEqual(('random+scheme://user:pass@ahost:port/', ''),
471
split('random+scheme://user:pass@ahost:port/'))
474
self.assertEqual(('path/to', 'foo'), split('path/to/foo'))
475
self.assertEqual(('path/to', 'foo'), split('path/to/foo/'))
476
self.assertEqual(('path/to/foo', ''),
477
split('path/to/foo/', exclude_trailing_slash=False))
478
self.assertEqual(('path/..', 'foo'), split('path/../foo'))
479
self.assertEqual(('../path', 'foo'), split('../path/foo'))
481
def test_split_segment_parameters_raw(self):
482
split_segment_parameters_raw = urlutils.split_segment_parameters_raw
483
self.assertEquals(("/some/path", []),
484
split_segment_parameters_raw("/some/path"))
485
self.assertEquals(("/some/path", ["tip"]),
486
split_segment_parameters_raw("/some/path,tip"))
487
self.assertEquals(("/some,dir/path", ["tip"]),
488
split_segment_parameters_raw("/some,dir/path,tip"))
489
self.assertEquals(("/somedir/path", ["heads%2Ftip"]),
490
split_segment_parameters_raw("/somedir/path,heads%2Ftip"))
491
self.assertEquals(("/somedir/path", ["heads%2Ftip", "bar"]),
492
split_segment_parameters_raw("/somedir/path,heads%2Ftip,bar"))
493
self.assertEquals(("/", ["key1=val1"]),
494
split_segment_parameters_raw(",key1=val1"))
495
self.assertEquals(("foo/", ["key1=val1"]),
496
split_segment_parameters_raw("foo/,key1=val1"))
497
self.assertEquals(("foo/base,la=bla/other/elements", []),
498
split_segment_parameters_raw("foo/base,la=bla/other/elements"))
499
self.assertEquals(("foo/base,la=bla/other/elements", ["a=b"]),
500
split_segment_parameters_raw("foo/base,la=bla/other/elements,a=b"))
502
def test_split_segment_parameters(self):
503
split_segment_parameters = urlutils.split_segment_parameters
504
self.assertEquals(("/some/path", {}),
505
split_segment_parameters("/some/path"))
506
self.assertEquals(("/some/path", {"branch": "tip"}),
507
split_segment_parameters("/some/path,branch=tip"))
508
self.assertEquals(("/some,dir/path", {"branch": "tip"}),
509
split_segment_parameters("/some,dir/path,branch=tip"))
510
self.assertEquals(("/somedir/path", {"ref": "heads%2Ftip"}),
511
split_segment_parameters("/somedir/path,ref=heads%2Ftip"))
512
self.assertEquals(("/somedir/path",
513
{"ref": "heads%2Ftip", "key1": "val1"}),
514
split_segment_parameters(
515
"/somedir/path,ref=heads%2Ftip,key1=val1"))
516
self.assertEquals(("/somedir/path", {"ref": "heads%2F=tip"}),
517
split_segment_parameters("/somedir/path,ref=heads%2F=tip"))
518
self.assertEquals(("/", {"key1": "val1"}),
519
split_segment_parameters(",key1=val1"))
520
self.assertEquals(("foo/", {"key1": "val1"}),
521
split_segment_parameters("foo/,key1=val1"))
522
self.assertEquals(("foo/base,key1=val1/other/elements", {}),
523
split_segment_parameters("foo/base,key1=val1/other/elements"))
524
self.assertEquals(("foo/base,key1=val1/other/elements",
525
{"key2": "val2"}), split_segment_parameters(
526
"foo/base,key1=val1/other/elements,key2=val2"))
528
def test_win32_strip_local_trailing_slash(self):
529
strip = urlutils._win32_strip_local_trailing_slash
530
self.assertEqual('file://', strip('file://'))
531
self.assertEqual('file:///', strip('file:///'))
532
self.assertEqual('file:///C', strip('file:///C'))
533
self.assertEqual('file:///C:', strip('file:///C:'))
534
self.assertEqual('file:///d|', strip('file:///d|'))
535
self.assertEqual('file:///C:/', strip('file:///C:/'))
536
self.assertEqual('file:///C:/a', strip('file:///C:/a/'))
538
def test_strip_trailing_slash(self):
539
sts = urlutils.strip_trailing_slash
540
if sys.platform == 'win32':
541
self.assertEqual('file:///C|/', sts('file:///C|/'))
542
self.assertEqual('file:///C:/foo', sts('file:///C:/foo'))
543
self.assertEqual('file:///C|/foo', sts('file:///C|/foo/'))
545
self.assertEqual('file:///', sts('file:///'))
546
self.assertEqual('file:///foo', sts('file:///foo'))
547
self.assertEqual('file:///foo', sts('file:///foo/'))
549
self.assertEqual('http://host/', sts('http://host/'))
550
self.assertEqual('http://host/foo', sts('http://host/foo'))
551
self.assertEqual('http://host/foo', sts('http://host/foo/'))
553
# No need to fail just because the slash is missing
554
self.assertEqual('http://host', sts('http://host'))
555
# TODO: jam 20060502 Should this raise InvalidURL?
556
self.assertEqual('file://', sts('file://'))
558
self.assertEqual('random+scheme://user:pass@ahost:port/path',
559
sts('random+scheme://user:pass@ahost:port/path'))
560
self.assertEqual('random+scheme://user:pass@ahost:port/path',
561
sts('random+scheme://user:pass@ahost:port/path/'))
562
self.assertEqual('random+scheme://user:pass@ahost:port/',
563
sts('random+scheme://user:pass@ahost:port/'))
565
# Make sure relative paths work too
566
self.assertEqual('path/to/foo', sts('path/to/foo'))
567
self.assertEqual('path/to/foo', sts('path/to/foo/'))
568
self.assertEqual('../to/foo', sts('../to/foo/'))
569
self.assertEqual('path/../foo', sts('path/../foo/'))
571
def test_unescape_for_display_utf8(self):
572
# Test that URLs are converted to nice unicode strings for display
573
def test(expected, url, encoding='utf-8'):
574
disp_url = urlutils.unescape_for_display(url, encoding=encoding)
575
self.assertIsInstance(disp_url, unicode)
576
self.assertEqual(expected, disp_url)
578
test('http://foo', 'http://foo')
579
if sys.platform == 'win32':
580
test('C:/foo/path', 'file:///C|/foo/path')
581
test('C:/foo/path', 'file:///C:/foo/path')
583
test('/foo/path', 'file:///foo/path')
585
test('http://foo/%2Fbaz', 'http://foo/%2Fbaz')
586
test(u'http://host/r\xe4ksm\xf6rg\xe5s',
587
'http://host/r%C3%A4ksm%C3%B6rg%C3%A5s')
589
# Make sure special escaped characters stay escaped
590
test(u'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23',
591
'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23')
593
# Can we handle sections that don't have utf-8 encoding?
594
test(u'http://host/%EE%EE%EE/r\xe4ksm\xf6rg\xe5s',
595
'http://host/%EE%EE%EE/r%C3%A4ksm%C3%B6rg%C3%A5s')
597
# Test encoding into output that can handle some characters
598
test(u'http://host/%EE%EE%EE/r\xe4ksm\xf6rg\xe5s',
599
'http://host/%EE%EE%EE/r%C3%A4ksm%C3%B6rg%C3%A5s',
600
encoding='iso-8859-1')
602
# This one can be encoded into utf8
603
test(u'http://host/\u062c\u0648\u062c\u0648',
604
'http://host/%d8%ac%d9%88%d8%ac%d9%88',
607
# This can't be put into 8859-1 and so stays as escapes
608
test(u'http://host/%d8%ac%d9%88%d8%ac%d9%88',
609
'http://host/%d8%ac%d9%88%d8%ac%d9%88',
610
encoding='iso-8859-1')
612
def test_escape(self):
613
self.assertEqual('%25', urlutils.escape('%'))
614
self.assertEqual('%C3%A5', urlutils.escape(u'\xe5'))
615
self.assertFalse(isinstance(urlutils.escape(u'\xe5'), unicode))
617
def test_escape_tildes(self):
618
self.assertEqual('~foo', urlutils.escape('~foo'))
620
def test_unescape(self):
621
self.assertEqual('%', urlutils.unescape('%25'))
622
self.assertEqual(u'\xe5', urlutils.unescape('%C3%A5'))
624
self.assertRaises(InvalidURL, urlutils.unescape, u'\xe5')
625
self.assertRaises(InvalidURL, urlutils.unescape, '\xe5')
626
self.assertRaises(InvalidURL, urlutils.unescape, '%E5')
628
def test_escape_unescape(self):
629
self.assertEqual(u'\xe5', urlutils.unescape(urlutils.escape(u'\xe5')))
630
self.assertEqual('%', urlutils.unescape(urlutils.escape('%')))
632
def test_relative_url(self):
633
def test(expected, base, other):
634
result = urlutils.relative_url(base, other)
635
self.assertEqual(expected, result)
637
test('a', 'http://host/', 'http://host/a')
638
test('http://entirely/different', 'sftp://host/branch',
639
'http://entirely/different')
640
test('../person/feature', 'http://host/branch/mainline',
641
'http://host/branch/person/feature')
642
test('..', 'http://host/branch', 'http://host/')
643
test('http://host2/branch', 'http://host1/branch', 'http://host2/branch')
644
test('.', 'http://host1/branch', 'http://host1/branch')
645
test('../../../branch/2b', 'file:///home/jelmer/foo/bar/2b',
646
'file:///home/jelmer/branch/2b')
647
test('../../branch/2b', 'sftp://host/home/jelmer/bar/2b',
648
'sftp://host/home/jelmer/branch/2b')
649
test('../../branch/feature/%2b', 'http://host/home/jelmer/bar/%2b',
650
'http://host/home/jelmer/branch/feature/%2b')
651
test('../../branch/feature/2b', 'http://host/home/jelmer/bar/2b/',
652
'http://host/home/jelmer/branch/feature/2b')
653
# relative_url should preserve a trailing slash
654
test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b/',
655
'http://host/home/jelmer/branch/feature/2b/')
656
test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b',
657
'http://host/home/jelmer/branch/feature/2b/')
659
# TODO: treat http://host as http://host/
660
# relative_url is typically called from a branch.base or
661
# transport.base which always ends with a /
662
#test('a', 'http://host', 'http://host/a')
663
test('http://host/a', 'http://host', 'http://host/a')
664
#test('.', 'http://host', 'http://host/')
665
test('http://host/', 'http://host', 'http://host/')
666
#test('.', 'http://host/', 'http://host')
667
test('http://host', 'http://host/', 'http://host')
669
# On Windows file:///C:/path/to and file:///D:/other/path
670
# should not use relative url over the non-existent '/' directory.
671
if sys.platform == 'win32':
673
test('../../other/path',
674
'file:///C:/path/to', 'file:///C:/other/path')
675
#~next two tests is failed, i.e. urlutils.relative_url expects
676
#~to see normalized file URLs?
677
#~test('../../other/path',
678
#~ 'file:///C:/path/to', 'file:///c:/other/path')
679
#~test('../../other/path',
680
#~ 'file:///C:/path/to', 'file:///C|/other/path')
682
# check UNC paths too
683
test('../../other/path',
684
'file://HOST/base/path/to', 'file://HOST/base/other/path')
685
# on different drives
686
test('file:///D:/other/path',
687
'file:///C:/path/to', 'file:///D:/other/path')
688
# TODO: strictly saying in UNC path //HOST/base is full analog
689
# of drive letter for hard disk, and this situation is also
690
# should be exception from rules. [bialix 20071221]
693
class TestCwdToURL(TestCaseInTempDir):
694
"""Test that local_path_to_url works base on the cwd"""
697
# This test will fail if getcwd is not ascii
701
url = urlutils.local_path_to_url('.')
702
self.assertEndsWith(url, '/mytest')
704
def test_non_ascii(self):
705
if win32utils.winver == 'Windows 98':
706
raise TestSkipped('Windows 98 cannot handle unicode filenames')
711
raise TestSkipped('cannot create unicode directory')
715
# On Mac OSX this directory is actually:
716
# u'/dode\u0301' => '/dode\xcc\x81
717
# but we should normalize it back to
718
# u'/dod\xe9' => '/dod\xc3\xa9'
719
url = urlutils.local_path_to_url('.')
720
self.assertEndsWith(url, '/dod%C3%A9')
723
class TestDeriveToLocation(TestCase):
724
"""Test that the mapping of FROM_LOCATION to TO_LOCATION works."""
726
def test_to_locations_derived_from_paths(self):
727
derive = urlutils.derive_to_location
728
self.assertEqual("bar", derive("bar"))
729
self.assertEqual("bar", derive("../bar"))
730
self.assertEqual("bar", derive("/foo/bar"))
731
self.assertEqual("bar", derive("c:/foo/bar"))
732
self.assertEqual("bar", derive("c:bar"))
734
def test_to_locations_derived_from_urls(self):
735
derive = urlutils.derive_to_location
736
self.assertEqual("bar", derive("http://foo/bar"))
737
self.assertEqual("bar", derive("bzr+ssh://foo/bar"))
738
self.assertEqual("foo-bar", derive("lp:foo-bar"))
741
class TestRebaseURL(TestCase):
742
"""Test the behavior of rebase_url."""
744
def test_non_relative(self):
745
result = urlutils.rebase_url('file://foo', 'file://foo',
747
self.assertEqual('file://foo', result)
748
result = urlutils.rebase_url('/foo', 'file://foo',
750
self.assertEqual('/foo', result)
752
def test_different_ports(self):
753
e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
754
'foo', 'http://bar:80', 'http://bar:81')
755
self.assertEqual(str(e), "URLs differ by more than path:"
756
" 'http://bar:80' and 'http://bar:81'")
758
def test_different_hosts(self):
759
e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
760
'foo', 'http://bar', 'http://baz')
761
self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
764
def test_different_protocol(self):
765
e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
766
'foo', 'http://bar', 'ftp://bar')
767
self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
770
def test_rebase_success(self):
771
self.assertEqual('../bar', urlutils.rebase_url('bar', 'http://baz/',
773
self.assertEqual('qux/bar', urlutils.rebase_url('bar',
774
'http://baz/qux', 'http://baz/'))
775
self.assertEqual('.', urlutils.rebase_url('foo',
776
'http://bar/', 'http://bar/foo/'))
777
self.assertEqual('qux/bar', urlutils.rebase_url('../bar',
778
'http://baz/qux/foo', 'http://baz/'))
780
def test_determine_relative_path(self):
781
self.assertEqual('../../baz/bar',
782
urlutils.determine_relative_path(
783
'/qux/quxx', '/baz/bar'))
784
self.assertEqual('..',
785
urlutils.determine_relative_path(
787
self.assertEqual('baz',
788
urlutils.determine_relative_path(
790
self.assertEqual('.', urlutils.determine_relative_path(
794
class TestParseURL(TestCase):
796
def test_parse_url(self):
797
self.assertEqual(urlutils.parse_url('http://example.com:80/one'),
798
('http', None, None, 'example.com', 80, '/one'))
799
self.assertEqual(urlutils.parse_url('http://[1:2:3::40]/one'),
800
('http', None, None, '1:2:3::40', None, '/one'))
801
self.assertEqual(urlutils.parse_url('http://[1:2:3::40]:80/one'),
802
('http', None, None, '1:2:3::40', 80, '/one'))