/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_urlutils.py

  • Committer: Jelmer Vernooij
  • Date: 2019-02-03 01:42:11 UTC
  • mto: This revision was merged to the branch mainline in revision 7267.
  • Revision ID: jelmer@jelmer.uk-20190203014211-poj1fv922sejfsb4
Don't require that short git shas have an even number of characters.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2012, 2015, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
19
19
import os
20
20
import sys
21
21
 
22
 
from bzrlib import osutils, urlutils, win32utils
23
 
from bzrlib.errors import InvalidURL, InvalidURLJoin, InvalidRebaseURLs
24
 
from bzrlib.tests import TestCaseInTempDir, TestCase, TestSkipped
 
22
from .. import osutils, urlutils
 
23
from ..errors import (
 
24
    PathNotChild,
 
25
    )
 
26
from ..sixish import (
 
27
    text_type,
 
28
    PY3,
 
29
    )
 
30
from . import features, TestCaseInTempDir, TestCase, TestSkipped
25
31
 
26
32
 
27
33
class TestUrlToPath(TestCase):
28
34
 
29
35
    def test_basename(self):
30
 
        # bzrlib.urlutils.basename
31
 
        # Test bzrlib.urlutils.split()
 
36
        # breezy.urlutils.basename
 
37
        # Test breezy.urlutils.split()
32
38
        basename = urlutils.basename
33
39
        if sys.platform == 'win32':
34
 
            self.assertRaises(InvalidURL, basename, 'file:///path/to/foo')
 
40
            self.assertRaises(urlutils.InvalidURL, basename,
 
41
                              'file:///path/to/foo')
35
42
            self.assertEqual('foo', basename('file:///C|/foo'))
36
43
            self.assertEqual('foo', basename('file:///C:/foo'))
37
44
            self.assertEqual('', basename('file:///C:/'))
41
48
 
42
49
        self.assertEqual('foo', basename('http://host/path/to/foo'))
43
50
        self.assertEqual('foo', basename('http://host/path/to/foo/'))
44
 
        self.assertEqual('',
45
 
            basename('http://host/path/to/foo/', exclude_trailing_slash=False))
 
51
        self.assertEqual(
 
52
            '', basename('http://host/path/to/foo/',
 
53
                         exclude_trailing_slash=False))
46
54
        self.assertEqual('path', basename('http://host/path'))
47
55
        self.assertEqual('', basename('http://host/'))
48
56
        self.assertEqual('', basename('http://host'))
49
57
        self.assertEqual('path', basename('http:///nohost/path'))
50
58
 
51
 
        self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path'))
52
 
        self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path/'))
 
59
        self.assertEqual('path', basename(
 
60
            'random+scheme://user:pass@ahost:port/path'))
 
61
        self.assertEqual('path', basename(
 
62
            'random+scheme://user:pass@ahost:port/path/'))
53
63
        self.assertEqual('', basename('random+scheme://user:pass@ahost:port/'))
54
64
 
55
65
        # relative paths
56
66
        self.assertEqual('foo', basename('path/to/foo'))
57
67
        self.assertEqual('foo', basename('path/to/foo/'))
58
68
        self.assertEqual('', basename('path/to/foo/',
59
 
            exclude_trailing_slash=False))
 
69
                                      exclude_trailing_slash=False))
60
70
        self.assertEqual('foo', basename('path/../foo'))
61
71
        self.assertEqual('foo', basename('../path/foo'))
62
72
 
115
125
 
116
126
        # Unescape characters that don't need to be escaped
117
127
        eq('http://host/~bob%2525-._',
118
 
                normalize_url('http://host/%7Ebob%2525%2D%2E%5F'))
 
128
           normalize_url('http://host/%7Ebob%2525%2D%2E%5F'))
119
129
        eq('http://host/~bob%2525-._',
120
 
                normalize_url(u'http://host/%7Ebob%2525%2D%2E%5F'))
 
130
           normalize_url(u'http://host/%7Ebob%2525%2D%2E%5F'))
121
131
 
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/ ')
 
132
        if not PY3:
 
133
            # On Python 2, normalize verifies URLs when they are not unicode
 
134
            # (indicating they did not come from the user)
 
135
            self.assertRaises(urlutils.InvalidURL, normalize_url,
 
136
                              b'http://host/\xb5')
 
137
            self.assertRaises(urlutils.InvalidURL,
 
138
                              normalize_url, b'http://host/ ')
126
139
 
127
140
    def test_url_scheme_re(self):
128
141
        # Test paths that may be URLs
151
164
        test_one('file:///usr/bin', ('file', '/usr/bin'))
152
165
        test_one('file:///C:/Windows', ('file', '/C:/Windows'))
153
166
        test_one('file:///C|/Windows', ('file', '/C|/Windows'))
154
 
        test_one(u'readonly+sftp://host/path/\xe5', ('readonly+sftp', u'host/path/\xe5'))
 
167
        test_one(u'readonly+sftp://host/path/\xe5',
 
168
                 ('readonly+sftp', u'host/path/\xe5'))
155
169
 
156
170
        # Weird stuff
157
171
        # Can't have slashes or colons in the scheme
158
172
        test_one('/path/to/://foo', None)
159
 
        test_one('path:path://foo', None)
 
173
        test_one('scheme:stuff://foo', ('scheme', 'stuff://foo'))
160
174
        # Must have more than one character for scheme
161
175
        test_one('C://foo', None)
162
176
        test_one('ab://foo', ('ab', 'foo'))
163
177
 
164
178
    def test_dirname(self):
165
 
        # Test bzrlib.urlutils.dirname()
 
179
        # Test breezy.urlutils.dirname()
166
180
        dirname = urlutils.dirname
167
181
        if sys.platform == 'win32':
168
 
            self.assertRaises(InvalidURL, dirname, 'file:///path/to/foo')
 
182
            self.assertRaises(urlutils.InvalidURL, dirname,
 
183
                              'file:///path/to/foo')
169
184
            self.assertEqual('file:///C|/', dirname('file:///C|/foo'))
170
185
            self.assertEqual('file:///C|/', dirname('file:///C|/'))
171
186
        else:
172
187
            self.assertEqual('file:///', dirname('file:///foo'))
173
188
            self.assertEqual('file:///', dirname('file:///'))
174
189
 
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/'))
 
190
        self.assertEqual('http://host/path/to',
 
191
                         dirname('http://host/path/to/foo'))
 
192
        self.assertEqual('http://host/path/to',
 
193
                         dirname('http://host/path/to/foo/'))
177
194
        self.assertEqual('http://host/path/to/foo',
178
 
            dirname('http://host/path/to/foo/', exclude_trailing_slash=False))
 
195
                         dirname('http://host/path/to/foo/',
 
196
                                 exclude_trailing_slash=False))
179
197
        self.assertEqual('http://host/', dirname('http://host/path'))
180
198
        self.assertEqual('http://host/', dirname('http://host/'))
181
199
        self.assertEqual('http://host', dirname('http://host'))
182
200
        self.assertEqual('http:///nohost', dirname('http:///nohost/path'))
183
201
 
184
202
        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/'))
 
203
                         dirname('random+scheme://user:pass@ahost:port/path'))
 
