/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 bzrlib/tests/test_urlutils.py

  • Committer: Wouter van Heyst
  • Date: 2006-06-07 12:22:54 UTC
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: larstiq@larstiq.dyndns.org-20060607122254-c617eeabfd5e05e7
more tests handle LANG=C

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Tests for the urlutils wrapper."""
 
18
 
 
19
import os
 
20
import sys
 
21
 
 
22
import bzrlib
 
23
from bzrlib.errors import InvalidURL, InvalidURLJoin
 
24
import bzrlib.urlutils as urlutils
 
25
from bzrlib.tests import TestCaseInTempDir, TestCase, TestSkipped
 
26
 
 
27
 
 
28
class TestUrlToPath(TestCase):
 
29
    
 
30
    def test_basename(self):
 
31
        # bzrlib.urlutils.basename
 
32
        # Test bzrlib.urlutils.split()
 
33
        basename = urlutils.basename
 
34
        if sys.platform == 'win32':
 
35
            self.assertRaises(InvalidURL, basename, 'file:///path/to/foo')
 
36
            self.assertEqual('foo', basename('file:///C|/foo'))
 
37
            self.assertEqual('', basename('file:///C|/'))
 
38
        else:
 
39
            self.assertEqual('foo', basename('file:///foo'))
 
40
            self.assertEqual('', basename('file:///'))
 
41
 
 
42
        self.assertEqual('foo', basename('http://host/path/to/foo'))
 
43
        self.assertEqual('foo', basename('http://host/path/to/foo/'))
 
44
        self.assertEqual('',
 
45
            basename('http://host/path/to/foo/', exclude_trailing_slash=False))
 
46
        self.assertEqual('path', basename('http://host/path'))
 
47
        self.assertEqual('', basename('http://host/'))
 
48
        self.assertEqual('', basename('http://host'))
 
49
        self.assertEqual('path', basename('http:///nohost/path'))
 
50
 
 
51
        self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path'))
 
52
        self.assertEqual('path', basename('random+scheme://user:pass@ahost:port/path/'))
 
53
        self.assertEqual('', basename('random+scheme://user:pass@ahost:port/'))
 
54
 
 
55
        # relative paths
 
56
        self.assertEqual('foo', basename('path/to/foo'))
 
57
        self.assertEqual('foo', basename('path/to/foo/'))
 
58
        self.assertEqual('', basename('path/to/foo/',
 
59
            exclude_trailing_slash=False))
 
60
        self.assertEqual('foo', basename('path/../foo'))
 
61
        self.assertEqual('foo', basename('../path/foo'))
 
62
 
 
63
    def test_normalize_url_files(self):
 
64
        # Test that local paths are properly normalized
 
65
        normalize_url = urlutils.normalize_url
 
66
 
 
67
        def norm_file(expected, path):
 
68
            url = normalize_url(path)
 
69
            self.assertStartsWith(url, 'file:///')
 
70
            if sys.platform == 'win32':
 
71
                url = url[len('file:///C:'):]
 
72
            else:
 
73
                url = url[len('file://'):]
 
74
 
 
75
            self.assertEndsWith(url, expected)
 
76
 
 
77
        norm_file('path/to/foo', 'path/to/foo')
 
78
        norm_file('/path/to/foo', '/path/to/foo')
 
79
        norm_file('path/to/foo', '../path/to/foo')
 
80
 
 
81
        # Local paths are assumed to *not* be escaped at all
 
82
        try:
 
83
            u'uni/\xb5'.encode(bzrlib.user_encoding)
 
84
        except UnicodeError:
 
85
            # locale cannot handle unicode 
 
86
            pass
 
87
        else:
 
88
            norm_file('uni/%C2%B5', u'uni/\xb5')
 
89
 
 
90
        norm_file('uni/%25C2%25B5', u'uni/%C2%B5')
 
91
        norm_file('uni/%20b', u'uni/ b')
 
92
        # All the crazy characters get escaped in local paths => file:/// urls
 
93
        norm_file('%27%3B/%3F%3A%40%26%3D%2B%24%2C%23%20', "';/?:@&=+$,# ")
 
94
 
 
95
    def test_normalize_url_hybrid(self):
 
96
        # Anything with a scheme:// should be treated as a hybrid url
 
97
        # which changes what characters get escaped.
 
98
        normalize_url = urlutils.normalize_url
 
99
 
 
100
        eq = self.assertEqual
 
101
        eq('file:///foo/', normalize_url(u'file:///foo/'))
 
102
        eq('file:///foo/%20', normalize_url(u'file:///foo/ '))
 
103
        eq('file:///foo/%20', normalize_url(u'file:///foo/%20'))
 
104
        # Don't escape reserved characters
 
105
        eq('file:///ab_c.d-e/%f:?g&h=i+j;k,L#M$',
 
106
            normalize_url('file:///ab_c.d-e/%f:?g&h=i+j;k,L#M$'))
 
107
        eq('http://ab_c.d-e/%f:?g&h=i+j;k,L#M$',
 
108
            normalize_url('http://ab_c.d-e/%f:?g&h=i+j;k,L#M$'))
 
109
 
 
110
        # Escape unicode characters, but not already escaped chars
 
111
        eq('http://host/ab/%C2%B5/%C2%B5',
 
112
            normalize_url(u'http://host/ab/%C2%B5/\xb5'))
 
113
 
 
114
        # Normalize verifies URLs when they are not unicode
 
115
        # (indicating they did not come from the user)
 
116
        self.assertRaises(InvalidURL, normalize_url, 'http://host/\xb5')
 
117
        self.assertRaises(InvalidURL, normalize_url, 'http://host/ ')
 
118
 
 
119
    def test_url_scheme_re(self):
 
120
        # Test paths that may be URLs
 
121
        def test_one(url, scheme_and_path):
 
122
            """Assert that _url_scheme_re correctly matches
 
