/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-09-01 15:33:59 UTC
  • mto: This revision was merged to the branch mainline in revision 7404.
  • Revision ID: jelmer@jelmer.uk-20190901153359-9gl0ai0x5wuiv444
Rename init-repo to init-shared-repo.

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
        self.assertRaises(
 
603
            urlutils.InvalidURL, split_segment_parameters,
 
604
            "foo/base,key1")
 
605
        # TODO: Check full URLs as well as relative references
 
606
 
415
607
    def test_win32_strip_local_trailing_slash(self):
416
608
        strip = urlutils._win32_strip_local_trailing_slash
417
609
        self.assertEqual('file://', strip('file://'))
443
635
        self.assertEqual('file://', sts('file://'))
444
636
 
445
637
        self.assertEqual('random+scheme://user:pass@ahost:port/path',
446
 
            sts('random+scheme://user:pass@ahost:port/path'))
 
638
                         sts('random+scheme://user:pass@ahost:port/path'))
447
639
        self.assertEqual('random+scheme://user:pass@ahost:port/path',
448
 
            sts('random+scheme://user:pass@ahost:port/path/'))
 
640
                         sts('random+scheme://user:pass@ahost:port/path/'))
449
641
        self.assertEqual('random+scheme://user:pass@ahost:port/',
450
 
            sts('random+scheme://user:pass@ahost:port/'))
 
642
                         sts('random+scheme://user:pass@ahost:port/'))
451
643
 
452
644
        # Make sure relative paths work too
453
645
        self.assertEqual('path/to/foo', sts('path/to/foo'))
459
651
        # Test that URLs are converted to nice unicode strings for display
460
652
        def test(expected, url, encoding='utf-8'):
461
653
            disp_url = urlutils.unescape_for_display(url, encoding=encoding)
462
 
            self.assertIsInstance(disp_url, unicode)
 
654
            self.assertIsInstance(disp_url, text_type)
463
655
            self.assertEqual(expected, disp_url)
464
656
 
465
657
        test('http://foo', 'http://foo')
499
691
    def test_escape(self):
500
692
        self.assertEqual('%25', urlutils.escape('%'))
501
693
        self.assertEqual('%C3%A5', urlutils.escape(u'\xe5'))
502
 
        self.assertFalse(isinstance(urlutils.escape(u'\xe5'), unicode))
 
694
        self.assertIsInstance(urlutils.escape(u'\xe5'), str)
503
695
 
504
696
    def test_escape_tildes(self):
505
697
        self.assertEqual('~foo', urlutils.escape('~foo'))
508
700
        self.assertEqual('%', urlutils.unescape('%25'))
509
701
        self.assertEqual(u'\xe5', urlutils.unescape('%C3%A5'))
510
702
 
511
 
        self.assertRaises(InvalidURL, urlutils.unescape, u'\xe5')
512
 
        self.assertRaises(InvalidURL, urlutils.unescape, '\xe5')
513
 
        self.assertRaises(InvalidURL, urlutils.unescape, '%E5')
 
703
        if not PY3:
 
704
            self.assertRaises(urlutils.InvalidURL, urlutils.unescape, u'\xe5')
 
705
        self.assertRaises((TypeError, urlutils.InvalidURL),
 
706
                          urlutils.unescape, b'\xe5')
 
707
        if not PY3:
 
708
            self.assertRaises(urlutils.InvalidURL, urlutils.unescape, '%E5')
 
709
        else:
 
710
            self.assertEqual('\xe5', urlutils.unescape('%C3%A5'))
514
711
 
515
712
    def test_escape_unescape(self):
516
713
        self.assertEqual(u'\xe5', urlutils.unescape(urlutils.escape(u'\xe5')))
523
720
 
524
721
        test('a', 'http://host/', 'http://host/a')
525
722
        test('http://entirely/different', 'sftp://host/branch',
526
 
                    'http://entirely/different')
 
723
             'http://entirely/different')
527
724
        test('../person/feature', 'http://host/branch/mainline',
528
 
                    'http://host/branch/person/feature')
 