204
        self.assertEqual('random+scheme://user:pass@ahost:port/',
 
205
                         dirname('random+scheme://user:pass@ahost:port/path/'))
 
206
        self.assertEqual('random+scheme://user:pass@ahost:port/',
 
207
                         dirname('random+scheme://user:pass@ahost:port/'))
190
208
 
191
209
        # relative paths
192
210
        self.assertEqual('path/to', dirname('path/to/foo'))
193
211
        self.assertEqual('path/to', dirname('path/to/foo/'))
194
212
        self.assertEqual('path/to/foo',
195
 
            dirname('path/to/foo/', exclude_trailing_slash=False))
 
213
                         dirname('path/to/foo/', exclude_trailing_slash=False))
196
214
        self.assertEqual('path/..', dirname('path/../foo'))
197
215
        self.assertEqual('../path', dirname('../path/foo'))
198
216
 
 
217
    def test_is_url(self):
 
218
        self.assertTrue(urlutils.is_url('http://foo/bar'))
 
219
        self.assertTrue(urlutils.is_url('bzr+ssh://foo/bar'))
 
220
        self.assertTrue(urlutils.is_url('lp:foo/bar'))
 
221
        self.assertTrue(urlutils.is_url('file:///foo/bar'))
 
222
        self.assertFalse(urlutils.is_url(''))
 
223
        self.assertFalse(urlutils.is_url('foo'))
 
224
        self.assertFalse(urlutils.is_url('foo/bar'))
 
225
        self.assertFalse(urlutils.is_url('/foo'))
 
226
        self.assertFalse(urlutils.is_url('/foo/bar'))
 
227
        self.assertFalse(urlutils.is_url('C:/'))
 
228
        self.assertFalse(urlutils.is_url('C:/foo'))
 
229
        self.assertFalse(urlutils.is_url('C:/foo/bar'))
 
230
 
199
231
    def test_join(self):
200
232
        def test(expected, *args):
201
233
            joined = urlutils.join(*args)
202
234
            self.assertEqual(expected, joined)
203
235
 
204
236
        # Test relative path joining
205
 
        test('foo', 'foo') # relative fragment with nothing is preserved.
 
237
        test('foo', 'foo')  # relative fragment with nothing is preserved.
206
238
        test('foo/bar', 'foo', 'bar')
207
239
        test('http://foo/bar', 'http://foo', 'bar')
208
240
        test('http://foo/bar', 'http://foo', '.', 'bar')
210
242
        test('http://foo/bar/baz', 'http://foo', 'bar/baz')
211
243
        test('http://foo/baz', 'http://foo', 'bar/../baz')
212
244
        test('http://foo/baz', 'http://foo/bar/', '../baz')
 
245
        test('lp:foo/bar', 'lp:foo', 'bar')
 
246
        test('lp:foo/bar/baz', 'lp:foo', 'bar/baz')
213
247
 
214
248
        # Absolute paths
215
 
        test('http://foo', 'http://foo') # abs url with nothing is preserved.
 
249
        test('http://foo', 'http://foo')  # abs url with nothing is preserved.
216
250
        test('http://bar', 'http://foo', 'http://bar')
217
251
        test('sftp://bzr/foo', 'http://foo', 'bar', 'sftp://bzr/foo')
218
252
        test('file:///bar', 'foo', 'file:///bar')
219
253
        test('http://bar/', 'http://foo', 'http://bar/')
220
254
        test('http://bar/a', 'http://foo', 'http://bar/a')
221
255
        test('http://bar/a/', 'http://foo', 'http://bar/a/')
 
256
        test('lp:bar', 'http://foo', 'lp:bar')
 
257
        test('lp:bar', 'lp:foo', 'lp:bar')
 
258
        test('file:///stuff', 'lp:foo', 'file:///stuff')
222
259
 
223
260
        # From a base path
224
261
        test('file:///foo', 'file:///', 'foo')
229
266
        # Invalid joinings
230
267
        # Cannot go above root
231
268
        # Implicitly at root:
232
 
        self.assertRaises(InvalidURLJoin, urlutils.join,
233
 
                'http://foo', '../baz')
234
 
        self.assertRaises(InvalidURLJoin, urlutils.join,
235
 
                'http://foo', '/..')
 
269
        self.assertRaises(urlutils.InvalidURLJoin, urlutils.join,
 
270
                          'http://foo', '../baz')
 
271
        self.assertRaises(urlutils.InvalidURLJoin, urlutils.join,
 
272
                          'http://foo', '/..')
236
273
        # Joining from a path explicitly under the root.
237
 
        self.assertRaises(InvalidURLJoin, urlutils.join,
238
 
                'http://foo/a', '../../b')
 
274
        self.assertRaises(urlutils.InvalidURLJoin, urlutils.join,
 
275
                          'http://foo/a', '../../b')
239
276
 
240
277
    def test_joinpath(self):
241
278
        def test(expected, *args):
266
303
 
267
304
        # Invalid joinings
268
305
        # Cannot go above root
269
 
        self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '../baz')
270
 
        self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..')
271
 
        self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..')
 
306
        self.assertRaises(urlutils.InvalidURLJoin, urlutils.joinpath, '/',
 
307
                          '../baz')
 
308
        self.assertRaises(urlutils.InvalidURLJoin, urlutils.joinpath, '/',
 
309
                          '..')
 
310
        self.assertRaises(urlutils.InvalidURLJoin, urlutils.joinpath, '/',
 
311
                          '/..')
 
312
 
 
313
    def test_join_segment_parameters_raw(self):
 
314
        join_segment_parameters_raw = urlutils.join_segment_parameters_raw
 
315
        self.assertEqual("/somedir/path",
 
316
                         join_segment_parameters_raw("/somedir/path"))
 
317
        self.assertEqual("/somedir/path,rawdata",
 
318
                         join_segment_parameters_raw(
 
319
                             "/somedir/path", "rawdata"))
 
320
        self.assertRaises(urlutils.InvalidURLJoin,
 
321
                          join_segment_parameters_raw, "/somedir/path",
 
322
                          "rawdata1,rawdata2,rawdata3")
 
323
        self.assertEqual("/somedir/path,bla,bar",
 
324
                         join_segment_parameters_raw(
 
325
                             "/somedir/path", "bla", "bar"))
 
326
        self.assertEqual(
 
327
            "/somedir,exist=some/path,bla,bar",
 
328
            join_segment_parameters_raw("/somedir,exist=some/path",
 
329
                                        "bla", "bar"))
 
330
        self.assertRaises(TypeError, join_segment_parameters_raw,
 
331
                          "/somepath", 42)
 
332
 
 
333
    def test_join_segment_parameters(self):
 
334
        join_segment_parameters = urlutils.join_segment_parameters
 
335
        self.assertEqual("/somedir/path",
 
336
                         join_segment_parameters("/somedir/path", {}))
 
337
        self.assertEqual(
 
338
            "/somedir/path,key1=val1",
 
339
            join_segment_parameters("/somedir/path", {"key1": "val1"}))
 
340
        self.assertRaises(urlutils.InvalidURLJoin,
 
341
                          join_segment_parameters, "/somedir/path",
 
342
                          {"branch": "brr,brr,brr"})
 
