Home | History | Annotate | Download | only in test
      1 """Regresssion tests for urllib"""
      2 
      3 import urllib
      4 import httplib
      5 import unittest
      6 import os
      7 import sys
      8 import mimetools
      9 import tempfile
     10 import StringIO
     11 
     12 from test import test_support
     13 from base64 import b64encode
     14 
     15 
     16 def hexescape(char):
     17     """Escape char as RFC 2396 specifies"""
     18     hex_repr = hex(ord(char))[2:].upper()
     19     if len(hex_repr) == 1:
     20         hex_repr = "0%s" % hex_repr
     21     return "%" + hex_repr
     22 
     23 
     24 class FakeHTTPMixin(object):
     25     def fakehttp(self, fakedata):
     26         class FakeSocket(StringIO.StringIO):
     27 
     28             def sendall(self, data):
     29                 FakeHTTPConnection.buf = data
     30 
     31             def makefile(self, *args, **kwds):
     32                 return self
     33 
     34             def read(self, amt=None):
     35                 if self.closed:
     36                     return ""
     37                 return StringIO.StringIO.read(self, amt)
     38 
     39             def readline(self, length=None):
     40                 if self.closed:
     41                     return ""
     42                 return StringIO.StringIO.readline(self, length)
     43 
     44         class FakeHTTPConnection(httplib.HTTPConnection):
     45 
     46             # buffer to store data for verification in urlopen tests.
     47             buf = ""
     48 
     49             def connect(self):
     50                 self.sock = FakeSocket(fakedata)
     51 
     52         assert httplib.HTTP._connection_class == httplib.HTTPConnection
     53 
     54         httplib.HTTP._connection_class = FakeHTTPConnection
     55 
     56     def unfakehttp(self):
     57         httplib.HTTP._connection_class = httplib.HTTPConnection
     58 
     59 
     60 class urlopen_FileTests(unittest.TestCase):
     61     """Test urlopen() opening a temporary file.
     62 
     63     Try to test as much functionality as possible so as to cut down on reliance
     64     on connecting to the Net for testing.
     65 
     66     """
     67 
     68     def setUp(self):
     69         """Setup of a temp file to use for testing"""
     70         self.text = "test_urllib: %s\n" % self.__class__.__name__
     71         FILE = file(test_support.TESTFN, 'wb')
     72         try:
     73             FILE.write(self.text)
     74         finally:
     75             FILE.close()
     76         self.pathname = test_support.TESTFN
     77         self.returned_obj = urllib.urlopen("file:%s" % self.pathname)
     78 
     79     def tearDown(self):
     80         """Shut down the open object"""
     81         self.returned_obj.close()
     82         os.remove(test_support.TESTFN)
     83 
     84     def test_interface(self):
     85         # Make sure object returned by urlopen() has the specified methods
     86         for attr in ("read", "readline", "readlines", "fileno",
     87                      "close", "info", "geturl", "getcode", "__iter__"):
     88             self.assertTrue(hasattr(self.returned_obj, attr),
     89                          "object returned by urlopen() lacks %s attribute" %
     90                          attr)
     91 
     92     def test_read(self):
     93         self.assertEqual(self.text, self.returned_obj.read())
     94 
     95     def test_readline(self):
     96         self.assertEqual(self.text, self.returned_obj.readline())
     97         self.assertEqual('', self.returned_obj.readline(),
     98                          "calling readline() after exhausting the file did not"
     99                          " return an empty string")
    100 
    101     def test_readlines(self):
    102         lines_list = self.returned_obj.readlines()
    103         self.assertEqual(len(lines_list), 1,
    104                          "readlines() returned the wrong number of lines")
    105         self.assertEqual(lines_list[0], self.text,
    106                          "readlines() returned improper text")
    107 
    108     def test_fileno(self):
    109         file_num = self.returned_obj.fileno()
    110         self.assertIsInstance(file_num, int, "fileno() did not return an int")
    111         self.assertEqual(os.read(file_num, len(self.text)), self.text,
    112                          "Reading on the file descriptor returned by fileno() "
    113                          "did not return the expected text")
    114 
    115     def test_close(self):
    116         # Test close() by calling it hear and then having it be called again
    117         # by the tearDown() method for the test
    118         self.returned_obj.close()
    119 
    120     def test_info(self):
    121         self.assertIsInstance(self.returned_obj.info(), mimetools.Message)
    122 
    123     def test_geturl(self):
    124         self.assertEqual(self.returned_obj.geturl(), self.pathname)
    125 
    126     def test_getcode(self):
    127         self.assertEqual(self.returned_obj.getcode(), None)
    128 
    129     def test_iter(self):
    130         # Test iterator
    131         # Don't need to count number of iterations since test would fail the
    132         # instant it returned anything beyond the first line from the
    133         # comparison
    134         for line in self.returned_obj.__iter__():
    135             self.assertEqual(line, self.text)
    136 
    137     def test_relativelocalfile(self):
    138         self.assertRaises(ValueError,urllib.urlopen,'./' + self.pathname)
    139 
    140 class ProxyTests(unittest.TestCase):
    141 
    142     def setUp(self):
    143         # Records changes to env vars
    144         self.env = test_support.EnvironmentVarGuard()
    145         # Delete all proxy related env vars
    146         for k in os.environ.keys():
    147             if 'proxy' in k.lower():
    148                 self.env.unset(k)
    149 
    150     def tearDown(self):
    151         # Restore all proxy related env vars
    152         self.env.__exit__()
    153         del self.env
    154 
    155     def test_getproxies_environment_keep_no_proxies(self):
    156         self.env.set('NO_PROXY', 'localhost')
    157         proxies = urllib.getproxies_environment()
    158         # getproxies_environment use lowered case truncated (no '_proxy') keys
    159         self.assertEqual('localhost', proxies['no'])
    160         # List of no_proxies with space.
    161         self.env.set('NO_PROXY', 'localhost, anotherdomain.com, newdomain.com')
    162         self.assertTrue(urllib.proxy_bypass_environment('anotherdomain.com'))
    163 
    164 
    165 class urlopen_HttpTests(unittest.TestCase, FakeHTTPMixin):
    166     """Test urlopen() opening a fake http connection."""
    167 
    168     def test_read(self):
    169         self.fakehttp('Hello!')
    170         try:
    171             fp = urllib.urlopen("http://python.org/")
    172             self.assertEqual(fp.readline(), 'Hello!')
    173             self.assertEqual(fp.readline(), '')
    174             self.assertEqual(fp.geturl(), 'http://python.org/')
    175             self.assertEqual(fp.getcode(), 200)
    176         finally:
    177             self.unfakehttp()
    178 
    179     def test_url_fragment(self):
    180         # Issue #11703: geturl() omits fragments in the original URL.
    181         url = 'http://docs.python.org/library/urllib.html#OK'
    182         self.fakehttp('Hello!')
    183         try:
    184             fp = urllib.urlopen(url)
    185             self.assertEqual(fp.geturl(), url)
    186         finally:
    187             self.unfakehttp()
    188 
    189     def test_read_bogus(self):
    190         # urlopen() should raise IOError for many error codes.
    191         self.fakehttp('''HTTP/1.1 401 Authentication Required
    192 Date: Wed, 02 Jan 2008 03:03:54 GMT
    193 Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
    194 Connection: close
    195 Content-Type: text/html; charset=iso-8859-1
    196 ''')
    197         try:
    198             self.assertRaises(IOError, urllib.urlopen, "http://python.org/")
    199         finally:
    200             self.unfakehttp()
    201 
    202     def test_invalid_redirect(self):
    203         # urlopen() should raise IOError for many error codes.
    204         self.fakehttp("""HTTP/1.1 302 Found
    205 Date: Wed, 02 Jan 2008 03:03:54 GMT
    206 Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
    207 Location: file:README
    208 Connection: close
    209 Content-Type: text/html; charset=iso-8859-1
    210 """)
    211         try:
    212             self.assertRaises(IOError, urllib.urlopen, "http://python.org/")
    213         finally:
    214             self.unfakehttp()
    215 
    216     def test_empty_socket(self):
    217         # urlopen() raises IOError if the underlying socket does not send any
    218         # data. (#1680230)
    219         self.fakehttp('')
    220         try:
    221             self.assertRaises(IOError, urllib.urlopen, 'http://something')
    222         finally:
    223             self.unfakehttp()
    224 
    225     def test_missing_localfile(self):
    226         self.assertRaises(IOError, urllib.urlopen,
    227                 'file://localhost/a/missing/file.py')
    228         fd, tmp_file = tempfile.mkstemp()
    229         tmp_fileurl = 'file://localhost/' + tmp_file.replace(os.path.sep, '/')
    230         try:
    231             self.assertTrue(os.path.exists(tmp_file))
    232             fp = urllib.urlopen(tmp_fileurl)
    233         finally:
    234             os.close(fd)
    235             fp.close()
    236         os.unlink(tmp_file)
    237 
    238         self.assertFalse(os.path.exists(tmp_file))
    239         self.assertRaises(IOError, urllib.urlopen, tmp_fileurl)
    240 
    241     def test_ftp_nonexisting(self):
    242         self.assertRaises(IOError, urllib.urlopen,
    243                 'ftp://localhost/not/existing/file.py')
    244 
    245 
    246     def test_userpass_inurl(self):
    247         self.fakehttp('Hello!')
    248         try:
    249             fakehttp_wrapper = httplib.HTTP._connection_class
    250             fp = urllib.urlopen("http://user:pass@python.org/")
    251             authorization = ("Authorization: Basic %s\r\n" %
    252                             b64encode('user:pass'))
    253             # The authorization header must be in place
    254             self.assertIn(authorization, fakehttp_wrapper.buf)
    255             self.assertEqual(fp.readline(), "Hello!")
    256             self.assertEqual(fp.readline(), "")
    257             self.assertEqual(fp.geturl(), 'http://user:pass@python.org/')
    258             self.assertEqual(fp.getcode(), 200)
    259         finally:
    260             self.unfakehttp()
    261 
    262     def test_userpass_with_spaces_inurl(self):
    263         self.fakehttp('Hello!')
    264         try:
    265             url = "http://a b:c d (at] python.org/"
    266             fakehttp_wrapper = httplib.HTTP._connection_class
    267             authorization = ("Authorization: Basic %s\r\n" %
    268                              b64encode('a b:c d'))
    269             fp = urllib.urlopen(url)
    270             # The authorization header must be in place
    271             self.assertIn(authorization, fakehttp_wrapper.buf)
    272             self.assertEqual(fp.readline(), "Hello!")
    273             self.assertEqual(fp.readline(), "")
    274             # the spaces are quoted in URL so no match
    275             self.assertNotEqual(fp.geturl(), url)
    276             self.assertEqual(fp.getcode(), 200)
    277         finally:
    278             self.unfakehttp()
    279 
    280 
    281 class urlretrieve_FileTests(unittest.TestCase):
    282     """Test urllib.urlretrieve() on local files"""
    283 
    284     def setUp(self):
    285         # Create a list of temporary files. Each item in the list is a file
    286         # name (absolute path or relative to the current working directory).
    287         # All files in this list will be deleted in the tearDown method. Note,
    288         # this only helps to makes sure temporary files get deleted, but it
    289         # does nothing about trying to close files that may still be open. It
    290         # is the responsibility of the developer to properly close files even
    291         # when exceptional conditions occur.
    292         self.tempFiles = []
    293 
    294         # Create a temporary file.
    295         self.registerFileForCleanUp(test_support.TESTFN)
    296         self.text = 'testing urllib.urlretrieve'
    297         try:
    298             FILE = file(test_support.TESTFN, 'wb')
    299             FILE.write(self.text)
    300             FILE.close()
    301         finally:
    302             try: FILE.close()
    303             except: pass
    304 
    305     def tearDown(self):
    306         # Delete the temporary files.
    307         for each in self.tempFiles:
    308             try: os.remove(each)
    309             except: pass
    310 
    311     def constructLocalFileUrl(self, filePath):
    312         return "file://%s" % urllib.pathname2url(os.path.abspath(filePath))
    313 
    314     def createNewTempFile(self, data=""):
    315         """Creates a new temporary file containing the specified data,
    316         registers the file for deletion during the test fixture tear down, and
    317         returns the absolute path of the file."""
    318 
    319         newFd, newFilePath = tempfile.mkstemp()
    320         try:
    321             self.registerFileForCleanUp(newFilePath)
    322             newFile = os.fdopen(newFd, "wb")
    323             newFile.write(data)
    324             newFile.close()
    325         finally:
    326             try: newFile.close()
    327             except: pass
    328         return newFilePath
    329 
    330     def registerFileForCleanUp(self, fileName):
    331         self.tempFiles.append(fileName)
    332 
    333     def test_basic(self):
    334         # Make sure that a local file just gets its own location returned and
    335         # a headers value is returned.
    336         result = urllib.urlretrieve("file:%s" % test_support.TESTFN)
    337         self.assertEqual(result[0], test_support.TESTFN)
    338         self.assertIsInstance(result[1], mimetools.Message,
    339                               "did not get a mimetools.Message instance as "
    340                               "second returned value")
    341 
    342     def test_copy(self):
    343         # Test that setting the filename argument works.
    344         second_temp = "%s.2" % test_support.TESTFN
    345         self.registerFileForCleanUp(second_temp)
    346         result = urllib.urlretrieve(self.constructLocalFileUrl(
    347             test_support.TESTFN), second_temp)
    348         self.assertEqual(second_temp, result[0])
    349         self.assertTrue(os.path.exists(second_temp), "copy of the file was not "
    350                                                   "made")
    351         FILE = file(second_temp, 'rb')
    352         try:
    353             text = FILE.read()
    354             FILE.close()
    355         finally:
    356             try: FILE.close()
    357             except: pass
    358         self.assertEqual(self.text, text)
    359 
    360     def test_reporthook(self):
    361         # Make sure that the reporthook works.
    362         def hooktester(count, block_size, total_size, count_holder=[0]):
    363             self.assertIsInstance(count, int)
    364             self.assertIsInstance(block_size, int)
    365             self.assertIsInstance(total_size, int)
    366             self.assertEqual(count, count_holder[0])
    367             count_holder[0] = count_holder[0] + 1
    368         second_temp = "%s.2" % test_support.TESTFN
    369         self.registerFileForCleanUp(second_temp)
    370         urllib.urlretrieve(self.constructLocalFileUrl(test_support.TESTFN),
    371             second_temp, hooktester)
    372 
    373     def test_reporthook_0_bytes(self):
    374         # Test on zero length file. Should call reporthook only 1 time.
    375         report = []
    376         def hooktester(count, block_size, total_size, _report=report):
    377             _report.append((count, block_size, total_size))
    378         srcFileName = self.createNewTempFile()
    379         urllib.urlretrieve(self.constructLocalFileUrl(srcFileName),
    380             test_support.TESTFN, hooktester)
    381         self.assertEqual(len(report), 1)
    382         self.assertEqual(report[0][2], 0)
    383 
    384     def test_reporthook_5_bytes(self):
    385         # Test on 5 byte file. Should call reporthook only 2 times (once when
    386         # the "network connection" is established and once when the block is
    387         # read). Since the block size is 8192 bytes, only one block read is
    388         # required to read the entire file.
    389         report = []
    390         def hooktester(count, block_size, total_size, _report=report):
    391             _report.append((count, block_size, total_size))
    392         srcFileName = self.createNewTempFile("x" * 5)
    393         urllib.urlretrieve(self.constructLocalFileUrl(srcFileName),
    394             test_support.TESTFN, hooktester)
    395         self.assertEqual(len(report), 2)
    396         self.assertEqual(report[0][1], 8192)
    397         self.assertEqual(report[0][2], 5)
    398 
    399     def test_reporthook_8193_bytes(self):
    400         # Test on 8193 byte file. Should call reporthook only 3 times (once
    401         # when the "network connection" is established, once for the next 8192
    402         # bytes, and once for the last byte).
    403         report = []
    404         def hooktester(count, block_size, total_size, _report=report):
    405             _report.append((count, block_size, total_size))
    406         srcFileName = self.createNewTempFile("x" * 8193)
    407         urllib.urlretrieve(self.constructLocalFileUrl(srcFileName),
    408             test_support.TESTFN, hooktester)
    409         self.assertEqual(len(report), 3)
    410         self.assertEqual(report[0][1], 8192)
    411         self.assertEqual(report[0][2], 8193)
    412 
    413 
    414 class urlretrieve_HttpTests(unittest.TestCase, FakeHTTPMixin):
    415     """Test urllib.urlretrieve() using fake http connections"""
    416 
    417     def test_short_content_raises_ContentTooShortError(self):
    418         self.fakehttp('''HTTP/1.1 200 OK
    419 Date: Wed, 02 Jan 2008 03:03:54 GMT
    420 Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
    421 Connection: close
    422 Content-Length: 100
    423 Content-Type: text/html; charset=iso-8859-1
    424 
    425 FF
    426 ''')
    427 
    428         def _reporthook(par1, par2, par3):
    429             pass
    430 
    431         try:
    432             self.assertRaises(urllib.ContentTooShortError, urllib.urlretrieve,
    433                     'http://example.com', reporthook=_reporthook)
    434         finally:
    435             self.unfakehttp()
    436 
    437     def test_short_content_raises_ContentTooShortError_without_reporthook(self):
    438         self.fakehttp('''HTTP/1.1 200 OK
    439 Date: Wed, 02 Jan 2008 03:03:54 GMT
    440 Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
    441 Connection: close
    442 Content-Length: 100
    443 Content-Type: text/html; charset=iso-8859-1
    444 
    445 FF
    446 ''')
    447         try:
    448             self.assertRaises(urllib.ContentTooShortError, urllib.urlretrieve, 'http://example.com/')
    449         finally:
    450             self.unfakehttp()
    451 
    452 class QuotingTests(unittest.TestCase):
    453     """Tests for urllib.quote() and urllib.quote_plus()
    454 
    455     According to RFC 2396 ("Uniform Resource Identifiers), to escape a
    456     character you write it as '%' + <2 character US-ASCII hex value>.  The Python
    457     code of ``'%' + hex(ord(<character>))[2:]`` escapes a character properly.
    458     Case does not matter on the hex letters.
    459 
    460     The various character sets specified are:
    461 
    462     Reserved characters : ";/?:@&=+$,"
    463         Have special meaning in URIs and must be escaped if not being used for
    464         their special meaning
    465     Data characters : letters, digits, and "-_.!~*'()"
    466         Unreserved and do not need to be escaped; can be, though, if desired
    467     Control characters : 0x00 - 0x1F, 0x7F
    468         Have no use in URIs so must be escaped
    469     space : 0x20
    470         Must be escaped
    471     Delimiters : '<>#%"'
    472         Must be escaped
    473     Unwise : "{}|\^[]`"
    474         Must be escaped
    475 
    476     """
    477 
    478     def test_never_quote(self):
    479         # Make sure quote() does not quote letters, digits, and "_,.-"
    480         do_not_quote = '' .join(["ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    481                                  "abcdefghijklmnopqrstuvwxyz",
    482                                  "0123456789",
    483                                  "_.-"])
    484         result = urllib.quote(do_not_quote)
    485         self.assertEqual(do_not_quote, result,
    486                          "using quote(): %s != %s" % (do_not_quote, result))
    487         result = urllib.quote_plus(do_not_quote)
    488         self.assertEqual(do_not_quote, result,
    489                         "using quote_plus(): %s != %s" % (do_not_quote, result))
    490 
    491     def test_default_safe(self):
    492         # Test '/' is default value for 'safe' parameter
    493         self.assertEqual(urllib.quote.func_defaults[0], '/')
    494 
    495     def test_safe(self):
    496         # Test setting 'safe' parameter does what it should do
    497         quote_by_default = "<>"
    498         result = urllib.quote(quote_by_default, safe=quote_by_default)
    499         self.assertEqual(quote_by_default, result,
    500                          "using quote(): %s != %s" % (quote_by_default, result))
    501         result = urllib.quote_plus(quote_by_default, safe=quote_by_default)
    502         self.assertEqual(quote_by_default, result,
    503                          "using quote_plus(): %s != %s" %
    504                          (quote_by_default, result))
    505 
    506     def test_default_quoting(self):
    507         # Make sure all characters that should be quoted are by default sans
    508         # space (separate test for that).
    509         should_quote = [chr(num) for num in range(32)] # For 0x00 - 0x1F
    510         should_quote.append('<>#%"{}|\^[]`')
    511         should_quote.append(chr(127)) # For 0x7F
    512         should_quote = ''.join(should_quote)
    513         for char in should_quote:
    514             result = urllib.quote(char)
    515             self.assertEqual(hexescape(char), result,
    516                              "using quote(): %s should be escaped to %s, not %s" %
    517                              (char, hexescape(char), result))
    518             result = urllib.quote_plus(char)
    519             self.assertEqual(hexescape(char), result,
    520                              "using quote_plus(): "
    521                              "%s should be escapes to %s, not %s" %
    522                              (char, hexescape(char), result))
    523         del should_quote
    524         partial_quote = "ab[]cd"
    525         expected = "ab%5B%5Dcd"
    526         result = urllib.quote(partial_quote)
    527         self.assertEqual(expected, result,
    528                          "using quote(): %s != %s" % (expected, result))
    529         result = urllib.quote_plus(partial_quote)
    530         self.assertEqual(expected, result,
    531                          "using quote_plus(): %s != %s" % (expected, result))
    532         self.assertRaises(TypeError, urllib.quote, None)
    533 
    534     def test_quoting_space(self):
    535         # Make sure quote() and quote_plus() handle spaces as specified in
    536         # their unique way
    537         result = urllib.quote(' ')
    538         self.assertEqual(result, hexescape(' '),
    539                          "using quote(): %s != %s" % (result, hexescape(' ')))
    540         result = urllib.quote_plus(' ')
    541         self.assertEqual(result, '+',
    542                          "using quote_plus(): %s != +" % result)
    543         given = "a b cd e f"
    544         expect = given.replace(' ', hexescape(' '))
    545         result = urllib.quote(given)
    546         self.assertEqual(expect, result,
    547                          "using quote(): %s != %s" % (expect, result))
    548         expect = given.replace(' ', '+')
    549         result = urllib.quote_plus(given)
    550         self.assertEqual(expect, result,
    551                          "using quote_plus(): %s != %s" % (expect, result))
    552 
    553     def test_quoting_plus(self):
    554         self.assertEqual(urllib.quote_plus('alpha+beta gamma'),
    555                          'alpha%2Bbeta+gamma')
    556         self.assertEqual(urllib.quote_plus('alpha+beta gamma', '+'),
    557                          'alpha+beta+gamma')
    558 
    559 class UnquotingTests(unittest.TestCase):
    560     """Tests for unquote() and unquote_plus()
    561 
    562     See the doc string for quoting_Tests for details on quoting and such.
    563 
    564     """
    565 
    566     def test_unquoting(self):
    567         # Make sure unquoting of all ASCII values works
    568         escape_list = []
    569         for num in range(128):
    570             given = hexescape(chr(num))
    571             expect = chr(num)
    572             result = urllib.unquote(given)
    573             self.assertEqual(expect, result,
    574                              "using unquote(): %s != %s" % (expect, result))
    575             result = urllib.unquote_plus(given)
    576             self.assertEqual(expect, result,
    577                              "using unquote_plus(): %s != %s" %
    578                              (expect, result))
    579             escape_list.append(given)
    580         escape_string = ''.join(escape_list)
    581         del escape_list
    582         result = urllib.unquote(escape_string)
    583         self.assertEqual(result.count('%'), 1,
    584                          "using quote(): not all characters escaped; %s" %
    585                          result)
    586         result = urllib.unquote(escape_string)
    587         self.assertEqual(result.count('%'), 1,
    588                          "using unquote(): not all characters escaped: "
    589                          "%s" % result)
    590 
    591     def test_unquoting_badpercent(self):
    592         # Test unquoting on bad percent-escapes
    593         given = '%xab'
    594         expect = given
    595         result = urllib.unquote(given)
    596         self.assertEqual(expect, result, "using unquote(): %r != %r"
    597                          % (expect, result))
    598         given = '%x'
    599         expect = given
    600         result = urllib.unquote(given)
    601         self.assertEqual(expect, result, "using unquote(): %r != %r"
    602                          % (expect, result))
    603         given = '%'
    604         expect = given
    605         result = urllib.unquote(given)
    606         self.assertEqual(expect, result, "using unquote(): %r != %r"
    607                          % (expect, result))
    608 
    609     def test_unquoting_mixed_case(self):
    610         # Test unquoting on mixed-case hex digits in the percent-escapes
    611         given = '%Ab%eA'
    612         expect = '\xab\xea'
    613         result = urllib.unquote(given)
    614         self.assertEqual(expect, result, "using unquote(): %r != %r"
    615                          % (expect, result))
    616 
    617     def test_unquoting_parts(self):
    618         # Make sure unquoting works when have non-quoted characters
    619         # interspersed
    620         given = 'ab%sd' % hexescape('c')
    621         expect = "abcd"
    622         result = urllib.unquote(given)
    623         self.assertEqual(expect, result,
    624                          "using quote(): %s != %s" % (expect, result))
    625         result = urllib.unquote_plus(given)
    626         self.assertEqual(expect, result,
    627                          "using unquote_plus(): %s != %s" % (expect, result))
    628 
    629     def test_unquoting_plus(self):
    630         # Test difference between unquote() and unquote_plus()
    631         given = "are+there+spaces..."
    632         expect = given
    633         result = urllib.unquote(given)
    634         self.assertEqual(expect, result,
    635                          "using unquote(): %s != %s" % (expect, result))
    636         expect = given.replace('+', ' ')
    637         result = urllib.unquote_plus(given)
    638         self.assertEqual(expect, result,
    639                          "using unquote_plus(): %s != %s" % (expect, result))
    640 
    641     def test_unquote_with_unicode(self):
    642         r = urllib.unquote(u'br%C3%BCckner_sapporo_20050930.doc')
    643         self.assertEqual(r, u'br\xc3\xbcckner_sapporo_20050930.doc')
    644 
    645 class urlencode_Tests(unittest.TestCase):
    646     """Tests for urlencode()"""
    647 
    648     def help_inputtype(self, given, test_type):
    649         """Helper method for testing different input types.
    650 
    651         'given' must lead to only the pairs:
    652             * 1st, 1
    653             * 2nd, 2
    654             * 3rd, 3
    655 
    656         Test cannot assume anything about order.  Docs make no guarantee and
    657         have possible dictionary input.
    658 
    659         """
    660         expect_somewhere = ["1st=1", "2nd=2", "3rd=3"]
    661         result = urllib.urlencode(given)
    662         for expected in expect_somewhere:
    663             self.assertIn(expected, result,
    664                          "testing %s: %s not found in %s" %
    665                          (test_type, expected, result))
    666         self.assertEqual(result.count('&'), 2,
    667                          "testing %s: expected 2 '&'s; got %s" %
    668                          (test_type, result.count('&')))
    669         amp_location = result.index('&')
    670         on_amp_left = result[amp_location - 1]
    671         on_amp_right = result[amp_location + 1]
    672         self.assertTrue(on_amp_left.isdigit() and on_amp_right.isdigit(),
    673                      "testing %s: '&' not located in proper place in %s" %
    674                      (test_type, result))
    675         self.assertEqual(len(result), (5 * 3) + 2, #5 chars per thing and amps
    676                          "testing %s: "
    677                          "unexpected number of characters: %s != %s" %
    678                          (test_type, len(result), (5 * 3) + 2))
    679 
    680     def test_using_mapping(self):
    681         # Test passing in a mapping object as an argument.
    682         self.help_inputtype({"1st":'1', "2nd":'2', "3rd":'3'},
    683                             "using dict as input type")
    684 
    685     def test_using_sequence(self):
    686         # Test passing in a sequence of two-item sequences as an argument.
    687         self.help_inputtype([('1st', '1'), ('2nd', '2'), ('3rd', '3')],
    688                             "using sequence of two-item tuples as input")
    689 
    690     def test_quoting(self):
    691         # Make sure keys and values are quoted using quote_plus()
    692         given = {"&":"="}
    693         expect = "%s=%s" % (hexescape('&'), hexescape('='))
    694         result = urllib.urlencode(given)
    695         self.assertEqual(expect, result)
    696         given = {"key name":"A bunch of pluses"}
    697         expect = "key+name=A+bunch+of+pluses"
    698         result = urllib.urlencode(given)
    699         self.assertEqual(expect, result)
    700 
    701     def test_doseq(self):
    702         # Test that passing True for 'doseq' parameter works correctly
    703         given = {'sequence':['1', '2', '3']}
    704         expect = "sequence=%s" % urllib.quote_plus(str(['1', '2', '3']))
    705         result = urllib.urlencode(given)
    706         self.assertEqual(expect, result)
    707         result = urllib.urlencode(given, True)
    708         for value in given["sequence"]:
    709             expect = "sequence=%s" % value
    710             self.assertIn(expect, result)
    711         self.assertEqual(result.count('&'), 2,
    712                          "Expected 2 '&'s, got %s" % result.count('&'))
    713 
    714 class Pathname_Tests(unittest.TestCase):
    715     """Test pathname2url() and url2pathname()"""
    716 
    717     def test_basic(self):
    718         # Make sure simple tests pass
    719         expected_path = os.path.join("parts", "of", "a", "path")
    720         expected_url = "parts/of/a/path"
    721         result = urllib.pathname2url(expected_path)
    722         self.assertEqual(expected_url, result,
    723                          "pathname2url() failed; %s != %s" %
    724                          (result, expected_url))
    725         result = urllib.url2pathname(expected_url)
    726         self.assertEqual(expected_path, result,
    727                          "url2pathame() failed; %s != %s" %
    728                          (result, expected_path))
    729 
    730     def test_quoting(self):
    731         # Test automatic quoting and unquoting works for pathnam2url() and
    732         # url2pathname() respectively
    733         given = os.path.join("needs", "quot=ing", "here")
    734         expect = "needs/%s/here" % urllib.quote("quot=ing")
    735         result = urllib.pathname2url(given)
    736         self.assertEqual(expect, result,
    737                          "pathname2url() failed; %s != %s" %
    738                          (expect, result))
    739         expect = given
    740         result = urllib.url2pathname(result)
    741         self.assertEqual(expect, result,
    742                          "url2pathname() failed; %s != %s" %
    743                          (expect, result))
    744         given = os.path.join("make sure", "using_quote")
    745         expect = "%s/using_quote" % urllib.quote("make sure")
    746         result = urllib.pathname2url(given)
    747         self.assertEqual(expect, result,
    748                          "pathname2url() failed; %s != %s" %
    749                          (expect, result))
    750         given = "make+sure/using_unquote"
    751         expect = os.path.join("make+sure", "using_unquote")
    752         result = urllib.url2pathname(given)
    753         self.assertEqual(expect, result,
    754                          "url2pathname() failed; %s != %s" %
    755                          (expect, result))
    756 
    757     @unittest.skipUnless(sys.platform == 'win32',
    758                          'test specific to the nturl2path library')
    759     def test_ntpath(self):
    760         given = ('/C:/', '///C:/', '/C|//')
    761         expect = 'C:\\'
    762         for url in given:
    763             result = urllib.url2pathname(url)
    764             self.assertEqual(expect, result,
    765                              'nturl2path.url2pathname() failed; %s != %s' %
    766                              (expect, result))
    767         given = '///C|/path'
    768         expect = 'C:\\path'
    769         result = urllib.url2pathname(given)
    770         self.assertEqual(expect, result,
    771                          'nturl2path.url2pathname() failed; %s != %s' %
    772                          (expect, result))
    773 
    774 class Utility_Tests(unittest.TestCase):
    775     """Testcase to test the various utility functions in the urllib."""
    776 
    777     def test_splitpasswd(self):
    778         """Some of the password examples are not sensible, but it is added to
    779         confirming to RFC2617 and addressing issue4675.
    780         """
    781         self.assertEqual(('user', 'ab'),urllib.splitpasswd('user:ab'))
    782         self.assertEqual(('user', 'a\nb'),urllib.splitpasswd('user:a\nb'))
    783         self.assertEqual(('user', 'a\tb'),urllib.splitpasswd('user:a\tb'))
    784         self.assertEqual(('user', 'a\rb'),urllib.splitpasswd('user:a\rb'))
    785         self.assertEqual(('user', 'a\fb'),urllib.splitpasswd('user:a\fb'))
    786         self.assertEqual(('user', 'a\vb'),urllib.splitpasswd('user:a\vb'))
    787         self.assertEqual(('user', 'a:b'),urllib.splitpasswd('user:a:b'))
    788         self.assertEqual(('user', 'a b'),urllib.splitpasswd('user:a b'))
    789         self.assertEqual(('user 2', 'ab'),urllib.splitpasswd('user 2:ab'))
    790         self.assertEqual(('user+1', 'a+b'),urllib.splitpasswd('user+1:a+b'))
    791 
    792 
    793 class URLopener_Tests(unittest.TestCase):
    794     """Testcase to test the open method of URLopener class."""
    795 
    796     def test_quoted_open(self):
    797         class DummyURLopener(urllib.URLopener):
    798             def open_spam(self, url):
    799                 return url
    800 
    801         self.assertEqual(DummyURLopener().open(
    802             'spam://example/ /'),'//example/%20/')
    803 
    804         # test the safe characters are not quoted by urlopen
    805         self.assertEqual(DummyURLopener().open(
    806             "spam://c:|windows%/:=&?~#+!$,;'@()*[]|/path/"),
    807             "//c:|windows%/:=&?~#+!$,;'@()*[]|/path/")
    808 
    809 
    810 # Just commented them out.
    811 # Can't really tell why keep failing in windows and sparc.
    812 # Everywhere else they work ok, but on those machines, sometimes
    813 # fail in one of the tests, sometimes in other. I have a linux, and
    814 # the tests go ok.
    815 # If anybody has one of the problematic enviroments, please help!
    816 # .   Facundo
    817 #
    818 # def server(evt):
    819 #     import socket, time
    820 #     serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    821 #     serv.settimeout(3)
    822 #     serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    823 #     serv.bind(("", 9093))
    824 #     serv.listen(5)
    825 #     try:
    826 #         conn, addr = serv.accept()
    827 #         conn.send("1 Hola mundo\n")
    828 #         cantdata = 0
    829 #         while cantdata < 13:
    830 #             data = conn.recv(13-cantdata)
    831 #             cantdata += len(data)
    832 #             time.sleep(.3)
    833 #         conn.send("2 No more lines\n")
    834 #         conn.close()
    835 #     except socket.timeout:
    836 #         pass
    837 #     finally:
    838 #         serv.close()
    839 #         evt.set()
    840 #
    841 # class FTPWrapperTests(unittest.TestCase):
    842 #
    843 #     def setUp(self):
    844 #         import ftplib, time, threading
    845 #         ftplib.FTP.port = 9093
    846 #         self.evt = threading.Event()
    847 #         threading.Thread(target=server, args=(self.evt,)).start()
    848 #         time.sleep(.1)
    849 #
    850 #     def tearDown(self):
    851 #         self.evt.wait()
    852 #
    853 #     def testBasic(self):
    854 #         # connects
    855 #         ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
    856 #         ftp.close()
    857 #
    858 #     def testTimeoutNone(self):
    859 #         # global default timeout is ignored
    860 #         import socket
    861 #         self.assertTrue(socket.getdefaulttimeout() is None)
    862 #         socket.setdefaulttimeout(30)
    863 #         try:
    864 #             ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
    865 #         finally:
    866 #             socket.setdefaulttimeout(None)
    867 #         self.assertEqual(ftp.ftp.sock.gettimeout(), 30)
    868 #         ftp.close()
    869 #
    870 #     def testTimeoutDefault(self):
    871 #         # global default timeout is used
    872 #         import socket
    873 #         self.assertTrue(socket.getdefaulttimeout() is None)
    874 #         socket.setdefaulttimeout(30)
    875 #         try:
    876 #             ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
    877 #         finally:
    878 #             socket.setdefaulttimeout(None)
    879 #         self.assertEqual(ftp.ftp.sock.gettimeout(), 30)
    880 #         ftp.close()
    881 #
    882 #     def testTimeoutValue(self):
    883 #         ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [],
    884 #                                 timeout=30)
    885 #         self.assertEqual(ftp.ftp.sock.gettimeout(), 30)
    886 #         ftp.close()
    887 
    888 
    889 
    890 def test_main():
    891     import warnings
    892     with warnings.catch_warnings():
    893         warnings.filterwarnings('ignore', ".*urllib\.urlopen.*Python 3.0",
    894                                 DeprecationWarning)
    895         test_support.run_unittest(
    896             urlopen_FileTests,
    897             urlopen_HttpTests,
    898             urlretrieve_FileTests,
    899             urlretrieve_HttpTests,
    900             ProxyTests,
    901             QuotingTests,
    902             UnquotingTests,
    903             urlencode_Tests,
    904             Pathname_Tests,
    905             Utility_Tests,
    906             URLopener_Tests,
    907             #FTPWrapperTests,
    908         )
    909 
    910 
    911 
    912 if __name__ == '__main__':
    913     test_main()
    914