725
             'http://host/branch/person/feature')
529
726
        test('..', 'http://host/branch', 'http://host/')
530
 
        test('http://host2/branch', 'http://host1/branch', 'http://host2/branch')
 
727
        test('http://host2/branch', 'http://host1/branch',
 
728
             'http://host2/branch')
531
729
        test('.', 'http://host1/branch', 'http://host1/branch')
532
730
        test('../../../branch/2b', 'file:///home/jelmer/foo/bar/2b',
533
 
                    'file:///home/jelmer/branch/2b')
 
731
             'file:///home/jelmer/branch/2b')
534
732
        test('../../branch/2b', 'sftp://host/home/jelmer/bar/2b',
535
 
                    'sftp://host/home/jelmer/branch/2b')
 
733
             'sftp://host/home/jelmer/branch/2b')
536
734
        test('../../branch/feature/%2b', 'http://host/home/jelmer/bar/%2b',
537
 
                    'http://host/home/jelmer/branch/feature/%2b')
 
735
             'http://host/home/jelmer/branch/feature/%2b')
538
736
        test('../../branch/feature/2b', 'http://host/home/jelmer/bar/2b/',
539
 
                    'http://host/home/jelmer/branch/feature/2b')
 
737
             'http://host/home/jelmer/branch/feature/2b')
540
738
        # relative_url should preserve a trailing slash
541
739
        test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b/',
542
 
                    'http://host/home/jelmer/branch/feature/2b/')
 
740
             'http://host/home/jelmer/branch/feature/2b/')
543
741
        test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b',
544
 
                    'http://host/home/jelmer/branch/feature/2b/')
 
742
             'http://host/home/jelmer/branch/feature/2b/')
545
743
 
546
744
        # TODO: treat http://host as http://host/
547
745
        #       relative_url is typically called from a branch.base or
548
746
        #       transport.base which always ends with a /
549
 
        #test('a', 'http://host', 'http://host/a')
 
747
        # test('a', 'http://host', 'http://host/a')
550
748
        test('http://host/a', 'http://host', 'http://host/a')
551
 
        #test('.', 'http://host', 'http://host/')
 
749
        # test('.', 'http://host', 'http://host/')
552
750
        test('http://host/', 'http://host', 'http://host/')
553
 
        #test('.', 'http://host/', 'http://host')
 
751
        # test('.', 'http://host/', 'http://host')
554
752
        test('http://host', 'http://host/', 'http://host')
555
753
 
556
754
        # On Windows file:///C:/path/to and file:///D:/other/path
558
756
        if sys.platform == 'win32':
559
757
            # on the same drive
560
758
            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')
 
759
                 'file:///C:/path/to', 'file:///C:/other/path')
 
760
            # ~next two tests is failed, i.e. urlutils.relative_url expects
 
761
            # ~to see normalized file URLs?
 
762
            # ~test('../../other/path',
 
763
            # ~    'file:///C:/path/to', 'file:///c:/other/path')
 
764
            # ~test('../../other/path',
 
765
            # ~    'file:///C:/path/to', 'file:///C|/other/path')
568
766
 
569
767
            # check UNC paths too
570
768
            test('../../other/path',
571
 
                'file://HOST/base/path/to', 'file://HOST/base/other/path')
 
769
                 'file://HOST/base/path/to', 'file://HOST/base/other/path')
572
770
            # on different drives
573
771
            test('file:///D:/other/path',
574
 
                'file:///C:/path/to', 'file:///D:/other/path')
 
772
                 'file:///C:/path/to', 'file:///D:/other/path')
575
773
            # TODO: strictly saying in UNC path //HOST/base is full analog
576
774
            # of drive letter for hard disk, and this situation is also
577
775
            # should be exception from rules. [bialix 20071221]
578
776
 
579
777
 
580
778
class TestCwdToURL(TestCaseInTempDir):
581
 
    """Test that local_path_to_url works base on the cwd"""
 
779
    """Test that local_path_to_url works based on the cwd"""
582
780
 
583
781
    def test_dot(self):