343
        self.assertRaises(
 
344
            urlutils.InvalidURLJoin,
 
345
            join_segment_parameters, "/somedir/path", {"key1=val1": "val2"})
 
346
        self.assertEqual("/somedir/path,key1=val1,key2=val2",
 
347
                         join_segment_parameters("/somedir/path", {
 
348
                             "key1": "val1", "key2": "val2"}))
 
349
        self.assertEqual("/somedir/path,key1=val1,key2=val2",
 
350
                         join_segment_parameters("/somedir/path,key1=val1", {
 
351
                             "key2": "val2"}))
 
352
        self.assertEqual("/somedir/path,key1=val2",
 
353
                         join_segment_parameters("/somedir/path,key1=val1", {
 
354
                             "key1": "val2"}))
 
355
        self.assertEqual("/somedir,exist=some/path,key1=val1",
 
356
                         join_segment_parameters("/somedir,exist=some/path",
 
357
                                                 {"key1": "val1"}))
 
358
        self.assertEqual(
 
359
            "/,key1=val1,key2=val2",
 
360
            join_segment_parameters("/,key1=val1", {"key2": "val2"}))
 
361
        self.assertRaises(TypeError,
 
362
                          join_segment_parameters, "/,key1=val1", {"foo": 42})
272
363
 
273
364
    def test_function_type(self):
274
365
        if sys.platform == 'win32':
275
 
            self.assertEqual(urlutils._win32_local_path_to_url, urlutils.local_path_to_url)
276
 
            self.assertEqual(urlutils._win32_local_path_from_url, urlutils.local_path_from_url)
 
366
            self.assertEqual(urlutils._win32_local_path_to_url,
 
367
                             urlutils.local_path_to_url)
 
368
            self.assertEqual(urlutils._win32_local_path_from_url,
 
369
                             urlutils.local_path_from_url)
277
370
        else:
278
 
            self.assertEqual(urlutils._posix_local_path_to_url, urlutils.local_path_to_url)
279
 
            self.assertEqual(urlutils._posix_local_path_from_url, urlutils.local_path_from_url)
 
371
            self.assertEqual(urlutils._posix_local_path_to_url,
 
372
                             urlutils.local_path_to_url)
 
373
            self.assertEqual(urlutils._posix_local_path_from_url,
 
374
                             urlutils.local_path_from_url)
280
375
 
281
376
    def test_posix_local_path_to_url(self):
282
377
        to_url = urlutils._posix_local_path_to_url
283
378
        self.assertEqual('file:///path/to/foo',
284
 
            to_url('/path/to/foo'))
 
379
                         to_url('/path/to/foo'))
 
380
 
 
381
        self.assertEqual('file:///path/to/foo%2Cbar',
 
382
                         to_url('/path/to/foo,bar'))
285
383
 
286
384
        try:
287
385
            result = to_url(u'/path/to/r\xe4ksm\xf6rg\xe5s')
289
387
            raise TestSkipped("local encoding cannot handle unicode")
290
388
 
