import os, sys, string, codecs
from Ft.Lib import Uri
from Ft.Lib.Resolvers import SchemeRegistryResolver

TEST_DOT_PY_BANGPATH = "#!/usr/bin/env python"

# Test cases for BaseJoin() ==================================================
# (base, relative, expected)
basejoin_test_cases = [
    ('','file:docs/xml/4XLink.api','file:docs/xml/4XLink.api'),
    ('file:docs/xml/','file:docs/xml/4XLink.api','file:docs/xml/4XLink.api'),
    ('file:/usr/local/lib/xslt/bar.xslt','http://4Suite.org/foo.xslt','http://4Suite.org/foo.xslt'),
    ]

# Test cases for Absolutize() ================================================
#CURRENT_DOC_URI = 'http://spam.com/bacon/eggs.xml'
BASE_URI = ('http://a/b/c/d;p?q',
            'http://a/b/c/d;p?q=1/2',
            'http://a/b/c/d;p=1/2?q',
            'fred:///s//a/b/c',
            'http:///s//a/b/c',
            )
# (ref, base, expected)
absolutize_test_cases = [
    # http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html
    ('../c',  'foo:a/b', 'foo:c'),
    ('foo:.', 'foo:a',   'foo:'),
    ('/foo/../../../bar', 'zz:abc', 'zz:/bar'),
    ('/foo/../bar',       'zz:abc', 'zz:/bar'),
    ('foo/../../../bar',  'zz:abc', 'zz:bar'),
    ('foo/../bar',        'zz:abc', 'zz:bar'),
    ('zz:.',              'zz:abc', 'zz:'),
    ('/.'      , BASE_URI[0], 'http://a/'),
    ('/.foo'   , BASE_URI[0], 'http://a/.foo'),
    ('.foo'    , BASE_URI[0], 'http://a/b/c/.foo'),

    # http://gbiv.com/protocols/uri/test/rel_examples1.html
    # examples from RFC 2396
    ('g:h'     , BASE_URI[0], 'g:h'),
    ('g'       , BASE_URI[0], 'http://a/b/c/g'),
    ('./g'     , BASE_URI[0], 'http://a/b/c/g'),
    ('g/'      , BASE_URI[0], 'http://a/b/c/g/'),
    ('/g'      , BASE_URI[0], 'http://a/g'),
    ('//g'     , BASE_URI[0], 'http://g'),
    # changed with RFC 2396bis
    #('?y'      , BASE_URI[0], 'http://a/b/c/d;p?y'),
    ('?y'      , BASE_URI[0], 'http://a/b/c/d;p?y'),
    ('g?y'     , BASE_URI[0], 'http://a/b/c/g?y'),
    # changed with RFC 2396bis
    #('#s'      , BASE_URI[0], CURRENT_DOC_URI + '#s'),
    ('#s'      , BASE_URI[0], 'http://a/b/c/d;p?q#s'),
    ('g#s'     , BASE_URI[0], 'http://a/b/c/g#s'),
    ('g?y#s'   , BASE_URI[0], 'http://a/b/c/g?y#s'),
    (';x'      , BASE_URI[0], 'http://a/b/c/;x'),
    ('g;x'     , BASE_URI[0], 'http://a/b/c/g;x'),
    ('g;x?y#s' , BASE_URI[0], 'http://a/b/c/g;x?y#s'),
    # changed with RFC 2396bis
    #(''        , BASE_URI[0], CURRENT_DOC_URI),
    (''        , BASE_URI[0], 'http://a/b/c/d;p?q'),
    ('.'       , BASE_URI[0], 'http://a/b/c/'),
    ('./'      , BASE_URI[0], 'http://a/b/c/'),
    ('..'      , BASE_URI[0], 'http://a/b/'),
    ('../'     , BASE_URI[0], 'http://a/b/'),
    ('../g'    , BASE_URI[0], 'http://a/b/g'),
    ('../..'   , BASE_URI[0], 'http://a/'),
    ('../../'  , BASE_URI[0], 'http://a/'),
    ('../../g' , BASE_URI[0], 'http://a/g'),
    ('../../../g', BASE_URI[0], ('http://a/../g', 'http://a/g')),
    ('../../../../g', BASE_URI[0], ('http://a/../../g', 'http://a/g')),
    # changed with RFC 2396bis
    #('/./g', BASE_URI[0], 'http://a/./g'),
    ('/./g', BASE_URI[0], 'http://a/g'),
    # changed with RFC 2396bis
    #('/../g', BASE_URI[0], 'http://a/../g'),
    ('/../g', BASE_URI[0], 'http://a/g'),
    ('g.', BASE_URI[0], 'http://a/b/c/g.'),
    ('.g', BASE_URI[0], 'http://a/b/c/.g'),
    ('g..', BASE_URI[0], 'http://a/b/c/g..'),
    ('..g', BASE_URI[0], 'http://a/b/c/..g'),
    ('./../g', BASE_URI[0], 'http://a/b/g'),
    ('./g/.', BASE_URI[0], 'http://a/b/c/g/'),
    ('g/./h', BASE_URI[0], 'http://a/b/c/g/h'),
    ('g/../h', BASE_URI[0], 'http://a/b/c/h'),
    ('g;x=1/./y', BASE_URI[0], 'http://a/b/c/g;x=1/y'),
    ('g;x=1/../y', BASE_URI[0], 'http://a/b/c/y'),
    ('g?y/./x', BASE_URI[0], 'http://a/b/c/g?y/./x'),
    ('g?y/../x', BASE_URI[0], 'http://a/b/c/g?y/../x'),
    ('g#s/./x', BASE_URI[0], 'http://a/b/c/g#s/./x'),
    ('g#s/../x', BASE_URI[0], 'http://a/b/c/g#s/../x'),
    ('http:g', BASE_URI[0], ('http:g', 'http://a/b/c/g')),
    ('http:', BASE_URI[0], ('http:', BASE_URI[0])),
    # not sure where this one originated
    ('/a/b/c/./../../g', BASE_URI[0], 'http://a/a/g'),

    # http://gbiv.com/protocols/uri/test/rel_examples2.html
    # slashes in base URI's query args
    ('g'       , BASE_URI[1], 'http://a/b/c/g'),
    ('./g'     , BASE_URI[1], 'http://a/b/c/g'),
    ('g/'      , BASE_URI[1], 'http://a/b/c/g/'),
    ('/g'      , BASE_URI[1], 'http://a/g'),
    ('//g'     , BASE_URI[1], 'http://g'),
    # changed in RFC 2396bis
    #('?y'      , BASE_URI[1], 'http://a/b/c/?y'),
    ('?y'      , BASE_URI[1], 'http://a/b/c/d;p?y'),
    ('g?y'     , BASE_URI[1], 'http://a/b/c/g?y'),
    ('g?y/./x' , BASE_URI[1], 'http://a/b/c/g?y/./x'),
    ('g?y/../x', BASE_URI[1], 'http://a/b/c/g?y/../x'),
    ('g#s'     , BASE_URI[1], 'http://a/b/c/g#s'),
    ('g#s/./x' , BASE_URI[1], 'http://a/b/c/g#s/./x'),
    ('g#s/../x', BASE_URI[1], 'http://a/b/c/g#s/../x'),
    ('./'      , BASE_URI[1], 'http://a/b/c/'),
    ('../'     , BASE_URI[1], 'http://a/b/'),
    ('../g'    , BASE_URI[1], 'http://a/b/g'),
    ('../../'  , BASE_URI[1], 'http://a/'),
    ('../../g' , BASE_URI[1], 'http://a/g'),

    # http://gbiv.com/protocols/uri/test/rel_examples3.html
    # slashes in path params
    # all of these changed in RFC 2396bis
    ('g'       , BASE_URI[2], 'http://a/b/c/d;p=1/g'),
    ('./g'     , BASE_URI[2], 'http://a/b/c/d;p=1/g'),
    ('g/'      , BASE_URI[2], 'http://a/b/c/d;p=1/g/'),
    ('g?y'     , BASE_URI[2], 'http://a/b/c/d;p=1/g?y'),
    (';x'      , BASE_URI[2], 'http://a/b/c/d;p=1/;x'),
    ('g;x'     , BASE_URI[2], 'http://a/b/c/d;p=1/g;x'),
    ('g;x=1/./y', BASE_URI[2], 'http://a/b/c/d;p=1/g;x=1/y'),
    ('g;x=1/../y', BASE_URI[2], 'http://a/b/c/d;p=1/y'),
    ('./'      , BASE_URI[2], 'http://a/b/c/d;p=1/'),
    ('../'     , BASE_URI[2], 'http://a/b/c/'),
    ('../g'    , BASE_URI[2], 'http://a/b/c/g'),
    ('../../'  , BASE_URI[2], 'http://a/b/'),
    ('../../g' , BASE_URI[2], 'http://a/b/g'),

    # http://gbiv.com/protocols/uri/test/rel_examples4.html
    # double and triple slash, unknown scheme
    ('g:h'     , BASE_URI[3], 'g:h'),
    ('g'       , BASE_URI[3], 'fred:///s//a/b/g'),
    ('./g'     , BASE_URI[3], 'fred:///s//a/b/g'),
    ('g/'      , BASE_URI[3], 'fred:///s//a/b/g/'),
    ('/g'      , BASE_URI[3], 'fred:///g'),  # may change to fred:///s//a/g
    ('//g'     , BASE_URI[3], 'fred://g'),   # may change to fred:///s//g
    ('//g/x'   , BASE_URI[3], 'fred://g/x'), # may change to fred:///s//g/x
    ('///g'    , BASE_URI[3], 'fred:///g'),
    ('./'      , BASE_URI[3], 'fred:///s//a/b/'),
    ('../'     , BASE_URI[3], 'fred:///s//a/'),
    ('../g'    , BASE_URI[3], 'fred:///s//a/g'),
    ('../../'  , BASE_URI[3], 'fred:///s//'),    # may change to fred:///s//a/../
    ('../../g' , BASE_URI[3], 'fred:///s//g'),   # may change to fred:///s//a/../g
    ('../../../g', BASE_URI[3], 'fred:///s/g'),  # may change to fred:///s//a/../../g
    ('../../../../g', BASE_URI[3], 'fred:///g'), # may change to fred:///s//a/../../../g

    # http://gbiv.com/protocols/uri/test/rel_examples5.html
    # double and triple slash, well-known scheme
    ('g:h'     , BASE_URI[4], 'g:h'),
    ('g'       , BASE_URI[4], 'http:///s//a/b/g'),
    ('./g'     , BASE_URI[4], 'http:///s//a/b/g'),
    ('g/'      , BASE_URI[4], 'http:///s//a/b/g/'),
    ('/g'      , BASE_URI[4], 'http:///g'),  # may change to http:///s//a/g
    ('//g'     , BASE_URI[4], 'http://g'),   # may change to http:///s//g
    ('//g/x'   , BASE_URI[4], 'http://g/x'), # may change to http:///s//g/x
    ('///g'    , BASE_URI[4], 'http:///g'),
    ('./'      , BASE_URI[4], 'http:///s//a/b/'),
    ('../'     , BASE_URI[4], 'http:///s//a/'),
    ('../g'    , BASE_URI[4], 'http:///s//a/g'),
    ('../../'  , BASE_URI[4], 'http:///s//'),    # may change to http:///s//a/../
    ('../../g' , BASE_URI[4], 'http:///s//g'),   # may change to http:///s//a/../g
    ('../../../g', BASE_URI[4], 'http:///s/g'),  # may change to http:///s//a/../../g
    ('../../../../g', BASE_URI[4], 'http:///g'), # may change to http:///s//a/../../../g

    # from Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py
    ("bar:abc", "foo:xyz", "bar:abc"),
    ('../abc', 'http://example/x/y/z', 'http://example/x/abc'),
    ('http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'),
    ('../r', 'http://ex/x/y/z', 'http://ex/x/r'),
    ('q/r', 'http://ex/x/y', 'http://ex/x/q/r'),
    ('q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'),
    ('q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'),
    ('ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'),
    ('', 'http://ex/x/y', 'http://ex/x/y'),
    ('', 'http://ex/x/y/', 'http://ex/x/y/'),
    ('', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'),
    ('z/', 'http://ex/x/y/', 'http://ex/x/y/z/'),
    ('#Animal', 'file:/swap/test/animal.rdf', 'file:/swap/test/animal.rdf#Animal'),
    ('../abc', 'file:/e/x/y/z', 'file:/e/x/abc'),
    ('/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'),
    ('../r', 'file:/ex/x/y/z', 'file:/ex/x/r'),
    ('/r', 'file:/ex/x/y/z', 'file:/r'),
    ('q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'),
    ('q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'),
    ('q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'),
    ('q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'),
    ('ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'),
    ('', 'file:/ex/x/y', 'file:/ex/x/y'),
    ('', 'file:/ex/x/y/', 'file:/ex/x/y/'),
    ('', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'),
    ('z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'),
    ('file://meetings.example.com/cal#m1', 'file:/devel/WWW/2000/10/swap/test/reluri-1.n3', 'file://meetings.example.com/cal#m1'),
    ('file://meetings.example.com/cal#m1', 'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3', 'file://meetings.example.com/cal#m1'),
    ('./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'),
    ('./#', 'file:/some/dir/foo', 'file:/some/dir/#'),
    # Ryan Lee
    ("./", "http://example/x/abc.efg", "http://example/x/"),

    #
    # Graham Klyne's tests
    # http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls
    # 01-31 are from Connelly's cases
    #
    # 32-49
    ('./q:r', 'http://ex/x/y', 'http://ex/x/q:r'),
    ('./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'),
    ('?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'),
    ('y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'),
    ('local/qual@domain.org#frag', 'mailto:local', 'mailto:local/qual@domain.org#frag'),
    ('more/qual2@domain2.org#frag', 'mailto:local/qual1@domain1.org', 'mailto:local/more/qual2@domain2.org#frag'),
    ('y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'),
    ('/x/y?q', 'http://ex?p', 'http://ex/x/y?q'),
    ('c/d',  'foo:a/b', 'foo:a/c/d'),
    ('/c/d', 'foo:a/b', 'foo:/c/d'),
    ('', 'foo:a/b?c#d', 'foo:a/b?c'),
    ('b/c', 'foo:a', 'foo:b/c'),
    ('../b/c', 'foo:/a/y/z', 'foo:/a/b/c'),
    ('./b/c', 'foo:a', 'foo:b/c'),
    ('/./b/c', 'foo:a', 'foo:/b/c'),
    ('../../d', 'foo://a//b/c', 'foo://a/d'),
    ('.', 'foo:a', 'foo:'),
    ('..', 'foo:a', 'foo:'),
    #
    # 50-57 (cf. TimBL comments --
    #  http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html,
    #  http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html)
    ('abc', 'http://example/x/y%2Fz', 'http://example/x/abc'),
    ('../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'),
    ('../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'),
    ('abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'),
    ('q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'),
    ('/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'),
    ('/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'),
    ('/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'),
    #
    # 70-77
    ('local2@domain2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2'),
    ('local2@domain2?query2', 'mailto:local1@domain1', 'mailto:local2@domain2?query2'),
    ('local2@domain2?query2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2?query2'),
    ('?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'),
    ('local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'),
    ('?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'),
    ('http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'),
    ('http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'),
    #
    # 82-88
    ('http:this', 'http://example.org/base/uri', 'http:this'),
    ('http:this', 'http:base', 'http:this'),
    ('.//g', 'f:/a', 'f://g'),
    ('b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'),
    ('m2@example.ord/c2@example.org', 'mid:m@example.ord/c@example.org', 'mid:m@example.ord/m2@example.ord/c2@example.org'),
    ('mini1.xml', 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/', 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'),
    ('../b/c', 'foo:a/y/z', 'foo:a/b/c'),

]

# Test cases for URI reference syntax ========================================
good_URI_references = [
    'file:///foo/bar',
    'mailto:user@host?subject=blah',
    'dav:', # empty opaque part / rel-path allowed by RFC 2396bis
    'about:', # empty opaque part / rel-path allowed by RFC 2396bis
    #
    # the following test cases are from a Perl script by David A. Wheeler
    # at http://www.dwheeler.com/secure-programs/url.pl
    'http://www.yahoo.com',
    'http://www.yahoo.com/',
    'http://1.2.3.4/',
    'http://www.yahoo.com/stuff',
    'http://www.yahoo.com/stuff/',
    'http://www.yahoo.com/hello%20world/',
    'http://www.yahoo.com?name=obi',
    'http://www.yahoo.com?name=obi+wan&status=jedi',
    'http://www.yahoo.com?onery',
    'http://www.yahoo.com#bottom',
    'http://www.yahoo.com/yelp.html#bottom',
    'https://www.yahoo.com/',
    'ftp://www.yahoo.com/',
    'ftp://www.yahoo.com/hello',
    'demo.txt',
    'demo/hello.txt',
    'demo/hello.txt?query=hello#fragment',
    '/cgi-bin/query?query=hello#fragment',
    '/demo.txt',
    '/hello/demo.txt',
    'hello/demo.txt',
    '/',
    '',
    '#',
    '#here',
    # Wheeler's script says these are invalid, but they aren't
    'http://www.yahoo.com?name=%00%01',
    'http://www.yaho%6f.com',
    'http://www.yahoo.com/hello%00world/',
    'http://www.yahoo.com/hello+world/',
    'http://www.yahoo.com?name=obi&',
    'http://www.yahoo.com?name=obi&type=',
    'http://www.yahoo.com/yelp.html#',
    '//',
    #
    # the following test cases are from a Haskell program by Graham Klyne
    # at http://www.ninebynine.org/Software/HaskellUtils/Network/URITest.hs
    'http://example.org/aaa/bbb#ccc',
    'mailto:local@domain.org',
    'mailto:local@domain.org#frag',
    'HTTP://EXAMPLE.ORG/AAA/BBB#CCC',
    '//example.org/aaa/bbb#ccc',
    '/aaa/bbb#ccc',
    'bbb#ccc',
    '#ccc',
    '#',
    #'/', # repeat of test above
    "A'C",
    #-- escapes
    'http://example.org/aaa%2fbbb#ccc',
    'http://example.org/aaa%2Fbbb#ccc',
    '%2F',
    'aaa%2Fbbb',
    #-- ports
    'http://example.org:80/aaa/bbb#ccc',
    'http://example.org:/aaa/bbb#ccc',
    'http://example.org./aaa/bbb#ccc',
    'http://example.123./aaa/bbb#ccc',
    #-- bare authority
    'http://example.org',
    #-- IPv6 literals (from RFC2732):
    'http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
    'http://[1080:0:0:0:8:800:200C:417A]/index.html',
    'http://[3ffe:2a00:100:7031::1]',
    'http://[1080::8:800:200C:417A]/foo',
    'http://[::192.9.5.5]/ipng',
    'http://[::FFFF:129.144.52.38]:80/index.html',
    'http://[2010:836B:4179::836B:4179]',
    '//[2010:836B:4179::836B:4179]',
    #-- Random other things that crop up
    'http://example/Andr&#567;',
    'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/',
    ]

bad_URI_references = [
    'beepbeep\x07\x07',
    '\n',
    '::', # not OK, per Roy Fielding on the W3C uri list on 2004-04-01
    #
    # the following test cases are from a Perl script by David A. Wheeler
    # at http://www.dwheeler.com/secure-programs/url.pl
    'http://www yahoo.com',
    'http://www.yahoo.com/hello world/',
    'http://www.yahoo.com/yelp.html#"',
    #
    # the following test cases are from a Haskell program by Graham Klyne
    # at http://www.ninebynine.org/Software/HaskellUtils/Network/URITest.hs
    '[2010:836B:4179::836B:4179]',
    ' ',
    '%',
    'A%Z',
    '%ZZ',
    '%AZ',
    'A C',
    r"A\'C",
    'A`C',
    'A<C',
    'A>C',
    'A^C',
    r'A\\C',
    'A{C',
    'A|C',
    'A}C',
    'A[C',
    'A]C',
    'A[**]C',
    'http://[xyz]/',
    'http://]/',
    'http://example.org/[2010:836B:4179::836B:4179]',
    'http://example.org/abc#[2010:836B:4179::836B:4179]',
    'http://example.org/xxx/[qwerty]#a[b]',
    #
    # from a post to the W3C uri list on 2004-02-17
    'http://w3c.org:80path1/path2',
    ]

# Test cases for UriToOsPath =================================================
#
# Each tuple is (URI, expected for Windows, expected for POSIX).
# None means a UriException is expected.
fileUris = [
    ('file:x/y/z',         r'x\y\z',       'x/y/z'),
    ('file:/x/y/z',        r'\x\y\z',      '/x/y/z'),
    ('file:/x/y/z/',       '\\x\\y\\z\\',  '/x/y/z/'),
    ('file:///x/y/z',      r'\x\y\z',      '/x/y/z'),
    ('file:///x/y/z/',     '\\x\\y\\z\\',  '/x/y/z/'),
    ('file:///x/y/z?q1=1&q2=2', r'\x\y\z', '/x/y/z'),
    ('file:///x/y/z#frag', r'\x\y\z',      '/x/y/z'),
    ('file:///c:/x/y/z',   r'C:\x\y\z',    '/c:/x/y/z'),
    ('file:///c|/x/y/z',   r'C:\x\y\z',    '/c|/x/y/z'),
    ('file:///c:/x:/y/z',  r'C:\x:\y\z',   '/c:/x:/y/z'),
    ('file://c:/x/y/z',    r'C:\x\y\z',    None),
    ('file://host/share/x/y/z', r'\\host\share\x\y\z', None),
    ('file:////host/share/x/y/z', r'\\host\share\x\y\z', '//host/share/x/y/z'),
    ('file://host/c:/x/y/z', r'\\host\c:\x\y\z', None),
    ('file://localhost/x/y/z', r'\x\y\z',  '/x/y/z'),
    ('file://localhost/c:/x/y/z', r'C:\x\y\z', '/c:/x/y/z'),
    ('file:///C:%5Cx%5Cy%5Cz', r'C:\x\y\z', r'/C:\x\y\z'),
    ('file:///C:%2Fx%2Fy%2Fz', r'C:\x\y\z', r'/C:\/x\/y\/z'),
    ('file:///water%3D%E6%B0%B4', '\\water=\xe6\xb0\xb4', '/water=\xe6\xb0\xb4'),
    (u'file:///water%3D%E6%B0%B4', u'\\water=\u6c34', u'/water=\u6c34'),
    ]

# Test cases for OsPathToUri =================================================
#
# Each tuple is (OS path, expected for Windows, expected for POSIX).
# None means a UriException is expected.

if Uri.WINDOWS_SLASH_COMPAT:
    # if treating '/' as alias for '\' on Windows
    filePaths = [
        # relative path
        ('x/y/z',     'file:x/y/z',              'file:x/y/z'),
        # absolute path
        ('/x/y/z',    'file:///x/y/z',           'file:///x/y/z'),
        # authority + absolute path
        ('//x/y/z',   'file://x/y/z',            'file:////x/y/z'),
        # authority + absolute path with extra leading slashes
        ('///x/y/z',  'file:///x/y/z',           'file://///x/y/z'),
    ]
else:
    # if NOT treating '/' as alias for '\' on Windows
    filePaths = [
        ('x/y/z',     'file:x%2Fy%2Fz',          'file:x/y/z'),
        ('/x/y/z',    'file:%2Fx%2Fy%2Fz',       'file:///x/y/z'),
        ('//x/y/z',   'file:%2F%2Fx%2Fy%2Fz',    'file:////x/y/z'),
        ('///x/y/z',  'file:%2F%2F%2Fx%2Fy%2Fz', 'file://///x/y/z'),
    ]

filePaths += [
    # absolute windows path, with drivespec
    ('C:\\',      'file:///C:/',             'file:C%3A%5C'),
    (r'C:\a\b\c', 'file:///C:/a/b/c',        'file:C%3A%5Ca%5Cb%5Cc'),
    # absolute windows path, no drivespec
    (r'\a\b\c',   'file:///a/b/c',           'file:%5Ca%5Cb%5Cc'),
    # relative windows path
    (r'a\b\c',    'file:a/b/c',              'file:a%5Cb%5Cc'),
    # absolute windows path with dot segments
    (r'C:\a\b\c\d\..\..\C', 'file:///C:/a/b/C',
     'file:C%3A%5Ca%5Cb%5Cc%5Cd%5C..%5C..%5CC'),
    # windows path with space chars
    (r'C:\Documents and Settings\Your Name\Local Settings\Temp',
     'file:///C:/Documents%20and%20Settings/Your%20Name/Local%20Settings/Temp',
     'file:C%3A%5CDocuments%20and%20Settings%5CYour%20Name%5CLocal%20Settings%5CTemp'),
    # relative windows path with dot segments
    ('.',         'file:.',                  'file:.'),
    ('..\Y\Z',    'file:../Y/Z',             'file:..%5CY%5CZ'),
    # reserved-character bytes in path
    ('a!;b?c#d^e()', 'file:a%21%3Bb%3Fc%23d%5Ee%28%29',
     'file:a%21%3Bb%3Fc%23d%5Ee%28%29'),
    # non-ASCII characters in path
    (u'98.6\xb0F', 'file:98.6%C2%B0F',       'file:98.6%C2%B0F'),
    (u'water=\u6c34', 'file:water%3D%E6%B0%B4', 'file:water%3D%E6%B0%B4'),
    # UNC path
    (r'\\h\s\x\y\z', 'file://h/s/x/y/z',     'file:%5C%5Ch%5Cs%5Cx%5Cy%5Cz'),
    (r'\\h\$c$\x\y\z', 'file://h/%24c%24/x/y/z',
     'file:%5C%5Ch%5C%24c%24%5Cx%5Cy%5Cz'),
    (r'\\localhost\share\x\y\z', 'file://localhost/share/x/y/z',
     'file:%5C%5Clocalhost%5Cshare%5Cx%5Cy%5Cz'),
    ]


# Test cases for NormalizeCase ===============================================
#
# Each tuple is (URI, expected w/o normalizing host, expected with norm. host)
caseNormalizationTests = [
    ('HTTP://www.EXAMPLE.com/', 'http://www.EXAMPLE.com/', 'http://www.example.com/'),
    ('example://A/b/c/%7bfoo%7d', 'example://A/b/c/%7Bfoo%7D', 'example://a/b/c/%7Bfoo%7D'),
    ]


# Test cases for NormalizePercentEncoding ====================================
#
# Each tuple is (URI, expected)
pctEncNormalizationTests = [
    ('http://host/%7Euser/x/y/z', 'http://host/~user/x/y/z'),
    ('http://host/%7euser/x/y/z', 'http://host/~user/x/y/z'),
    ('example://A/b/c/%7bfoo%7d', 'example://A/b/c/%7bfoo%7d'),
    ]


# Test cases for NormalizePathSegments =======================================
#
# Each tuple is (path, expected)
pathSegmentNormalizationTests = [
    ('/a/b/../../c', '/c'),
    ('a/b/../../c', 'a/b/../../c'),
    ('/a/b/././c', '/a/b/c'),
    ('a/b/././c', 'a/b/././c'),
    ('/a/b/../c/././d', '/a/c/d'),
    ('a/b/../c/././d', 'a/b/../c/././d'),
    ]


# Test cases for MakeUrllibSafe ==============================================
#
if sys.version_info[0:2] < (2,3):
    # IDNA unsupported on 2.2, so there aren't many tests we can do
    makeUrllibSafeTests = [
        ('http://www.w%33.org', 'http://www.w3.org'),
    ]
else:
    # Each tuple is (URI, expected)
    makeUrllibSafeTests = [
        # Martin Duerst's IDN tests in http://www.w3.org/2004/04/uri-rel-test.html
        ('http://www.w%33.org', 'http://www.w3.org'), # 101
        ('http://r%C3%A4ksm%C3%B6rg%C3%A5s.josefsson.org', 'http://xn--rksmrgs-5wao1o.josefsson.org'), # 111
        ('http://%E7%B4%8D%E8%B1%86.w3.mag.keio.ac.jp', 'http://xn--99zt52a.w3.mag.keio.ac.jp'), # 112
        ('http://www.%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81'
         '%8C%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81'
         '%AA%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81'
         '%AE%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81'
         '%8F%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81'
         '%84.w3.mag.keio.ac.jp/',
         'http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lr'
         'a.w3.mag.keio.ac.jp/'), # 121
        ('http://%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81%8C'
         '%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA'
         '%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81%AE'
         '%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81%8F'
         '%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81%84'
         '.%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81%8C%E3%81'
         '%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81'
         '%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81%AE%E3%82'
         '%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81%8F%E3%81'
         '%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81%84.%E3'
         '%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81%8C%E3%81%84%E3'
         '%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84%E3'
         '%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81%AE%E3%82%89%E3'
         '%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81%8F%E3%81%97%E3'
         '%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81%84.w3.mag.keio'
         '.ac.jp/',
         'http://xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lra.xn'
         '--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lra.xn--n8jaaaa'
         'ai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp/'), #122
        # Unicode versions of above
        (u'http://www.w%33.org', u'http://www.w3.org'), # 101
        (u'http://r%C3%A4ksm%C3%B6rg%C3%A5s.josefsson.org', u'http://xn--rksmrgs-5wao1o.josefsson.org'), # 111
        (u'http://%E7%B4%8D%E8%B1%86.w3.mag.keio.ac.jp', u'http://xn--99zt52a.w3.mag.keio.ac.jp'), # 112
        (u'http://www.%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81'
         u'%8C%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81'
         u'%AA%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81'
         u'%AE%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81'
         u'%8F%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81'
         u'%84.w3.mag.keio.ac.jp/',
         u'http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lr'
         u'a.w3.mag.keio.ac.jp/'), # 121
        (u'http://%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81%8C'
         u'%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA'
         u'%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81%AE'
         u'%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81%8F'
         u'%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81%84'
         u'.%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81%8C%E3%81'
         u'%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81'
         u'%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81%AE%E3%82'
         u'%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81%8F%E3%81'
         u'%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81%84.%E3'
         u'%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81%8C%E3%81%84%E3'
         u'%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84%E3'
         u'%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81%AE%E3%82%89%E3'
         u'%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81%8F%E3%81%97%E3'
         u'%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81%84.w3.mag.keio'
         u'.ac.jp/',
         u'http://xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lra.xn'
         u'--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lra.xn--n8jaaaa'
         u'ai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp/'), #122
        ]

winMakeUrllibSafeTests = [
    ('file:///C:/path/to/file', 'file:///C|/path/to/file'),
    ('http://foo/bar:baz/', 'http://foo/bar:baz/'),
    ]


# Test cases for PublicIdToUrn and UrnToPublicId =============================
#
publicIdTests = [
    # examples from RFC 3151
    ("ISO/IEC 10179:1996//DTD DSSSL Architecture//EN",
     "urn:publicid:ISO%2FIEC+10179%3A1996:DTD+DSSSL+Architecture:EN"),
    ("ISO 8879:1986//ENTITIES Added Latin 1//EN",
     "urn:publicid:ISO+8879%3A1986:ENTITIES+Added+Latin+1:EN"),
    ("-//OASIS//DTD DocBook XML V4.1.2//EN",
     "urn:publicid:-:OASIS:DTD+DocBook+XML+V4.1.2:EN"),
    ("+//IDN example.org//DTD XML Bookmarks 1.0//EN//XML",
     "urn:publicid:%2B:IDN+example.org:DTD+XML+Bookmarks+1.0:EN:XML"),
    ("-//ArborText::prod//DTD Help Document::19970708//EN",
     "urn:publicid:-:ArborText;prod:DTD+Help+Document;19970708:EN"),
    ("foo",
     "urn:publicid:foo"),
    ("3+3=6",
     #"urn:publicid:3%2B3=6" # RFC 2396
     "urn:publicid:3%2B3%3D6"), # RFC 2396bis
    ("-//Acme, Inc.//DTD Book Version 1.0",
     #"urn:publicid:-:Acme,+Inc.:DTD+Book+Version+1.0" # RFC 2396
     "urn:publicid:-:Acme%2C+Inc.:DTD+Book+Version+1.0"), # RFC 2396bis
]

# Test cases for PercentEncode and PercentDecode =============================
#
percentEncodeTests = [
    # Empty string
    ('', ''),
    (u'', u''),
    # The unreserved set: the only characters that don't need to be
    # percent-encoded (it's OK to percent-encode them, though, as it
    # will still produce an equivalent URI). We expect that they won't
    # be percent-encoded.
    ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~',
     'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'),
    (u'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~',
     u'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'),
    # The reserved set: characters that need to be percent-encoded when they
    # are not being used for their reserved purpose. We expect that they
    # will all be percent-encoded.
    (":/?#[]@!$&'()*+,;=",
     '%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D'),
    (u":/?#[]@!$&'()*+,;=",
     u'%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D'),
    # Disallowed characters: characters not in the reserved or unreserved
    # sets; these must be converted to percent-encoded octets, always.
    # Basically this is all of Unicode, which would be impractical to test.
    # We'll just test some reasonable subsets.
    #
    #   1. the rest of printable ASCII
    (' %^{}|\\"<>`',
     '%20%25%5E%7B%7D%7C%5C%22%3C%3E%60'),
    (u' %^{}|\\"<>`',
     u'%20%25%5E%7B%7D%7C%5C%22%3C%3E%60'),
    #   2. the ASCII / C0 control set
    (''.join(map(chr, xrange(32))) + '\x7f',
     '%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F' \
     '%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F'),
    (u''.join(map(unichr, xrange(32))) + u'\x7f',
     u'%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F' \
     u'%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F'),
    #   3. non-ASCII (ISO-8859-* range)
    (''.join(map(chr, xrange(128, 256))),
     '%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F' \
     '%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F' \
     '%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF' \
     '%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF' \
     '%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF' \
     '%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF' \
     '%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF' \
     '%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF'),
    (u''.join(map(unichr, xrange(128, 256))),
     u'%C2%80%C2%81%C2%82%C2%83%C2%84%C2%85%C2%86%C2%87' \
     u'%C2%88%C2%89%C2%8A%C2%8B%C2%8C%C2%8D%C2%8E%C2%8F' \
     u'%C2%90%C2%91%C2%92%C2%93%C2%94%C2%95%C2%96%C2%97' \
     u'%C2%98%C2%99%C2%9A%C2%9B%C2%9C%C2%9D%C2%9E%C2%9F' \
     u'%C2%A0%C2%A1%C2%A2%C2%A3%C2%A4%C2%A5%C2%A6%C2%A7' \
     u'%C2%A8%C2%A9%C2%AA%C2%AB%C2%AC%C2%AD%C2%AE%C2%AF' \
     u'%C2%B0%C2%B1%C2%B2%C2%B3%C2%B4%C2%B5%C2%B6%C2%B7' \
     u'%C2%B8%C2%B9%C2%BA%C2%BB%C2%BC%C2%BD%C2%BE%C2%BF' \
     u'%C3%80%C3%81%C3%82%C3%83%C3%84%C3%85%C3%86%C3%87' \
     u'%C3%88%C3%89%C3%8A%C3%8B%C3%8C%C3%8D%C3%8E%C3%8F' \
     u'%C3%90%C3%91%C3%92%C3%93%C3%94%C3%95%C3%96%C3%97' \
     u'%C3%98%C3%99%C3%9A%C3%9B%C3%9C%C3%9D%C3%9E%C3%9F' \
     u'%C3%A0%C3%A1%C3%A2%C3%A3%C3%A4%C3%A5%C3%A6%C3%A7' \
     u'%C3%A8%C3%A9%C3%AA%C3%AB%C3%AC%C3%AD%C3%AE%C3%AF' \
     u'%C3%B0%C3%B1%C3%B2%C3%B3%C3%B4%C3%B5%C3%B6%C3%B7' \
     u'%C3%B8%C3%B9%C3%BA%C3%BB%C3%BC%C3%BD%C3%BE%C3%BF'),
    #   4. U+0100 to low surrogate range, in big steps
    (u''.join(map(unichr, xrange(256, 55296, 1024))) + u'\ud7ff',
     u'%C4%80%D4%80%E0%A4%80%E0%B4%80%E1%84%80%E1%94%80' \
     u'%E1%A4%80%E1%B4%80%E2%84%80%E2%94%80%E2%A4%80%E2' \
     u'%B4%80%E3%84%80%E3%94%80%E3%A4%80%E3%B4%80%E4%84' \
     u'%80%E4%94%80%E4%A4%80%E4%B4%80%E5%84%80%E5%94%80' \
     u'%E5%A4%80%E5%B4%80%E6%84%80%E6%94%80%E6%A4%80%E6' \
     u'%B4%80%E7%84%80%E7%94%80%E7%A4%80%E7%B4%80%E8%84' \
     u'%80%E8%94%80%E8%A4%80%E8%B4%80%E9%84%80%E9%94%80' \
     u'%E9%A4%80%E9%B4%80%EA%84%80%EA%94%80%EA%A4%80%EA' \
     u'%B4%80%EB%84%80%EB%94%80%EB%A4%80%EB%B4%80%EC%84' \
     u'%80%EC%94%80%EC%A4%80%EC%B4%80%ED%84%80%ED%94%80%ED%9F%BF'),
    #  5. U+E000 through U+FFFD, in big steps
    (u''.join(map(unichr, xrange(57344, 65534, 256))) + u'\ufffd',
     u'%EE%80%80%EE%84%80%EE%88%80%EE%8C%80%EE%90%80%EE' \
     u'%94%80%EE%98%80%EE%9C%80%EE%A0%80%EE%A4%80%EE%A8' \
     u'%80%EE%AC%80%EE%B0%80%EE%B4%80%EE%B8%80%EE%BC%80' \
     u'%EF%80%80%EF%84%80%EF%88%80%EF%8C%80%EF%90%80%EF' \
     u'%94%80%EF%98%80%EF%9C%80%EF%A0%80%EF%A4%80%EF%A8' \
     u'%80%EF%AC%80%EF%B0%80%EF%B4%80%EF%B8%80%EF%BC%80%EF%BF%BD'),
    #  6. a couple of random chars from U+10000 to U+10FFFD,
    #     presented as surrogate pairs, then as whatever the
    #     native representation is (full, wide chars if
    #     Python is so compiled; surrogate pairs otherwise)
    (u'\ud800\udc00\U00010000\udbff\udffd\U0010FFFD',
     u'%F0%90%80%80%F0%90%80%80%F4%8F%BF%BD%F4%8F%BF%BD'),
]

def Test(tester):
    tester.startGroup('UriDict')
    tester.startGroup('file:/// and file://localhost/ equivalence')
    tester.startTest('equivalent key in UriDict')
    uris = Uri.UriDict()
    uris['file:///path/to/resource'] = 0
    tester.compare(True, 'file://localhost/path/to/resource' in uris, 'RFC 1738 localhost support failed')
    tester.testDone()
    tester.startTest('value of 2 equivalent keys')
    uris = Uri.UriDict()
    uris['file:///path/to/resource'] = 1
    uris['file://localhost/path/to/resource'] = 2
    tester.compare(2, uris['file:///path/to/resource'], 'RFC 1738 localhost support failed')
    tester.testDone()
    tester.groupDone()
    tester.startGroup('case equivalence')
    for uri, expected, junk in caseNormalizationTests:
        tester.startTest('%s and %s equivalence' % (uri, expected))
        uris[uri] = 1
        uris[expected] = 2
        tester.compare(2, uris[uri])
        tester.testDone()
    tester.groupDone()
    tester.startGroup('percent-encoding equivalence')
    for uri, expected in pctEncNormalizationTests:
        tester.startTest('%s and %s equivalence' % (uri, expected))
        uris[uri] = 1
        uris[expected] = 2
        tester.compare(2, uris[uri])
        tester.testDone()
    tester.groupDone()
    tester.groupDone()

    tester.startGroup("PercentEncode and PercentDecode")
    for unencoded, encoded in percentEncodeTests:
        if len(unencoded) > 10:
            test_title = unencoded[:11] + '...'
        else:
            test_title = unencoded
        tester.startTest(repr(test_title))
        tester.compare(encoded, Uri.PercentEncode(unencoded))
        tester.compare(unencoded, Uri.PercentDecode(encoded))
        tester.testDone()

    # test a few iso-8859-n variations just to make sure
    # iso-8859-1 isn't special
    unencoded = ''.join(map(chr, range(256)))
    encoded = '%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F' \
              '%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F' \
              '%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F' \
              '0123456789%3A%3B%3C%3D%3E%3F%40' \
              'ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60' \
              'abcdefghijklmnopqrstuvwxyz%7B%7C%7D~' \
              '%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F' \
              '%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F' \
              '%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF' \
              '%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF' \
              '%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF' \
              '%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF' \
              '%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF' \
              '%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF'
    for part in (1,2,3,15):
        enc_name = 'iso-8859-%d' % part
        tester.startTest(enc_name)
        try:
            codecs.lookup(enc_name)
        except LookupError:
            tester.warning('Not supported on this platform')
            tester.testDone()
            continue
        tester.compare(encoded, Uri.PercentEncode(unencoded, encoding=enc_name))
        tester.compare(unencoded, Uri.PercentDecode(encoded, encoding=enc_name))
        tester.testDone()

    # utf-16be: why not?
    unencoded = u'a test string...\x00\xe9...\x20\x22...\xd8\x00\xdc\x00'
    encoded = u'a%20test%20string...\u00e9...%20%22...%D8%00%DC%00'

    tester.groupDone()

    tester.startGroup("PublicIdToUrn")
    for publicid, urn in publicIdTests:
        tester.startTest(publicid)
        tester.compare(urn, Uri.PublicIdToUrn(publicid))
        tester.testDone()
    tester.groupDone()

    tester.startGroup("UrnToPublicId")
    for publicid, urn in publicIdTests:
        tester.startTest(urn)
        tester.compare(publicid, Uri.UrnToPublicId(urn))
        tester.testDone()
    tester.groupDone()

    tester.startGroup("URI reference syntax")
    for testuri in good_URI_references:
        tester.startTest("Good URI ref: %s" % repr(testuri))
        tester.compare(1, Uri.MatchesUriRefSyntax(testuri), "Mistakenly tests as invalid")
        tester.testDone()
    for testuri in bad_URI_references:
        tester.startTest("Bad URI ref: %s" % repr(testuri))
        tester.compare(0, Uri.MatchesUriRefSyntax(testuri), "Mistakenly tests as valid")
        tester.testDone()
    tester.groupDone()

    tester.startGroup('Absolutize')
    for uriRef, baseUri, expectedUri in absolutize_test_cases:
        tester.startTest('base=%r ref=%r' % (baseUri, uriRef))
        res = Uri.Absolutize(uriRef, baseUri)
        # in a couple cases, there's more than one correct result
        if isinstance(expectedUri, tuple):
            tester.compare(1, res in expectedUri, 'Invalid result')
        else:
            tester.compare(expectedUri, res, 'Invalid result')
        tester.testDone()
    tester.groupDone()

    tester.startGroup('BaseJoin')
    for base, relative, expectedUri in basejoin_test_cases:
        tester.startTest('base=%r rel=%r' % (base,relative))
        res = Uri.BaseJoin(base, relative)
        tester.compare(expectedUri, res, 'Invalid result')
        tester.testDone()
    tester.groupDone()

    tester.startGroup('UriToOsPath')
    for osname in ('posix', 'nt'):
        tester.startGroup(osname)
        for subgroupname in ('absolute', 'relative'):
            tester.startGroup(subgroupname)
            for uri, nt_path, posix_path in fileUris:
                if subgroupname == 'relative':
                    if uri[:5] == 'file:':
                        uri = uri[5:]
                    else:
                        break
                if isinstance(uri, unicode):
                    testname = repr(uri)
                else:
                    testname = uri
                tester.startTest(testname)
                if osname == 'nt':
                    path = nt_path
                elif osname == 'posix':
                    path = posix_path
                else:
                    break
                if path is None:
                     tester.testException(Uri.UriToOsPath, (uri,),
                                          Uri.UriException, kwargs={'attemptAbsolute':False, 'osname': osname})
                else:
                    tester.compare(path, Uri.UriToOsPath(uri, attemptAbsolute=False, osname=osname))
                tester.testDone()
            tester.groupDone()
        tester.groupDone()
    tester.groupDone()

    tester.startGroup('OsPathToUri')
    for osname in ('posix', 'nt'):
        tester.startGroup(osname)
        for path, nt_uri, posix_uri in filePaths:
            if isinstance(path, unicode):
                testname = repr(path)
            else:
                testname = path
            tester.startTest(testname)
            if osname == 'nt':
                uri = nt_uri
            elif osname == 'posix':
                uri = posix_uri
            else:
                break
            if uri is None:
                 tester.testException(Uri.OsPathToUri, (path,),
                                      Uri.UriException, kwargs={'attemptAbsolute': False, 'osname': osname})
            else:
                tester.compare(uri, Uri.OsPathToUri(path, attemptAbsolute=False, osname=osname))
            tester.testDone()
        tester.groupDone()
    tester.groupDone()

    tester.startGroup('NormalizeCase')
    for uri, expected0, expected1 in caseNormalizationTests:
        testname = uri
        uri = Uri.SplitUriRef(uri)
        tester.startTest(testname)
        tester.compare(expected0, Uri.UnsplitUriRef(Uri.NormalizeCase(uri)))
        tester.testDone()
        tester.startTest(testname + ' (host too)')
        tester.compare(expected1, Uri.UnsplitUriRef(Uri.NormalizeCase(uri, doHost=1)))
        tester.testDone()
    tester.groupDone()

    tester.startGroup('NormalizePercentEncoding')
    for uri, expected in pctEncNormalizationTests:
        testname = uri
        tester.startTest(testname)
        tester.compare(expected, Uri.NormalizePercentEncoding(uri))
        tester.testDone()
    tester.groupDone()

    tester.startGroup('NormalizePathSegments')
    for path, expected in pathSegmentNormalizationTests:
        testname = path
        tester.startTest(testname)
        tester.compare(expected, Uri.NormalizePathSegments(path))
        tester.testDone()
    tester.groupDone()

    tester.startGroup('NormalizePathSegmentsInUri')
    for path, expectedpath in pathSegmentNormalizationTests:
        # for non-hierarchical scheme, no change expected in every case
        uri = 'urn:bogus:%s?a=1&b=2#frag' % path
        expected = 'urn:bogus:%s?a=1&b=2#frag' % path
        testname = uri
        tester.startTest(testname)
        tester.compare(expected, Uri.NormalizePathSegmentsInUri(uri))
        tester.testDone()
    for path, expectedpath in pathSegmentNormalizationTests:
        if path[:1] == '/':
            # hierarchical scheme
            uri = 'file://root:changeme@host%s?a=1&b=2#frag' % path
            expected = 'file://root:changeme@host%s?a=1&b=2#frag' % expectedpath
            testname = uri
            tester.startTest(testname)
            tester.compare(expected, Uri.NormalizePathSegmentsInUri(uri))
            tester.testDone()
    tester.groupDone()

    tester.startGroup("MakeUrllibSafe")
    tests = makeUrllibSafeTests
    if os.name == 'nt':
        tests += winMakeUrllibSafeTests
    for uri, expected in makeUrllibSafeTests:
        if isinstance(uri, unicode):
            test_title = repr(uri)
        else:
            test_title = uri
        tester.startTest(test_title)
        res = Uri.MakeUrllibSafe(uri)
        tester.compare(expected, res)
        tester.testDone()
    tester.groupDone()

    tester.startGroup("Basic Uri Resolver")
    data = [('http://foo.com/root/', 'path', 'http://foo.com/root/path'),
            ('http://foo.com/root',  'path', 'http://foo.com/path'),
            ]
    for base,uri,exp in data:
        tester.startTest("normalize: %s %s" % (base, uri))
        res = Uri.BASIC_RESOLVER.normalize(uri, base)
        tester.compare(exp, res)
        tester.testDone()

    base = 'foo:foo.com'
    uri = 'path'
    tester.startTest("normalize: %s %s" % (base, uri))
    tester.testException(Uri.BASIC_RESOLVER.normalize, (uri, base), Uri.UriException)
    tester.testDone()

    tester.startTest('resolve')
    base = os.getcwd()
    if base[-1] != os.sep:
        base += os.sep
    stream = Uri.BASIC_RESOLVER.resolve('test.py', Uri.OsPathToUri(base))
    tester.compare(TEST_DOT_PY_BANGPATH, string.rstrip(stream.readline()))
    stream.close()
    tester.testDone()

    tester.startTest('generate')
    uuid = Uri.BASIC_RESOLVER.generate()
    tester.compare('urn:uuid:', uuid[:9])

    tester.testDone()

    tester.groupDone()

    tester.startGroup("SchemeRegistryResolver")

    def evalSchemeHandler(uri, base=None):
        if base: uri = base+uri
        uri = uri[5:]
        return str(eval(uri))

    def shiftSchemeHandler(uri, base=None):
        if base: uri = base+uri
        uri = uri[6:]
        return ''.join([ chr(ord(c)+1) for c in uri])

    resolver = SchemeRegistryResolver({'eval': evalSchemeHandler,
                                       'shift': shiftSchemeHandler,
                                      })

    scheme_cases = [(None, 'eval:150-50', '100'),
            (None, 'shift:abcde', 'bcdef'),
            ('eval:150-', '50', '100'),
            ('shift:ab', 'cde', 'bcdef'),
        ]

    for base, relative, expected in scheme_cases:
        tester.startTest("URI: base=%s uri=%s" % (base, relative))

        res = resolver.resolve(relative, base)
        tester.compare(expected, res)

        tester.testDone()

    resolver.handlers[None] = shiftSchemeHandler
    del resolver.handlers['shift']

    for base, relative, expected in scheme_cases:
        tester.startTest("URI: base=%s uri=%s" % (base, relative))

        res = resolver.resolve(relative, base)
        tester.compare(expected, res)

        tester.testDone()

    tester.groupDone()

    return


if __name__ == '__main__':
    from Ft.Lib.TestSuite import Tester
    tester = Tester.Tester()
    Test(tester)