123
 
 
124
            :param scheme_and_path: The (scheme, path) that should be matched
 
125
                can be None, to indicate it should not match
 
126
            """
 
127
            m = urlutils._url_scheme_re.match(url)
 
128
            if scheme_and_path is None:
 
129
                self.assertEqual(None, m)
 
130
            else:
 
131
                self.assertEqual(scheme_and_path[0], m.group('scheme'))
 
132
                self.assertEqual(scheme_and_path[1], m.group('path'))
 
133
 
 
134
        # Local paths
 
135
        test_one('/path', None)
 
136
        test_one('C:/path', None)
 
137
        test_one('../path/to/foo', None)
 
138
        test_one(u'../path/to/fo\xe5', None)
 
139
 
 
140
        # Real URLS
 
141
        test_one('http://host/path/', ('http', 'host/path/'))
 
142
        test_one('sftp://host/path/to/foo', ('sftp', 'host/path/to/foo'))
 
143
        test_one('file:///usr/bin', ('file', '/usr/bin'))
 
144
        test_one('file:///C:/Windows', ('file', '/C:/Windows'))
 
145
        test_one('file:///C|/Windows', ('file', '/C|/Windows'))
 
146
        test_one(u'readonly+sftp://host/path/\xe5', ('readonly+sftp', u'host/path/\xe5'))
 
147
 
 
148
        # Weird stuff
 
149
        # Can't have slashes or colons in the scheme
 
150
        test_one('/path/to/://foo', None)
 
151
        test_one('path:path://foo', None)
 
152
        # Must have more than one character for scheme
 
153
        test_one('C://foo', None)
 
154
        test_one('ab://foo', ('ab', 'foo'))
 
155
 
 
156
    def test_dirname(self):
 
157
        # Test bzrlib.urlutils.dirname()
 
158
        dirname = urlutils.dirname
 
159
        if sys.platform == 'win32':
 
160
            self.assertRaises(InvalidURL, dirname, 'file:///path/to/foo')
 
161
            self.assertEqual('file:///C|/', dirname('file:///C|/foo'))
 
162
            self.assertEqual('file:///C|/', dirname('file:///C|/'))
 
163
        else:
 
164
            self.assertEqual('file:///', dirname('file:///foo'))
 
165
            self.assertEqual('file:///', dirname('file:///'))
 
166
 
 
167
        self.assertEqual('http://host/path/to', dirname('http://host/path/to/foo'))
 
168
        self.assertEqual('http://host/path/to', dirname('http://host/path/to/foo/'))
 
169
        self.assertEqual('http://host/path/to/foo',
 
170
            dirname('http://host/path/to/foo/', exclude_trailing_slash=False))
 
171
        self.assertEqual('http://host/', dirname('http://host/path'))
 
172
        self.assertEqual('http://host/', dirname('http://host/'))
 
173
        self.assertEqual('http://host', dirname('http://host'))
 
174
        self.assertEqual('http:///nohost', dirname('http:///nohost/path'))
 
175
 
 
176
        self.assertEqual('random+scheme://user:pass@ahost:port/',
 
177
            dirname('random+scheme://user:pass@ahost:port/path'))
 
178
        self.assertEqual('random+scheme://user:pass@ahost:port/',
 
179
            dirname('random+scheme://user:pass@ahost:port/path/'))
 
180
        self.assertEqual('random+scheme://user:pass@ahost:port/',
 
181
            dirname('random+scheme://user:pass@ahost:port/'))
 
182
 
 
183
        # relative paths
 
184
        self.assertEqual('path/to', dirname('path/to/foo'))
 
185
        self.assertEqual('path/to', dirname('path/to/foo/'))
 
186
        self.assertEqual('path/to/foo',
 
187
            dirname('path/to/foo/', exclude_trailing_slash=False))
 
188
        self.assertEqual('path/..', dirname('path/../foo'))
 
189
        self.assertEqual('../path', dirname('../path/foo'))
 
190
 
 
191
    def test_join(self):
 
192
        def test(expected, *args):
 
193
            joined = urlutils.join(*args)
 
194
            self.assertEqual(expected, joined)
 
195
 
 
196
        # Test a single element
 
197
        test('foo', 'foo')
 
198
 
 
199
        # Test relative path joining
 
200
        test('foo/bar', 'foo', 'bar')
 
201
        test('http://foo/bar', 'http://foo', 'bar')
 
202
        test('http://foo/bar', 'http://foo', '.', 'bar')
 
203
        test('http://foo/baz', 'http://foo', 'bar', '../baz')
 
204
        test('http://foo/bar/baz', 'http://foo', 'bar/baz')
 
205
        test('http://foo/baz', 'http://foo', 'bar/../baz')
 
206
 
 
207
        # Absolute paths
 
208
        test('http://bar', 'http://foo', 'http://bar')
 
209
        test('sftp://bzr/foo', 'http://foo', 'bar', 'sftp://bzr/foo')
 
210
        test('file:///bar', 'foo', 'file:///bar')
 
211
        
 
212
        # Invalid joinings
 
213
        # Cannot go above root
 
214
        self.assertRaises(InvalidURLJoin, urlutils.join,
 
215
                'http://foo', '../baz')
 
216
 
 
217
    def test_function_type(self):
 
218
        if sys.platform == 'win32':
 
219
            self.assertEqual(urlutils._win32_local_path_to_url, urlutils.local_path_to_url)
 
220
            self.assertEqual(urlutils._win32_local_path_from_url, urlutils.local_path_from_url)
 
221
        else:
 
222
            self.assertEqual(urlutils._posix_local_path_to_url, urlutils.local_path_to_url)
 
223
            self.assertEqual(urlutils._posix_local_path_from_url, urlutils.local_path_from_url)
 
224
 
 
225
    def test_posix_local_path_to_url(self):
 
226
        to_url = urlutils._posix_local_path_to_url
 
227
        self.assertEqual('file:///path/to/foo',
 
228
            to_url('/path/to/foo'))
 
229
 
 
230
        try:
 
231
            result = to_url(u'/path/to/r\xe4ksm\xf6rg\xe5s')
 
232
        except UnicodeError:
 
233
            raise TestSkipped("local encoding cannot handle unicode")
 
234
 
 
235
        self.assertEqual('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
 
236
 
 
237
    def test_posix_local_path_from_url(self):
 
238
        from_url = urlutils._posix_local_path_from_url
 
239
        self.assertEqual('/path/to/foo',
 
240
            from_url('file:///path/to/foo'))
 
241
        self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
 
242
            from_url('file:///path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
 
243
        self.assertEqual(u'/path/to/r\xe4ksm\xf6rg\xe5s',
 
244
            from_url('file:///path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
 
245
 
 
246
        self.assertRaises(InvalidURL, from_url, '/path/to/foo')
 
247
 
 
248
    def test_win32_local_path_to_url(self):
 
249
        to_url = urlutils._win32_local_path_to_url
 
250
        self.assertEqual('file:///C|/path/to/foo',
 
251
            to_url('C:/path/to/foo'))
 
252
 
 
253
        try:
 
254
            result = to_url(u'd:/path/to/r\xe4ksm\xf6rg\xe5s')
 
255
        except UnicodeError:
 
256
            raise TestSkipped("local encoding cannot handle unicode")
 
257
 
 
258
        self.assertEqual('file:///D|/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s', result)
 
259
 
 
260
    def test_win32_local_path_from_url(self):
 
261
        from_url = urlutils._win32_local_path_from_url
 
262
        self.assertEqual('C:/path/to/foo',
 
263
            from_url('file:///C|/path/to/foo'))
 
264
        self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
 
265
            from_url('file:///d|/path/to/r%C3%A4ksm%C3%B6rg%C3%A5s'))
 
266
        self.assertEqual(u'D:/path/to/r\xe4ksm\xf6rg\xe5s',
 
267
            from_url('file:///d|/path/to/r%c3%a4ksm%c3%b6rg%c3%a5s'))
 
268
 
 
269
        self.assertRaises(InvalidURL, from_url, '/path/to/foo')
 
270
        # Not a valid _win32 url, no drive letter
 
271
        self.assertRaises(InvalidURL, from_url, 'file:///path/to/foo')
 
272
 
 
273
    def test_split(self):
 
274
        # Test bzrlib.urlutils.split()
 
275
        split = urlutils.split
 
276
        if sys.platform == 'win32':
 
277
            self.assertRaises(InvalidURL, split, 'file:///path/to/foo')
 
278
            self.assertEqual(('file:///C|/', 'foo'), split('file:///C|/foo'))
 
279
            self.assertEqual(('file:///C|/', ''), split('file:///C|/'))
 
280
        else:
 
281
            self.assertEqual(('file:///', 'foo'), split('file:///foo'))
 
282
            self.assertEqual(('file:///', ''), split('file:///'))
 
283
 
 
284
        self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo'))
 
285
        self.assertEqual(('http://host/path/to', 'foo'), split('http://host/path/to/foo/'))
 
286
        self.assertEqual(('http://host/path/to/foo', ''),
 
287
            split('http://host/path/to/foo/', exclude_trailing_slash=False))
 
288
        self.assertEqual(('http://host/', 'path'), split('http://host/path'))
 
289
        self.assertEqual(('http://host/', ''), split('http://host/'))
 
290
        self.assertEqual(('http://host', ''), split('http://host'))
 
291
        self.assertEqual(('http:///nohost', 'path'), split('http:///nohost/path'))
 
292
 
 
293
        self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
 
294
            split('random+scheme://user:pass@ahost:port/path'))
 
295
        self.assertEqual(('random+scheme://user:pass@ahost:port/', 'path'),
 
296
            split('random+scheme://user:pass@ahost:port/path/'))
 
297
        self.assertEqual(('random+scheme://user:pass@ahost:port/', ''),
 
298
            split('random+scheme://user:pass@ahost:port/'))
 
299
 
 
300
        # relative paths
 
301
        self.assertEqual(('path/to', 'foo'), split('path/to/foo'))
 
302
        self.assertEqual(('path/to', 'foo'), split('path/to/foo/'))
 
303
        self.assertEqual(('path/to/foo', ''),
 
304
            split('path/to/foo/', exclude_trailing_slash=False))
 
305
        self.assertEqual(('path/..', 'foo'), split('path/../foo'))
 
306
        self.assertEqual(('../path', 'foo'), split('../path/foo'))
 
307
 
 
308
    def test_strip_trailing_slash(self):
 
309
        sts = urlutils.strip_trailing_slash
 
310
        if sys.platform == 'win32':
 
311
            self.assertEqual('file:///C|/', sts('file:///C|/'))
 
312
            self.assertEqual('file:///C|/foo', sts('file:///C|/foo'))
 
313
            self.assertEqual('file:///C|/foo', sts('file:///C|/foo/'))
 
314
        else:
 
315
            self.assertEqual('file:///', sts('file:///'))
 
316
            self.assertEqual('file:///foo', sts('file:///foo'))
 
317
            self.assertEqual('file:///foo', sts('file:///foo/'))
 
318
 
 
319
        self.assertEqual('http://host/', sts('http://host/'))
 
320
        self.assertEqual('http://host/foo', sts('http://host/foo'))
 
321
        self.assertEqual('http://host/foo', sts('http://host/foo/'))
 
322
 
 
323
        # No need to fail just because the slash is missing
 
324
        self.assertEqual('http://host', sts('http://host'))
 
325
        # TODO: jam 20060502 Should this raise InvalidURL?
 
326
        self.assertEqual('file://', sts('file://'))
 
327
 
 
328
        self.assertEqual('random+scheme://user:pass@ahost:port/path',
 
329
            sts('random+scheme://user:pass@ahost:port/path'))
 
330
        self.assertEqual('random+scheme://user:pass@ahost:port/path',
 
331
            sts('random+scheme://user:pass@ahost:port/path/'))
 
332
        self.assertEqual('random+scheme://user:pass@ahost:port/',
 
333
            sts('random+scheme://user:pass@ahost:port/'))
 
334
 
 
335
        # Make sure relative paths work too
 
336
        self.assertEqual('path/to/foo', sts('path/to/foo'))
 
337
        self.assertEqual('path/to/foo', sts('path/to/foo/'))
 
338
        self.assertEqual('../to/foo', sts('../to/foo/'))
 
339
        self.assertEqual('path/../foo', sts('path/../foo/'))
 
340
 
 
341
    def test_unescape_for_display_utf8(self):
 
342
        # Test that URLs are converted to nice unicode strings for display
 
343
        def test(expected, url, encoding='utf-8'):
 
344
            disp_url = urlutils.unescape_for_display(url, encoding=encoding)
 
345
            self.assertIsInstance(disp_url, unicode)
 
346
            self.assertEqual(expected, disp_url)
 
347
        test('http://foo', 'http://foo')
 
348
        if sys.platform == 'win32':
 
349
            test('C:/foo/path', 'file:///C|foo/path')
 
350
        else:
 
351
            test('/foo/path', 'file:///foo/path')
 
352
 
 
353
        test('http://foo/%2Fbaz', 'http://foo/%2Fbaz')
 
354
        test(u'http://host/r\xe4ksm\xf6rg\xe5s',
 
355
             'http://host/r%C3%A4ksm%C3%B6rg%C3%A5s')
 
356
 
 
357
        # Make sure special escaped characters stay escaped
 
358
        test(u'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23',
 
359
             'http://host/%3B%2F%3F%3A%40%26%3D%2B%24%2C%23')
 
360
 
 
361
        # Can we handle sections that don't have utf-8 encoding?
 
362
        test(u'http://host/%EE%EE%EE/r\xe4ksm\xf6rg\xe5s',
 
363
             'http://host/%EE%EE%EE/r%C3%A4ksm%C3%B6rg%C3%A5s')
 
364
 
 
365
        # Test encoding into output that can handle some characters
 
366
        test(u'http://host/%EE%EE%EE/r\xe4ksm\xf6rg\xe5s',
 
367
             'http://host/%EE%EE%EE/r%C3%A4ksm%C3%B6rg%C3%A5s',
 
368
             encoding='iso-8859-1')
 
369
 
 
370
        # This one can be encoded into utf8
 
371
        test(u'http://host/\u062c\u0648\u062c\u0648',
 
372
             'http://host/%d8%ac%d9%88%d8%ac%d9%88',
 
373
             encoding='utf-8')
 
374
 
 
375
        # This can't be put into 8859-1 and so stays as escapes
 
376
        test(u'http://host/%d8%ac%d9%88%d8%ac%d9%88',
 
377
             'http://host/%d8%ac%d9%88%d8%ac%d9%88',
 
378
             encoding='iso-8859-1')
 
379
 
 
380
    def test_escape(self):
 
381
        self.assertEqual('%25', urlutils.escape('%'))
 
382
        self.assertEqual('%C3%A5', urlutils.escape(u'\xe5'))
 
383
 
 
384
    def test_unescape(self):
 
385
        self.assertEqual('%', urlutils.unescape('%25'))
 
386
        self.assertEqual(u'\xe5', urlutils.unescape('%C3%A5'))
 
387
 
 
388
        self.assertRaises(InvalidURL, urlutils.unescape, u'\xe5')
 
389
        self.assertRaises(InvalidURL, urlutils.unescape, '\xe5')
 
390
        self.assertRaises(InvalidURL, urlutils.unescape, '%E5')
 
391
 
 
392
    def test_escape_unescape(self):
 
393
        self.assertEqual(u'\xe5', urlutils.unescape(urlutils.escape(u'\xe5')))
 
394
        self.assertEqual('%', urlutils.unescape(urlutils.escape('%')))
 
395
 
 
396
    def test_relative_url(self):
 
397
        def test(expected, base, other):
 
398
            result = urlutils.relative_url(base, other)
 
399
            self.assertEqual(expected, result)
 
400
            
 
401
        test('a', 'http://host/', 'http://host/a')
 
402
        test('http://entirely/different', 'sftp://host/branch',
 
403
                    'http://entirely/different')
 
404
        test('../person/feature', 'http://host/branch/mainline',
 
405
                    'http://host/branch/person/feature')
 
406
        test('..', 'http://host/branch', 'http://host/')
 
407
        test('http://host2/branch', 'http://host1/branch', 'http://host2/branch')
 
408
        test('.', 'http://host1/branch', 'http://host1/branch')
 
409
        test('../../../branch/2b', 'file:///home/jelmer/foo/bar/2b',
 
410
                    'file:///home/jelmer/branch/2b')
 
411
        test('../../branch/2b', 'sftp://host/home/jelmer/bar/2b',
 
412
                    'sftp://host/home/jelmer/branch/2b')
 
413
        test('../../branch/feature/2b', 'http://host/home/jelmer/bar/2b',
 
414
                    'http://host/home/jelmer/branch/feature/2b')
 
415
        test('../../branch/feature/2b', 'http://host/home/jelmer/bar/2b/', 
 
416
                    'http://host/home/jelmer/branch/feature/2b')
 
417
        # relative_url should preserve a trailing slash
 
418
        test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b/',
 
419
                    'http://host/home/jelmer/branch/feature/2b/')
 
420
        test('../../branch/feature/2b/', 'http://host/home/jelmer/bar/2b',
 
421
                    'http://host/home/jelmer/branch/feature/2b/')
 
422
 
 
423
        # TODO: treat http://host as http://host/
 
424
        #       relative_url is typically called from a branch.base or
 
425
        #       transport.base which always ends with a /
 
426
        #test('a', 'http://host', 'http://host/a')
 
427
        test('http://host/a', 'http://host', 'http://host/a')
 
428
        #test('.', 'http://host', 'http://host/')
 
429
        test('http://host/', 'http://host', 'http://host/')
 
430
        #test('.', 'http://host/', 'http://host')
 
431
        test('http://host', 'http://host/', 'http://host')