291
389
        self.assertEqual('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
292
 
        self.assertFalse(isinstance(result, unicode))
 
390
        self.assertTrue(isinstance(result, str))
293
391
 
294
392
    def test_posix_local_path_from_url(self):
295
393
        from_url = urlutils._posix_local_path_from_url
296
394
        self.assertEqual('/path/to/foo',
297
 
            from_url('file:///path/to/foo'))
298
 
        self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
299
 
            from_url('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
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',
 
395
                         from_url('file:///path/to/foo'))
 
396
        self.assertEqual('/path/to/foo',
 
397
                         from_url('file:///path/to/foo,branch=foo'))
 
398
        self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
 
399
                         from_url('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
 
400
        self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
 
401
                         from_url('file:///path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
 
402
        self.assertEqual(
 
403
            u'/path/to/r\xe4ksm\xf6rg\xe5s',
303
404
            from_url('file://localhost/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
304
405
 
305
 
        self.assertRaises(InvalidURL, from_url, '/path/to/foo')
 
406
        self.assertRaises(urlutils.InvalidURL, from_url, '/path/to/foo')
306
407
        self.assertRaises(
307
 
            InvalidURL, from_url,
 
408
            urlutils.InvalidURL, from_url,
308
409
            'file://remotehost/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s')
309
410
 
310
411
    def test_win32_local_path_to_url(self):
311
412
        to_url = urlutils._win32_local_path_to_url
312
413
        self.assertEqual('file:///C:/path/to/foo',
313
 
            to_url('C:/path/to/foo'))
 
414
                         to_url('C:/path/to/foo'))
314
415
        # BOGUS: on win32, ntpath.abspath will strip trailing
315
416
        #       whitespace, so this will always fail
316
417
        #       Though under linux, it fakes abspath support
318
419
        # self.assertEqual('file:///C:/path/to/foo%20',
319
420
        #     to_url('C:/path/to/foo '))
320
421
        self.assertEqual('file:///C:/path/to/f%20oo',
321
 
            to_url('C:/path/to/f oo'))
 
422
                         to_url('C:/path/to/f oo'))
322
423
 
323
424
        self.assertEqual('file:///', to_url('/'))
324
425
 
 
426
        self.assertEqual('file:///C:/path/to/foo%2Cbar',
 
427
                         to_url('C:/path/to/foo,bar'))
325
428
        try:
326
429
            result = to_url(u'd:/path/to/r\xe4ksm\xf6rg\xe5s')
327
430
        except UnicodeError:
328
431
            raise TestSkipped("local encoding cannot handle unicode")
329
432
 
330
 
        self.assertEqual('file:///D:/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
331
 
        self.assertFalse(isinstance(result, unicode))
 
433
        self.assertEqual(
 
434
            'file:///D:/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
 
435
        self.assertIsInstance(result, str)
332
436
 
333
437
    def test_win32_unc_path_to_url(self):
 
438
        self.requireFeature(features.win32_feature)
334
439
        to_url = urlutils._win32_local_path_to_url
335
440
        self.assertEqual('file://HOST/path',
336
 
            to_url(r'\\HOST\path'))
 
441
                         to_url(r'\\HOST\path'))
337
442
        self.assertEqual('file://HOST/path',
338
 
            to_url('//HOST/path'))
 
443
                         to_url('//HOST/path'))
339
444
 
340
445
        try:
341
446
            result = to_url(u'//HOST/path/to/r\xe4ksm\xf6rg\xe5s')
342
447
        except UnicodeError:
343
448
            raise TestSkipped("local encoding cannot handle unicode")
344
449
 
345
 
        self.assertEqual('file://HOST/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
346
 
        self.assertFalse(isinstance(result, unicode))
 
450
        self.assertEqual(
 
451
            'file://HOST/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
 
452
        self.assertFalse(isinstance(result, text_type))
347
453
 
348
454
    def test_win32_local_path_from_url(self):
349
455
        from_url = urlutils._win32_local_path_from_url
350
456
        self.assertEqual('C:/path/to/foo',
351
 
            from_url('file:///C|/path/to/foo'))
352
 
        self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
 
457
                         from_url('file:///C|/path/to/foo'))
 
458
        self.assertEqual(
 
459
            u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
353
460
            from_url('file:///d|/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
354
 
        self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
 
461
        self.assertEqual(
 
462
            u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
355
463
            from_url('file:///d:/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
356
464
        self.assertEqual('/', from_url('file:///'))
 
465
        self.assertEqual('C:/path/to/foo',
 
466
                         from_url('file:///C|/path/to/foo,branch=foo'))
357
467
 
358
 
        self.assertRaises(InvalidURL, from_url, '/path/to/foo')
 
468
        self.assertRaises(urlutils.InvalidURL, from_url, 'file:///C:')
 
469
        self.assertRaises(urlutils.InvalidURL, from_url, 'file:///c')
 
470
        self.assertRaises(urlutils.InvalidURL, from_url, '/path/to/foo')
359
471
        # Not a valid _win32 url, no drive letter
360
 
        self.assertRaises(InvalidURL, from_url, 'file:///path/to/foo')
 
472
        self.assertRaises(urlutils.InvalidURL, from_url, 'file:///path/to/foo')
361
473
 
362
474
    def test_win32_unc_path_from_url(self):
363
475
        from_url = urlutils._win32_local_path_from_url
364
476
        self.assertEqual('//HOST/path', from_url('file://HOST/path'))
 
477
        self.assertEqual('//HOST/path',
 
478
                         from_url('file://HOST/path,branch=foo'))
365
479
        # despite IE allows 2, 4, 5 and 6 slashes in URL to another machine
366
480
        # we want to use only 2 slashes
367
481
        # Firefox understand only 5 slashes in URL, but it's ugly
368
 
        self.assertRaises(InvalidURL, from_url, 'file:////HOST/path')
369
 
        self.assertRaises(InvalidURL, from_url, 'file://///HOST/path')
370
 
        self.assertRaises(InvalidURL, from_url, 'file://////HOST/path')
 
482
        self.assertRaises(urlutils.InvalidURL, from_url, 'file:////HOST/path')
 
483
        self.assertRaises(urlutils.InvalidURL, from_url, 'file://///HOST/path')
 
484
        self.assertRaises(urlutils.InvalidURL, from_url,
 
485
                          'file://////HOST/path')
371
486
        # check for file://C:/ instead of file:///C:/
372
 
        self.assertRaises(InvalidURL, from_url, 'file://C:/path')
 
487
        self.assertRaises(urlutils.InvalidURL, from_url, 'file://C:/path')
373
488
 
374
489
    def test_win32_extract_drive_letter(self):
375
490
        extract = urlutils._win32_extract_drive_letter
376
491
        self.assertEqual(('file:///C:', '/foo'), extract('file://', '/C:/foo'))
377
 
        self.assertEqual(('file:///d|', '/path'), extract('file://', '/d|/path'))
378
 
        self.assertRaises(InvalidURL, extract, 'file://', '/path')
 
492
        self.assertEqual(('file:///d|', '/path'),
 
493
                         extract('file://', '/d|/path'))
 
494
        self.assertRaises(urlutils.InvalidURL, extract, 'file://', '/path')
 
495
        # Root drives without slash treated as invalid, see bug #841322
 
496
        self.assertEqual(('file:///C:', '/'), extract('file://', '/C:/'))
 
497
        self.assertRaises(urlutils.InvalidURL, extract, 'file://', '/C:')
 
498
        # Invalid without drive separator or following forward slash
 
499
        self.assertRaises(urlutils.InvalidURL, extract, 'file://', '/C')
 
500
        self.assertRaises(urlutils.InvalidURL, extract, 'file://', '/C:ool')
379
501
 
380
502
    def test_split(self):
381
 
        # Test bzrlib.urlutils.split()
 
503
        # Test breezy.urlutils.split()
382
504
        split = urlutils.split
383
505
        if sys.platform == 'win32':
384
 
            self.assertRaises(InvalidURL, split, 'file:///path/to/foo')
 
506
            self.assertRaises(urlutils.InvalidURL, split,
 
507
                              'file:///path/to/foo')
385
508
            self.assertEqual(('file:///C|/', 'foo'), split('file:///C|/foo'))
386
509
            self.assertEqual(('file:///C:/', ''), split('file:///C:/'))
387
510
        else:
388
511
            self.assertEqual(('file:///', 'foo'), split('file:///foo'))
389
512
            self.assertEqual(('file:///', ''), split('file:///'))
390
513
 
391
 
        self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo'))
392
 
        self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo/'))
393
 
        self.assertEqual(('http://host/path/to/foo', ''),
 
514
        self.assertEqual(('http://host/path/to', 'foo'),
 
515
                         split('http://host/path/to/foo'))
 
516
        self.assertEqual(('http://host/path/to', 'foo'),
 
517
                         split('http://host/path/to/foo/'))
 
518
        self.assertEqual(
 
519
            ('http://host/path/to/foo', ''),
394
520
            split('http://host/path/to/foo/', exclude_trailing_slash=False))
395
521
        self.assertEqual(('http://host/', 'path'), split('http://host/path'))
396
522
        self.assertEqual(('http://host/', ''), split('http://host/'))
397
523
        self.assertEqual(('http://host', ''), split('http://host'))
398
 
        self.assertEqual(('http:///nohost', 'path'), split('http:///nohost/path'))
 
524
        self.assertEqual(('http:///nohost', 'path'),
 
525
                         split('http:///nohost/path'))
399
526
 
400
527
        self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
401
 
            split('random+scheme://user:pass@ahost:port/path'))
 
528
                         split('random+scheme://user:pass@ahost:port/path'))
402
529
        self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
403
 
            split('random+scheme://user:pass@ahost:port/path/'))
 
530
                         split('random+scheme://user:pass@ahost:port/path/'))
404
531
        self.assertEqual(('random+scheme://user:pass@ahost:port/', ''),
405
 
            split('random+scheme://user:pass@ahost:port/'))
 
532
                         split('random+scheme://user:pass@ahost:port/'))
406
533
 
407
534
        # relative paths
408
535
        self.assertEqual(('path/to', 'foo'), split('path/to/foo'))
409
536
        self.assertEqual(('path/to', 'foo'), split('path/to/foo/'))
410
537
        self.assertEqual(('path/to/foo', ''),
411
 
            split('path/to/foo/', exclude_trailing_slash=False))
 
538
                         split('path/to/foo/', exclude_trailing_slash=False))
412
539
        self.assertEqual(('path/..', 'foo'), split('path/../foo'))
413
540
        self.assertEqual(('../path', 'foo'), split('../path/foo'))
414
541
 
 
542
    def test_split_segment_parameters_raw(self):
 
543
        split_segment_parameters_raw = urlutils.split_segment_parameters_raw
 
544
        # Check relative references with absolute paths
 
545
        self.assertEqual(("/some/path", []),
 
546
                         split_segment_parameters_raw("/some/path"))
 
547
        self.assertEqual(("/some/path", ["tip"]),
 
548
                         split_segment_parameters_raw("/some/path,tip"))
 
549
        self.assertEqual(("/some,dir/path", ["tip"]),
 
550
                         split_segment_parameters_raw("/some,dir/path,tip"))
 
551
        self.assertEqual(
 
552
            ("/somedir/path", ["heads%2Ftip"]),
 
553
            split_segment_parameters_raw("/somedir/path,heads%2Ftip"))
 
554
        self.assertEqual(
 
555
            ("/somedir/path", ["heads%2Ftip", "bar"]),
 
556
            split_segment_parameters_raw("/somedir/path,heads%2Ftip,bar"))
 
557
        # Check relative references with relative paths
 
558
        self.assertEqual(("", ["key1=val1"]),
 
559
                         split_segment_parameters_raw(",key1=val1"))
 
560
        self.assertEqual(("foo/", ["key1=val1"]),
 
561
                         split_segment_parameters_raw("foo/,key1=val1"))
 
562
        self.assertEqual(("foo", ["key1=val1"]),
 
563
                         split_segment_parameters_raw("foo,key1=val1"))
 
564
        self.assertEqual(
 
565
            ("foo/base,la=bla/other/elements", []),
 
566
            split_segment_parameters_raw("foo/base,la=bla/other/elements"))
 
567
        self.assertEqual(
 
568
            ("foo/base,la=bla/other/elements", ["a=b"]),
 
569
            split_segment_parameters_raw("foo/base,la=bla/other/elements,a=b"))
 
570
        # TODO: Check full URLs as well as relative references
 
571
 
 
572
    def test_split_segment_parameters(self):
 
573
        split_segment_parameters = urlutils.split_segment_parameters
 
574
        # Check relative references with absolute paths
 
575
        self.assertEqual(("/some/path", {}),
 
576
                         split_segment_parameters("/some/path"))
 
577
        self.assertEqual(("/some/path", {"branch": "tip"}),
 
578
                         split_segment_parameters("/some/path,branch=tip"))
 
579
        self.assertEqual(("/some,dir/path", {"branch": "tip"}),
 
580
                         split_segment_parameters("/some,dir/path,branch=tip"))
 
581
        self.assertEqual(
 
582
            ("/somedir/path", {"ref": "heads%2Ftip"}),
 
583
            split_segment_parameters("/somedir/path,ref=heads%2Ftip"))
 
584
        self.assertEqual(("/somedir/path",
 
585
                          {"ref": "heads%2Ftip", "key1": "val1"}),
 
586
                         split_segment_parameters(
 
587
            "/somedir/path,ref=heads%2Ftip,key1=val1"))
 
588
        self.assertEqual(
 
589
            ("/somedir/path", {"ref": "heads%2F=tip"}),
 
590
            split_segment_parameters("/somedir/path,ref=heads%2F=tip"))
 
591
        # Check relative references with relative paths
 
592
        self.assertEqual(("", {"key1": "val1"}),
 
593
                         split_segment_parameters(",key1=val1"))
 
594
        self.assertEqual(("foo/", {"key1": "val1"}),
 
595
                         split_segment_parameters("foo/,key1=val1"))
 
596
        self.assertEqual(
 
597
            ("foo/base,key1=val1/other/elements", {}),
 
598
            split_segment_parameters("foo/base,key1=val1/other/elements"))
 
599
        self.assertEqual(("foo/base,key1=val1/other/elements",
 
600
                          {"key2": "val2"}), split_segment_parameters(
 
601
            "foo/base,key1=val1/other/elements,key2=val2"))
 
602
        # TODO: Check full URLs as well as relative references
 
603
 
415
604
    def test_win32_strip_local_trailing_slash(self):
416
605
        strip = urlutils._win32_strip_local_trailing_slash
417
606
        self.assertEqual('file://', strip('file://'))
443
632
        self.assertEqual('file://', sts('file://'))
444
633
 
445
634
        self.assertEqual('random+scheme://user:pass@ahost:port/path',
446
 
            sts('random+scheme://user:pass@ahost:port/path'))
 
635
                         sts('random+scheme://user:pass@ahost:port/path'))
447
636
        self.assertEqual('random+scheme://user:pass@ahost:port/path',
448
 
            sts('random+scheme://user:pass@ahost:port/path/'))
 
637
                         sts('random+scheme://user:pass@ahost:port/path/'))
449
638
        self.assertEqual('random+scheme://user:pass@ahost:port/',
450
 
            sts('random+scheme://user:pass@ahost:port/'))
 
639
                         sts('random+scheme://user:pass@ahost:port/'))