584
782
        # This test will fail if getcwd is not ascii
589
787
        self.assertEndsWith(url, '/mytest')
590
788
 
591
789
    def test_non_ascii(self):
592
 
        if win32utils.winver == 'Windows 98':
593
 
            raise TestSkipped('Windows 98 cannot handle unicode filenames')
594
 
 
595
790
        try:
596
791
            os.mkdir(u'dod\xe9')
597
792
        except UnicodeError:
637
832
        self.assertEqual('/foo', result)
638
833
 
639
834
    def test_different_ports(self):
640
 
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
 
835
        e = self.assertRaises(urlutils.InvalidRebaseURLs, urlutils.rebase_url,
641
836
                              'foo', 'http://bar:80', 'http://bar:81')
642
837
        self.assertEqual(str(e), "URLs differ by more than path:"
643
838
                         " 'http://bar:80' and 'http://bar:81'")
644
839
 
645
840
    def test_different_hosts(self):
646
 
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
 
841
        e = self.assertRaises(urlutils.InvalidRebaseURLs, urlutils.rebase_url,
647
842
                              'foo', 'http://bar', 'http://baz')
648
843
        self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
649
844
                         " and 'http://baz'")
650
845
 
651
846
    def test_different_protocol(self):
652
 
        e = self.assertRaises(InvalidRebaseURLs, urlutils.rebase_url,
 
847
        e = self.assertRaises(urlutils.InvalidRebaseURLs, urlutils.rebase_url,
653
848
                              'foo', 'http://bar', 'ftp://bar')
654
849
        self.assertEqual(str(e), "URLs differ by more than path: 'http://bar'"
655
850
                         " and 'ftp://bar'")
656
851
 
657
852
    def test_rebase_success(self):
658
853
        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/'))
 
854
                                                       'http://baz/qux'))
 
855
        self.assertEqual(
 
856
            'qux/bar',
 
857
            urlutils.rebase_url('bar', 'http://baz/qux', 'http://baz/'))
 
858
        self.assertEqual(
 
859
            '.', urlutils.rebase_url('foo', 'http://bar/', 'http://bar/foo/'))
 
860
        self.assertEqual(
 
861
            'qux/bar',
 
862
            urlutils.rebase_url('../bar', 'http://baz/qux/foo', 'http://baz/'))
666
863
 
667
864
    def test_determine_relative_path(self):
668
865
        self.assertEqual('../../baz/bar',
669
866
                         urlutils.determine_relative_path(
670
 
                         '/qux/quxx', '/baz/bar'))
 
867
                             '/qux/quxx', '/baz/bar'))
671
868
        self.assertEqual('..',
672
869
                         urlutils.determine_relative_path(
673
 
                         '/bar/baz', '/bar'))
 
870
                             '/bar/baz', '/bar'))
674
871
        self.assertEqual('baz',
675
872
                         urlutils.determine_relative_path(
676
 
                         '/bar', '/bar/baz'))
 
873
                             '/bar', '/bar/baz'))
677
874
        self.assertEqual('.', urlutils.determine_relative_path(
678
875
                         '/bar', '/bar'))
679
876
 
680
877
 
681
878
class TestParseURL(TestCase):
682
879
 
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'))
 
880
    def test_parse_simple(self):
 
881
        parsed = urlutils.parse_url('http://example.com:80/one')
 
882
        self.assertEqual(('http', None, None, 'example.com', 80, '/one'),
 
883
                         parsed)
 
884
 
 
885
    def test_ipv6(self):
 
886
        parsed = urlutils.parse_url('http://[1:2:3::40]/one')
 
887
        self.assertEqual(('http', None, None, '1:2:3::40', None, '/one'),
 
888
                         parsed)
 
889
 
 
890
    def test_ipv6_port(self):
 
891
        parsed = urlutils.parse_url('http://[1:2:3::40]:80/one')
 
892
        self.assertEqual(('http', None, None, '1:2:3::40', 80, '/one'),
 
893
                         parsed)
 