451
640
 
452
641
        # Make sure relative paths work too
453
642
        self.assertEqual('path/to/foo', sts('path/to/foo'))
459
648
        # Test that URLs are converted to nice unicode strings for display
460
649
        def test(expected, url, encoding='utf-8'):
461
650
            disp_url = urlutils.unescape_for_display(url, encoding=encoding)
462
 
            self.assertIsInstance(disp_url, unicode)
 
651
            self.assertIsInstance(disp_url, text_type)
463
652
            self.assertEqual(expected, disp_url)
464
653
 
465
654
        test('http://foo', 'http://foo')
499
688
    def test_escape(self):
500
689
        self.assertEqual('%25', urlutils.escape('%'))
501
690
        self.assertEqual('%C3%A5', urlutils.escape(u'\xe5'))
502
 
        self.assertFalse(isinstance(urlutils.escape(u'\xe5'), unicode))
 
691
        self.assertIsInstance(urlutils.escape(u'\xe5'), str)
503
692
 
504
693
    def test_escape_tildes(self):
505
694
        self.assertEqual('~foo', urlutils.escape('~foo'))
508
697
        self.assertEqual('%', urlutils.unescape('%25'))
509
698
        self.assertEqual(u'\xe5', urlutils.unescape('%C3%A5'))
510
699
 
511
 
        self.assertRaises(InvalidURL, urlutils.unescape, u'\xe5')
512
 
        self.assertRaises(InvalidURL, urlutils.unescape, '\xe5')
513
 
        self.assertRaises(InvalidURL, urlutils.unescape, '%E5')
 
700
        if not PY3:
 
701
            self.assertRaises(urlutils.InvalidURL, urlutils.unescape, u'\xe5')
 
702
        self.assertRaises((TypeError, urlutils.InvalidURL),
 
703
                          urlutils.unescape, b'\xe5')
 
704
        if not PY3:
 
705
            self.assertRaises(urlutils.InvalidURL, urlutils.unescape, '%E5')
 
706
        else:
 
707
            self.assertEqual('\xe5', urlutils.unescape('%C3%A5'))
514
708
 
515
709
    def test_escape_unescape(self):
516
710
        self.assertEqual(u'\xe5', urlutils.unescape(urlutils.escape(u'\xe5')))
523
717
 
524
718
        test('a', 'http://host/', 'http://host/a')
525
719
        test('http://entirely/different', 'sftp://host/branch',
526
 
                    'http://entirely/different')
 
720
             'http://entirely/different')
527
721
        test('../person/feature', 'http://host/branch/mainline',
528
 
                    'http://host/branch/person/feature')
 
722
             'http://host/branch/person/feature')
529
723
        test('..', 'http://host/branch', 'http://host/')
530
 
        test('http://host2/branch', 'http://host1/branch', 'http://host2/branch')
 
724
        test('http://host2/branch', 'http://host1/branch',
 
725
             'http://host2/branch')
531
726
        test('.', 'http://host1/branch', 'http://host1/branch')
532
727
        test('../../../branch/2b', 'file:///home/jelmer/foo/bar/2b',
533
 
                    'file:///home/jelmer/branch/2b')
 
728
             'file:///home/jelmer/branch/2b')
534
729
        test('../../branch/2b', 'sftp://host/home/jelmer/bar/2b',
535
 
                    'sftp://host/home/jelmer/branch/2b')
 
730
             'sftp://host/home/jelmer/branch/2b')
536
731
        test('../../branch/feature/%2b', 'http://host/home/jelmer/bar/%2b',
537
 
                    'http://host/home/jelmer/branch/feature/%2b')
 
732
             'http://host/home/jelmer/branch/feature/%2b')
538
733
        test('../../branch/feature/2b', 'http://host/home/jelmer/bar/2b/',
539
 
                    'http://host/home/jelmer/branch/feature/2b')
 
734
             'http://host/home/jelmer/branch/feature/2b')
540
735
        # relative_url should preserve a trailing slash
541
736
        test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b/',
542
 
                    'http://host/home/jelmer/branch/feature/2b/')
 
737
             'http://host/home/jelmer/branch/feature/2b/')
543
738
        test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b',
544
 
                    'http://host/home/jelmer/branch/feature/2b/')
 
739
             'http://host/home/jelmer/branch/feature/2b/')
545
740
 
546
741
        # TODO: treat http://host as http://host/
547
742
        #       relative_url is typically called from a branch.base or
548
743
        #       transport.base which always ends with a /
549
 
        #test('a', 'http://host', 'http://host/a')
 
744
        # test('a', 'http://host', 'http://host/a')
550
745
        test('http://host/a', 'http://host', 'http://host/a')
551
 
        #test('.', 'http://host', 'http://host/')
 
746
        # test('.', 'http://host', 'http://host/')
552
747
        test('http://host/', 'http://host', 'http://host/')
553
 
        #test('.', 'http://host/', 'http://host')
 
748
        # test('.', 'http://host/', 'http://host')
554
749
        test('http://host', 'http://host/', 'http://host')
555
750
 
556
751
        # On Windows file:///C:/path/to and file:///D:/other/path
558
753
        if sys.platform == 'win32':
559
754
            # on the same drive
560
755
            test('../../other/path',
561
 
                'file:///C:/path/to', 'file:///C:/other/path')
562
 
            #~next two tests is failed, i.e. urlutils.relative_url expects
563
 
            #~to see normalized file URLs?
564
 
            #~test('../../other/path',
565
 
            #~    'file:///C:/path/to', 'file:///c:/other/path')
566
 
            #~test('../../other/path',
567
 
            #~    'file:///C:/path/to', 'file:///C|/other/path')
 
756
                 'file:///C:/path/to', 'file:///C:/other/path')
 
757
            # ~next two tests is failed, i.e. urlutils.relative_url expects
 
758
            # ~to see normalized file URLs?
 
759
            # ~test('../../other/path',
 
760
            # ~    'file:///C:/path/to', 'file:///c:/other/path')
 
761
            # ~test('../../other/path',
 
762
            # ~    'file:///C:/path/to', 'file:///C|/other/path')
568
763
 
569
764
            # check UNC paths too
570
765
            test('../../other/path',
571
 
                'file://HOST/base/path/to', 'file://HOST/base/other/path')
 
766
                 'file://HOST/base/path/to', 'file://HOST/base/other/path')
572
767
            # on different drives
573
768
            test('file:///D:/other/path',
574
 
                'file:///C:/path/to', 'file:///D:/other/path')
 
769
                 'file:///C:/path/to', 'file:///D:/other/path')
575
770
            # TODO: strictly saying in UNC path //HOST/base is full analog
576
771
            # of drive letter for hard disk, and this situation is also
577
772
            # should be exception from rules. [bialix 20071221]
578
773
 
579
774
 
580
775
class TestCwdToURL(TestCaseInTempDir):
581
 
    """Test that local_path_to_url works base on the cwd"""
 
776
    """Test that local_path_to_url works based on the cwd"""
582
777
 
583
778
    def test_dot(self):
584
779
        # This test will fail if getcwd is not ascii
589
784
        self.assertEndsWith(url, '/mytest')
590
785
 
591
786
    def test_non_ascii(self):
592
 
        if win32utils.winver == 'Windows 98':
593
 
            raise TestSkipped('Windows 98 cannot handle unicode filenames')
594
 
 
595
787
        try:
596
788
            os.mkdir(u'dod\xe9')
597
789
        except UnicodeError:
637
829
        self.assertEqual('/foo', result)
638
830
 
639
831
    def test_different_ports(self):
640
 
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
 
832
        e = self.assertRaises(urlutils.InvalidRebaseURLs, urlutils.rebase_url,
641
833
                              'foo', 'http://bar:80', 'http://bar:81')
642
834
        self.assertEqual(str(e), "URLs differ by more than path:"
643
835
                         " 'http://bar:80' and 'http://bar:81'")
644
836
 
645
837
    def test_different_hosts(self):
646
 
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
 
838
        e = self.assertRaises(urlutils.InvalidRebaseURLs, urlutils.rebase_url,
647
839
                              'foo', 'http://bar', 'http://baz')
648
840
        self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
649
841
                         " and 'http://baz'")
650
842
 
651
843
    def test_different_protocol(self):
652
 
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
 
844
        e = self.assertRaises(urlutils.InvalidRebaseURLs, urlutils.rebase_url,
653
845
                              'foo', 'http://bar', 'ftp://bar')
654
846
        self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
655
847
                         " and 'ftp://bar'")
656
848
 
657
849
    def test_rebase_success(self):
658
850
        self.assertEqual('../bar', urlutils.rebase_url('bar', 'http://baz/',
659
 
                         'http://baz/qux'))
660
 
        self.assertEqual('qux/bar', urlutils.rebase_url('bar',
661
 
                         'http://baz/qux', 'http://baz/'))
662
 
        self.assertEqual('.', urlutils.rebase_url('foo',
663
 
                         'http://bar/', 'http://bar/foo/'))
664
 
        self.assertEqual('qux/bar', urlutils.rebase_url('../bar',
665
 
                         'http://baz/qux/foo', 'http://baz/'))
 
851
                                                       'http://baz/qux'))
 
852
        self.assertEqual(
 
853
            'qux/bar',
 
854
            urlutils.rebase_url('bar', 'http://baz/qux', 'http://baz/'))
 
855
        self.assertEqual(
 
856
            '.', urlutils.rebase_url('foo', 'http://bar/', 'http://bar/foo/'))
 
857
        self.assertEqual(
 
858
            'qux/bar',
 
859
            urlutils.rebase_url('../bar', 'http://baz/qux/foo', 'http://baz/'))
666
860
 
667
861
    def test_determine_relative_path(self):
668
862
        self.assertEqual('../../baz/bar',
669
863
                         urlutils.determine_relative_path(
670
 
                         '/qux/quxx', '/baz/bar'))
 
864
                             '/qux/quxx', '/baz/bar'))
671
865
        self.assertEqual('..',
672
866
                         urlutils.determine_relative_path(
673
 
                         '/bar/baz', '/bar'))
 