894
 
 
895
 
 
896
class TestURL(TestCase):
 
897
 
 
898
    def test_parse_simple(self):
 
899
        parsed = urlutils.URL.from_string('http://example.com:80/one')
 
900
        self.assertEqual('http', parsed.scheme)
 
901
        self.assertIs(None, parsed.user)
 
902
        self.assertIs(None, parsed.password)
 
903
        self.assertEqual('example.com', parsed.host)
 
904
        self.assertEqual(80, parsed.port)
 
905
        self.assertEqual('/one', parsed.path)
 
906
 
 
907
    def test_ipv6(self):
 
908
        parsed = urlutils.URL.from_string('http://[1:2:3::40]/one')
 
909
        self.assertEqual('http', parsed.scheme)
 
910
        self.assertIs(None, parsed.port)
 
911
        self.assertIs(None, parsed.user)
 
912
        self.assertIs(None, parsed.password)
 
913
        self.assertEqual('1:2:3::40', parsed.host)
 
914
        self.assertEqual('/one', parsed.path)
 
915
 
 
916
    def test_ipv6_port(self):
 
917
        parsed = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
918
        self.assertEqual('http', parsed.scheme)
 
919
        self.assertEqual('1:2:3::40', parsed.host)
 
920
        self.assertIs(None, parsed.user)
 
921
        self.assertIs(None, parsed.password)
 
922
        self.assertEqual(80, parsed.port)
 
923
        self.assertEqual('/one', parsed.path)
 
924
 
 
925
    def test_quoted(self):
 
926
        parsed = urlutils.URL.from_string(
 
927
            'http://ro%62ey:h%40t@ex%41mple.com:2222/path')
 
928
        self.assertEqual(parsed.quoted_host, 'ex%41mple.com')
 
929
        self.assertEqual(parsed.host, 'exAmple.com')
 
930
        self.assertEqual(parsed.port, 2222)
 
931
        self.assertEqual(parsed.quoted_user, 'ro%62ey')
 
932
        self.assertEqual(parsed.user, 'robey')
 
933
        self.assertEqual(parsed.quoted_password, 'h%40t')
 
934
        self.assertEqual(parsed.password, 'h@t')
 
935
        self.assertEqual(parsed.path, '/path')
 
936
 
 
937
    def test_eq(self):
 
938
        parsed1 = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
939
        parsed2 = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
940
        self.assertEqual(parsed1, parsed2)
 
941
        self.assertEqual(parsed1, parsed1)
 
942
        parsed2.path = '/two'
 
943
        self.assertNotEqual(parsed1, parsed2)
 
944
 
 
945
    def test_repr(self):
 
946
        parsed = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
947
        self.assertEqual(
 
948
            "<URL('http', None, None, '1:2:3::40', 80, '/one')>",
 
949
            repr(parsed))
 
950
 
 
951
    def test_str(self):
 
952
        parsed = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
953
        self.assertEqual('http://[1:2:3::40]:80/one', str(parsed))
 
954
 
 
955
    def test__combine_paths(self):
 
956
        combine = urlutils.URL._combine_paths
 
957
        self.assertEqual('/home/sarah/project/foo',
 
958
                         combine('/home/sarah', 'project/foo'))
 
959
        self.assertEqual('/etc',
 
960
                         combine('/home/sarah', '../../etc'))
 
961
        self.assertEqual('/etc',
 
962
                         combine('/home/sarah', '../../../etc'))
 
963
        self.assertEqual('/etc',
 
964
                         combine('/home/sarah', '/etc'))
 
965
 
 
966
    def test_clone(self):
 
967
        url = urlutils.URL.from_string('http://[1:2:3::40]:80/one')
 
968
        url1 = url.clone("two")
 
969
        self.assertEqual("/one/two", url1.path)
 
970
        url2 = url.clone("/two")
 
971
        self.assertEqual("/two", url2.path)
 
972
        url3 = url.clone()
 
973
        self.assertIsNot(url, url3)
 
974
        self.assertEqual(url, url3)
 
975
 
 
976
    def test_parse_empty_port(self):
 