867
                             '/bar/baz', '/bar'))
674
868
        self.assertEqual('baz',
675
869
                         urlutils.determine_relative_path(
676
 
                         '/bar', '/bar/baz'))
 
870
                             '/bar', '/bar/baz'))
677
871
        self.assertEqual('.', urlutils.determine_relative_path(
678
872
                         '/bar', '/bar'))
679
873
 
680
874
 
681
875
class TestParseURL(TestCase):
682
876
 
683
 
    def test_parse_url(self):
684
 
        self.assertEqual(urlutils.parse_url('http://example.com:80/one'),
685
 
            ('http', None, None, 'example.com', 80, '/one'))
686
 
        self.assertEqual(urlutils.parse_url('http://[1:2:3::40]/one'),
687
 
                ('http', None, None, '1:2:3::40', None, '/one'))
688
 
        self.assertEqual(urlutils.parse_url('http://[1:2:3::40]:80/one'),
689
 
                ('http', None, None, '1:2:3::40', 80, '/one'))
 
877
    def test_parse_simple(self):
 
878
        parsed = urlutils.parse_url('http://example.com:80/one')
 
879
        self.assertEqual(('http', None, None, 'example.com', 80, '/one'),
 
880
                         parsed)
 
881
 
 
882
    def test_ipv6(self):
 
883
        parsed = urlutils.parse_url('http://[1:2:3::40]/one')
 
884
        self.assertEqual(('http', None, None, '1:2:3::40', None, '/one'),
 
885
                         parsed)
 
886
 
 
887
    def test_ipv6_port(self):
 
888
        parsed = urlutils.parse_url('http://[1:2:3::40]:80/one')
 
889
        self.assertEqual(('http', None, None, '1:2:3::40', 80, '/one'),
 
890
                         parsed)
 
891
 
 
892
 
 
893
class TestURL(TestCase):
 
894
 
 
895
    def test_parse_simple(self):
 
896
        parsed = urlutils.URL.from_string('http://example.com:80/one')
 
897
        self.assertEqual('http', parsed.scheme)
 
898
        self.assertIs(None, parsed.user)
 
899
        self.assertIs(None, parsed.password)
 
900
        self.assertEqual('example.com', parsed.host)
 
901
        self.assertEqual(80, parsed.port)
 
902
        self.assertEqual('/one', parsed.path)
 
903
 
 
904
    def test_ipv6(self):
 
905
        parsed = urlutils.URL.from_string('http://[1:2:3::40]/one')
 
906
        self.assertEqual('http', parsed.scheme)
 
907
        self.assertIs(None, parsed.port)
 
908
        self.assertIs(None, parsed.user)
 
909
        self.assertIs(None, parsed.password)
 
910
        self.assertEqual('1:2:3::40', parsed.host)
 
911
        self.assertEqual('/one', parsed.path)
 
912
 
 
913
    def test_ipv6_port(self):
 
914
        parsed = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
915
        self.assertEqual('http', parsed.scheme)
 
916
        self.assertEqual('1:2:3::40', parsed.host)
 
917
        self.assertIs(None, parsed.user)
 
918
        self.assertIs(None, parsed.password)
 
919
        self.assertEqual(80, parsed.port)
 
920
        self.assertEqual('/one', parsed.path)
 
921
 
 
922
    def test_quoted(self):
 
923
        parsed = urlutils.URL.from_string(
 
924
            'http://ro%62ey:h%40t@ex%41mple.com:2222/path')
 
925
        self.assertEqual(parsed.quoted_host, 'ex%41mple.com')
 
926
        self.assertEqual(parsed.host, 'exAmple.com')
 
927
        self.assertEqual(parsed.port, 2222)
 
928
        self.assertEqual(parsed.quoted_user, 'ro%62ey')
 
929
        self.assertEqual(parsed.user, 'robey')
 
930
        self.assertEqual(parsed.quoted_password, 'h%40t')
 
931
        self.assertEqual(parsed.password, 'h@t')
 
932
        self.assertEqual(parsed.path, '/path')
 
933
 
 
934
    def test_eq(self):
 
935
        parsed1 = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
936
        parsed2 = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
937
        self.assertEqual(parsed1, parsed2)
 
938
        self.assertEqual(parsed1, parsed1)
 
939
        parsed2.path = '/two'
 
940
        self.assertNotEqual(parsed1, parsed2)
 
941
 
 
942
    def test_repr(self):
 
943
        parsed = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
944
        self.assertEqual(
 
945
            "<URL('http', None, None, '1:2:3::40', 80, '/one')>",
 
946
            repr(parsed))
 
947
 
 
948
    def test_str(self):
 
949
        parsed = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
950
        self.assertEqual('http://[1:2:3::40]:80/one', str(parsed))
 
951
 
 
952
    def test__combine_paths(self):
 
953
        combine = urlutils.URL._combine_paths
 
954
        self.assertEqual('/home/sarah/project/foo',
 
955
                         combine('/home/sarah', 'project/foo'))
 
956
        self.assertEqual('/etc',
 
957
                         combine('/home/sarah', '../../etc'))
 
958
        self.assertEqual('/etc',
 
959
                         combine('/home/sarah', '../../../etc'))
 
960
        self.assertEqual('/etc',
 
961
                         combine('/home/sarah', '/etc'))
 
962
 
 
963
    def test_clone(self):
 
964
        url = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
965
        url1 = url.clone("two")
 
966
        self.assertEqual("/one/two", url1.path)
 
967
        url2 = url.clone("/two")
 
968
        self.assertEqual("/two", url2.path)
 
969
        url3 = url.clone()
 
970
        self.assertIsNot(url, url3)
 
971
        self.assertEqual(url, url3)
 
972
 
 
973
    def test_parse_empty_port(self):
 
974
        parsed = urlutils.URL.from_string('http://example.com:/one')
 
975
        self.assertEqual('http', parsed.scheme)
 
976
        self.assertIs(None, parsed.user)
 
977
        self.assertIs(None, parsed.password)
 
978
        self.assertEqual('example.com', parsed.host)
 
979
        self.assertIs(None, parsed.port)
 
980
        self.assertEqual('/one', parsed.path)
 
981
 
 
982
 
 
983
class TestFileRelpath(TestCase):
 
984
 
 
985
    # GZ 2011-11-18: A way to override all path handling functions to one
 
986
    #                platform or another for testing would be nice.
 
987
 
 
988
    def _with_posix_paths(self):
 
989
        self.overrideAttr(urlutils, "local_path_from_url",
 
990
                          urlutils._posix_local_path_from_url)
 
991
        self.overrideAttr(urlutils, "MIN_ABS_FILEURL_LENGTH", len("file:///"))
 
992
        self.overrideAttr(osutils, "normpath", osutils._posix_normpath)
 
993
        self.overrideAttr(osutils, "abspath", osutils._posix_abspath)
 
994
        self.overrideAttr(osutils, "normpath", osutils._posix_normpath)
 
995
        self.overrideAttr(osutils, "pathjoin", osutils.posixpath.join)
 
996
        self.overrideAttr(osutils, "split", osutils.posixpath.split)
 
997
        self.overrideAttr(osutils, "MIN_ABS_PATHLENGTH", 1)
 