977
        parsed = urlutils.URL.from_string('http://example.com:/one')
 
978
        self.assertEqual('http', parsed.scheme)
 
979
        self.assertIs(None, parsed.user)
 
980
        self.assertIs(None, parsed.password)
 
981
        self.assertEqual('example.com', parsed.host)
 
982
        self.assertIs(None, parsed.port)
 
983
        self.assertEqual('/one', parsed.path)
 
984
 
 
985
 
 
986
class TestFileRelpath(TestCase):
 
987
 
 
988
    # GZ 2011-11-18: A way to override all path handling functions to one
 
989
    #                platform or another for testing would be nice.
 
990
 
 
991
    def _with_posix_paths(self):
 
992
        self.overrideAttr(urlutils, "local_path_from_url",
 
993
                          urlutils._posix_local_path_from_url)
 
994
        self.overrideAttr(urlutils, "MIN_ABS_FILEURL_LENGTH", len("file:///"))
 
995
        self.overrideAttr(osutils, "normpath", osutils._posix_normpath)
 
996
        self.overrideAttr(osutils, "abspath", osutils._posix_abspath)
 
997
        self.overrideAttr(osutils, "normpath", osutils._posix_normpath)
 
998
        self.overrideAttr(osutils, "pathjoin", osutils.posixpath.join)
 
999
        self.overrideAttr(osutils, "split", osutils.posixpath.split)
 
1000
        self.overrideAttr(osutils, "MIN_ABS_PATHLENGTH", 1)
 
1001
 
 
1002
    def _with_win32_paths(self):
 
1003
        self.overrideAttr(urlutils, "local_path_from_url",
 
1004
                          urlutils._win32_local_path_from_url)
 
1005
        self.overrideAttr(urlutils, "MIN_ABS_FILEURL_LENGTH",
 
1006
                          urlutils.WIN32_MIN_ABS_FILEURL_LENGTH)
 
1007
        self.overrideAttr(osutils, "abspath", osutils._win32_abspath)
 
1008
        self.overrideAttr(osutils, "normpath", osutils._win32_normpath)
 
1009
        self.overrideAttr(osutils, "pathjoin", osutils._win32_pathjoin)
 
1010
        self.overrideAttr(osutils, "split", osutils.ntpath.split)
 
1011
        self.overrideAttr(osutils, "MIN_ABS_PATHLENGTH", 3)
 
1012
 
 
1013
    def test_same_url_posix(self):
 
1014
        self._with_posix_paths()
 
1015
        self.assertEqual("",
 
1016
                         urlutils.file_relpath("file:///a", "file:///a"))
 
1017
        self.assertEqual("",
 
1018
                         urlutils.file_relpath("file:///a", "file:///a/"))
 
1019
        self.assertEqual("",
 
1020
                         urlutils.file_relpath("file:///a/", "file:///a"))
 
1021
 
 
1022
    def test_same_url_win32(self):
 
1023
        self._with_win32_paths()
 
1024
        self.assertEqual("",
 
1025
                         urlutils.file_relpath("file:///A:/", "file:///A:/"))
 
1026
        self.assertEqual("",
 
1027
                         urlutils.file_relpath("file:///A|/", "file:///A:/"))
 
1028
        self.assertEqual(
 
1029
            "", urlutils.file_relpath("file:///A:/b/", "file:///A:/b/"))
 
1030
        self.assertEqual(
 
1031
            "", urlutils.file_relpath("file:///A:/b", "file:///A:/b/"))
 
1032
        self.assertEqual(
 
1033
            "", urlutils.file_relpath("file:///A:/b/", "file:///A:/b"))
 
1034
 
 
1035
    def test_child_posix(self):
 
1036
        self._with_posix_paths()
 
1037
        self.assertEqual(
 
1038
            "b", urlutils.file_relpath("file:///a", "file:///a/b"))
 
1039
        self.assertEqual(
 
1040
            "b", urlutils.file_relpath("file:///a/", "file:///a/b"))
 