998
 
 
999
    def _with_win32_paths(self):
 
1000
        self.overrideAttr(urlutils, "local_path_from_url",
 
1001
                          urlutils._win32_local_path_from_url)
 
1002
        self.overrideAttr(urlutils, "MIN_ABS_FILEURL_LENGTH",
 
1003
                          urlutils.WIN32_MIN_ABS_FILEURL_LENGTH)
 
1004
        self.overrideAttr(osutils, "abspath", osutils._win32_abspath)
 
1005
        self.overrideAttr(osutils, "normpath", osutils._win32_normpath)
 
1006
        self.overrideAttr(osutils, "pathjoin", osutils._win32_pathjoin)
 
1007
        self.overrideAttr(osutils, "split", osutils.ntpath.split)
 
1008
        self.overrideAttr(osutils, "MIN_ABS_PATHLENGTH", 3)
 
1009
 
 
1010
    def test_same_url_posix(self):
 
1011
        self._with_posix_paths()
 
1012
        self.assertEqual("",
 
1013
                         urlutils.file_relpath("file:///a", "file:///a"))
 
1014
        self.assertEqual("",
 
1015
                         urlutils.file_relpath("file:///a", "file:///a/"))
 
1016
        self.assertEqual("",
 
1017
                         urlutils.file_relpath("file:///a/", "file:///a"))
 
1018
 
 
1019
    def test_same_url_win32(self):
 
1020
        self._with_win32_paths()
 
1021
        self.assertEqual("",
 
1022
                         urlutils.file_relpath("file:///A:/", "file:///A:/"))
 
1023
        self.assertEqual("",
 
1024
                         urlutils.file_relpath("file:///A|/", "file:///A:/"))
 
1025
        self.assertEqual(
 
1026
            "", urlutils.file_relpath("file:///A:/b/", "file:///A:/b/"))
 
1027
        self.assertEqual(
 
1028
            "", urlutils.file_relpath("file:///A:/b", "file:///A:/b/"))
 
1029
        self.assertEqual(
 
1030
            "", urlutils.file_relpath("file:///A:/b/", "file:///A:/b"))
 
1031
 
 
1032
    def test_child_posix(self):
 
1033
        self._with_posix_paths()
 
1034
        self.assertEqual(
 
1035
            "b", urlutils.file_relpath("file:///a", "file:///a/b"))
 
1036
        self.assertEqual(
 
1037
            "b", urlutils.file_relpath("file:///a/", "file:///a/b"))
 
1038
        self.assertEqual(
 
1039
            "b/c", urlutils.file_relpath("file:///a", "file:///a/b/c"))
 
1040
 
 
1041
    def test_child_win32(self):
 
1042
        self._with_win32_paths()
 
1043
        self.assertEqual(
 
1044
            "b", urlutils.file_relpath("file:///A:/", "file:///A:/b"))
 
1045
        self.assertEqual(
 
1046
            "b", urlutils.file_relpath("file:///A|/", "file:///A:/b"))
 
1047
        self.assertEqual(
 
1048
            "c", urlutils.file_relpath("file:///A:/b", "file:///A:/b/c"))
 
1049
        self.assertEqual(
 
1050
            "c", urlutils.file_relpath("file:///A:/b/", "file:///A:/b/c"))
 
1051
        self.assertEqual(
 
1052
            "c/d", urlutils.file_relpath("file:///A:/b", "file:///A:/b/c/d"))
 
1053
 
 
1054
    def test_sibling_posix(self):
 
1055
        self._with_posix_paths()
 
1056
        self.assertRaises(
 
1057
            PathNotChild,
 
1058
            urlutils.file_relpath, "file:///a/b", "file:///a/c")
 
1059
        self.assertRaises(
 
1060
            PathNotChild,
 
1061
            urlutils.file_relpath, "file:///a/b/", "file:///a/c")
 
1062
        self.assertRaises(
 
1063
            PathNotChild,
 
1064
            urlutils.file_relpath, "file:///a/b/", "file:///a/c/")
 
1065
 
 
1066
    def test_sibling_win32(self):
 
1067
        self._with_win32_paths()
 
1068
        self.assertRaises(
 
1069
            PathNotChild,
 
1070
            urlutils.file_relpath, "file:///A:/b", "file:///A:/c")
 
1071
        self.assertRaises(
 
1072
            PathNotChild,
 
1073
            urlutils.file_relpath, "file:///A:/b/", "file:///A:/c")
 
1074
        self.assertRaises(
 
1075
            PathNotChild,
 
1076
            urlutils.file_relpath, "file:///A:/b/", "file:///A:/c/")
 
1077
 
 
1078
    def test_parent_posix(self):
 
1079
        self._with_posix_paths()
 
1080
        self.assertRaises(PathNotChild,
 
1081
                          urlutils.file_relpath, "file:///a/b", "file:///a")
 
1082
        self.assertRaises(PathNotChild,
 
1083
                          urlutils.file_relpath, "file:///a/b", "file:///a/")
 
1084
 
 
1085
    def test_parent_win32(self):
 
1086
        self._with_win32_paths()
 
1087
        self.assertRaises(
 
1088
            PathNotChild,
 
1089
            urlutils.file_relpath, "file:///A:/b", "file:///A:/")
 
1090
        self.assertRaises(
 
1091
            PathNotChild,
 
1092
            urlutils.file_relpath, "file:///A:/b/c", "file:///A:/b")
 
1093
 
 
1094
 
 
1095
class QuoteTests(TestCase):
 
1096
 
 
1097
    def test_quote(self):
 
1098
        self.assertEqual('abc%20def', urlutils.quote('abc def'))
 
1099
        self.assertEqual('abc%2Fdef', urlutils.quote('abc/def', safe=''))
 
1100
        self.assertEqual('abc/def', urlutils.quote('abc/def', safe='/'))
 
1101
 
 
1102
    def test_quote_tildes(self):
 
1103
        # Whether ~ is quoted by default depends on the python version
 
1104
        if sys.version_info[:2] >= (3, 7):
 
1105
            # https://docs.python.org/3/whatsnew/3.7.html#urllib-parse
 
1106
            self.assertEqual('~foo', urlutils.quote('~foo'))
 
1107
        else:
 
1108
            self.assertEqual('%7Efoo', urlutils.quote('~foo'))
 
1109
        self.assertEqual('~foo', urlutils.quote('~foo', safe='/~'))
 
1110
 
 
1111
    def test_unquote(self):
 
1112
        self.assertEqual('%', urlutils.unquote('%25'))
 
1113
        if PY3:
 
1114
            self.assertEqual('\xe5', urlutils.unquote('%C3%A5'))
 
1115
        else:
 
1116
            self.assertEqual('\xc3\xa5', urlutils.unquote('%C3%A5'))
 
1117
        self.assertEqual(u"\xe5", urlutils.unquote(u'\xe5'))
 
1118
 
 
1119
    def test_unquote_to_bytes(self):
 
1120
        self.assertEqual(b'%', urlutils.unquote_to_bytes('%25'))
 
1121
        self.assertEqual(b'\xc3\xa5', urlutils.unquote_to_bytes('%C3%A5'))