1041
        self.assertEqual(
 
1042
            "b/c", urlutils.file_relpath("file:///a", "file:///a/b/c"))
 
1043
 
 
1044
    def test_child_win32(self):
 
1045
        self._with_win32_paths()
 
1046
        self.assertEqual(
 
1047
            "b", urlutils.file_relpath("file:///A:/", "file:///A:/b"))
 
1048
        self.assertEqual(
 
1049
            "b", urlutils.file_relpath("file:///A|/", "file:///A:/b"))
 
1050
        self.assertEqual(
 
1051
            "c", urlutils.file_relpath("file:///A:/b", "file:///A:/b/c"))
 
1052
        self.assertEqual(
 
1053
            "c", urlutils.file_relpath("file:///A:/b/", "file:///A:/b/c"))
 
1054
        self.assertEqual(
 
1055
            "c/d", urlutils.file_relpath("file:///A:/b", "file:///A:/b/c/d"))
 
1056
 
 
1057
    def test_sibling_posix(self):
 
1058
        self._with_posix_paths()
 
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
        self.assertRaises(
 
1066
            PathNotChild,
 
1067
            urlutils.file_relpath, "file:///a/b/", "file:///a/c/")
 
1068
 
 
1069
    def test_sibling_win32(self):
 
1070
        self._with_win32_paths()
 
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
        self.assertRaises(
 
1078
            PathNotChild,
 
1079
            urlutils.file_relpath, "file:///A:/b/", "file:///A:/c/")
 
1080
 
 
1081
    def test_parent_posix(self):
 
1082
        self._with_posix_paths()
 
1083
        self.assertRaises(PathNotChild,
 
1084
                          urlutils.file_relpath, "file:///a/b", "file:///a")
 
1085
        self.assertRaises(PathNotChild,
 
1086
                          urlutils.file_relpath, "file:///a/b", "file:///a/")
 
1087
 
 
1088
    def test_parent_win32(self):
 
1089
        self._with_win32_paths()
 
1090
        self.assertRaises(
 
1091
            PathNotChild,
 
1092
            urlutils.file_relpath, "file:///A:/b", "file:///A:/")
 
1093
        self.assertRaises(
 
1094
            PathNotChild,
 
1095
            urlutils.file_relpath, "file:///A:/b/c", "file:///A:/b")
 
1096
 
 
1097
 
 
1098
class QuoteTests(TestCase):
 
1099
 
 
1100
    def test_quote(self):
 
1101
        self.assertEqual('abc%20def', urlutils.quote('abc def'))
 
1102
        self.assertEqual('abc%2Fdef', urlutils.quote('abc/def', safe=''))
 
1103
        self.assertEqual('abc/def', urlutils.quote('abc/def', safe='/'))
 
1104
 
 
1105
    def test_quote_tildes(self):
 
1106
        # Whether ~ is quoted by default depends on the python version
 
1107
        if sys.version_info[:2] >= (3, 7):
 
1108
            # https://docs.python.org/3/whatsnew/3.7.html#urllib-parse
 
1109
            self.assertEqual('~foo', urlutils.quote('~foo'))
 
1110
        else:
 
1111
            self.assertEqual('%7Efoo', urlutils.quote('~foo'))
 
1112
        self.assertEqual('~foo', urlutils.quote('~foo', safe='/~'))
 
1113
 
 
1114
    def test_unquote(self):
 
1115
        self.assertEqual('%', urlutils.unquote('%25'))
 
1116
        if PY3:
 
1117
            self.assertEqual('\xe5', urlutils.unquote('%C3%A5'))
 
1118
        else:
 
1119
            self.assertEqual('\xc3\xa5', urlutils.unquote('%C3%A5'))
 
1120
        self.assertEqual(u"\xe5", urlutils.unquote(u'\xe5'))
 
1121
 
 
1122
    def test_unquote_to_bytes(self):
 
1123
        self.assertEqual(b'%', urlutils.unquote_to_bytes('%25'))
 
1124
        self.assertEqual(b'\xc3\xa5', urlutils.unquote_to_bytes('%C3%A5'))