blob: 7db281cb90f8ed56575c4eda09bda398628a02f3 [file] [log] [blame]
Brett Cannon74bfd702003-04-25 09:39:47 +00001"""Regresssion tests for urllib"""
2
Jeremy Hylton6102e292000-08-31 15:48:10 +00003import urllib
Hye-Shik Chang39aef792004-06-05 13:30:56 +00004import httplib
Jeremy Hylton66dc8c52007-08-04 03:42:26 +00005import io
Brett Cannon74bfd702003-04-25 09:39:47 +00006import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00007from test import support
Brett Cannon74bfd702003-04-25 09:39:47 +00008import os
9import mimetools
Georg Brandl5a650a22005-08-26 08:51:34 +000010import tempfile
Jeremy Hylton6102e292000-08-31 15:48:10 +000011
Brett Cannon74bfd702003-04-25 09:39:47 +000012def hexescape(char):
13 """Escape char as RFC 2396 specifies"""
14 hex_repr = hex(ord(char))[2:].upper()
15 if len(hex_repr) == 1:
16 hex_repr = "0%s" % hex_repr
17 return "%" + hex_repr
Jeremy Hylton6102e292000-08-31 15:48:10 +000018
Brett Cannon74bfd702003-04-25 09:39:47 +000019class urlopen_FileTests(unittest.TestCase):
20 """Test urlopen() opening a temporary file.
Jeremy Hylton6102e292000-08-31 15:48:10 +000021
Brett Cannon74bfd702003-04-25 09:39:47 +000022 Try to test as much functionality as possible so as to cut down on reliance
Andrew M. Kuchlingf1a2f9e2004-06-29 13:07:53 +000023 on connecting to the Net for testing.
Jeremy Hylton7ae51bf2000-09-14 16:59:07 +000024
Brett Cannon74bfd702003-04-25 09:39:47 +000025 """
Jeremy Hylton7ae51bf2000-09-14 16:59:07 +000026
Brett Cannon74bfd702003-04-25 09:39:47 +000027 def setUp(self):
28 """Setup of a temp file to use for testing"""
Guido van Rossum70d0dda2007-08-29 01:53:26 +000029 self.text = bytes("test_urllib: %s\n" % self.__class__.__name__, "ascii")
Benjamin Petersonee8712c2008-05-20 21:35:26 +000030 FILE = open(support.TESTFN, 'wb')
Brett Cannon74bfd702003-04-25 09:39:47 +000031 try:
32 FILE.write(self.text)
33 finally:
34 FILE.close()
Benjamin Petersonee8712c2008-05-20 21:35:26 +000035 self.pathname = support.TESTFN
Brett Cannon74bfd702003-04-25 09:39:47 +000036 self.returned_obj = urllib.urlopen("file:%s" % self.pathname)
Jeremy Hylton7ae51bf2000-09-14 16:59:07 +000037
Brett Cannon74bfd702003-04-25 09:39:47 +000038 def tearDown(self):
39 """Shut down the open object"""
40 self.returned_obj.close()
Benjamin Petersonee8712c2008-05-20 21:35:26 +000041 os.remove(support.TESTFN)
Jeremy Hylton7ae51bf2000-09-14 16:59:07 +000042
Brett Cannon74bfd702003-04-25 09:39:47 +000043 def test_interface(self):
44 # Make sure object returned by urlopen() has the specified methods
45 for attr in ("read", "readline", "readlines", "fileno",
Christian Heimes9bd667a2008-01-20 15:14:11 +000046 "close", "info", "geturl", "getcode", "__iter__"):
Brett Cannon74bfd702003-04-25 09:39:47 +000047 self.assert_(hasattr(self.returned_obj, attr),
48 "object returned by urlopen() lacks %s attribute" %
49 attr)
Skip Montanaroe78b92a2001-01-20 20:22:30 +000050
Brett Cannon74bfd702003-04-25 09:39:47 +000051 def test_read(self):
52 self.assertEqual(self.text, self.returned_obj.read())
Skip Montanaro080c9972001-01-28 21:12:22 +000053
Brett Cannon74bfd702003-04-25 09:39:47 +000054 def test_readline(self):
55 self.assertEqual(self.text, self.returned_obj.readline())
Guido van Rossuma0982942007-07-10 08:30:03 +000056 self.assertEqual(b'', self.returned_obj.readline(),
Brett Cannon74bfd702003-04-25 09:39:47 +000057 "calling readline() after exhausting the file did not"
58 " return an empty string")
Skip Montanaro080c9972001-01-28 21:12:22 +000059
Brett Cannon74bfd702003-04-25 09:39:47 +000060 def test_readlines(self):
61 lines_list = self.returned_obj.readlines()
62 self.assertEqual(len(lines_list), 1,
63 "readlines() returned the wrong number of lines")
64 self.assertEqual(lines_list[0], self.text,
65 "readlines() returned improper text")
Skip Montanaro080c9972001-01-28 21:12:22 +000066
Brett Cannon74bfd702003-04-25 09:39:47 +000067 def test_fileno(self):
68 file_num = self.returned_obj.fileno()
69 self.assert_(isinstance(file_num, int),
70 "fileno() did not return an int")
71 self.assertEqual(os.read(file_num, len(self.text)), self.text,
72 "Reading on the file descriptor returned by fileno() "
73 "did not return the expected text")
Skip Montanaroe78b92a2001-01-20 20:22:30 +000074
Brett Cannon74bfd702003-04-25 09:39:47 +000075 def test_close(self):
76 # Test close() by calling it hear and then having it be called again
77 # by the tearDown() method for the test
78 self.returned_obj.close()
Skip Montanaro080c9972001-01-28 21:12:22 +000079
Brett Cannon74bfd702003-04-25 09:39:47 +000080 def test_info(self):
81 self.assert_(isinstance(self.returned_obj.info(), mimetools.Message))
Skip Montanaroe78b92a2001-01-20 20:22:30 +000082
Brett Cannon74bfd702003-04-25 09:39:47 +000083 def test_geturl(self):
84 self.assertEqual(self.returned_obj.geturl(), self.pathname)
Skip Montanaro080c9972001-01-28 21:12:22 +000085
Christian Heimes9bd667a2008-01-20 15:14:11 +000086 def test_getcode(self):
87 self.assertEqual(self.returned_obj.getcode(), None)
88
Brett Cannon74bfd702003-04-25 09:39:47 +000089 def test_iter(self):
90 # Test iterator
91 # Don't need to count number of iterations since test would fail the
92 # instant it returned anything beyond the first line from the
93 # comparison
94 for line in self.returned_obj.__iter__():
95 self.assertEqual(line, self.text)
Skip Montanaro080c9972001-01-28 21:12:22 +000096
Hye-Shik Chang39aef792004-06-05 13:30:56 +000097class urlopen_HttpTests(unittest.TestCase):
98 """Test urlopen() opening a fake http connection."""
99
100 def fakehttp(self, fakedata):
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000101 class FakeSocket(io.BytesIO):
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000102 def sendall(self, str): pass
103 def makefile(self, mode, name): return self
104 def read(self, amt=None):
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000105 if self.closed: return b""
106 return io.BytesIO.read(self, amt)
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000107 def readline(self, length=None):
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000108 if self.closed: return b""
109 return io.BytesIO.readline(self, length)
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000110 class FakeHTTPConnection(httplib.HTTPConnection):
111 def connect(self):
112 self.sock = FakeSocket(fakedata)
Jeremy Hylton77553ab2007-08-04 19:23:09 +0000113 self._connection_class = httplib.HTTPConnection
114 httplib.HTTPConnection = FakeHTTPConnection
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000115
116 def unfakehttp(self):
Jeremy Hylton77553ab2007-08-04 19:23:09 +0000117 httplib.HTTPConnection = self._connection_class
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000118
119 def test_read(self):
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000120 self.fakehttp(b"Hello!")
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000121 try:
122 fp = urllib.urlopen("http://python.org/")
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000123 self.assertEqual(fp.readline(), b"Hello!")
124 self.assertEqual(fp.readline(), b"")
Christian Heimes9bd667a2008-01-20 15:14:11 +0000125 self.assertEqual(fp.geturl(), 'http://python.org/')
126 self.assertEqual(fp.getcode(), 200)
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000127 finally:
128 self.unfakehttp()
129
Christian Heimes57dddfb2008-01-02 18:30:52 +0000130 def test_read_bogus(self):
131 # urlopen() should raise IOError for many error codes.
132 self.fakehttp(b'''HTTP/1.1 401 Authentication Required
133Date: Wed, 02 Jan 2008 03:03:54 GMT
134Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e
135Connection: close
136Content-Type: text/html; charset=iso-8859-1
137''')
138 try:
139 self.assertRaises(IOError, urllib.urlopen, "http://python.org/")
140 finally:
141 self.unfakehttp()
142
Guido van Rossumd8faa362007-04-27 19:54:29 +0000143 def test_empty_socket(self):
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000144 # urlopen() raises IOError if the underlying socket does not send any
145 # data. (#1680230)
Christian Heimes57dddfb2008-01-02 18:30:52 +0000146 self.fakehttp(b'')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000147 try:
Jeremy Hylton66dc8c52007-08-04 03:42:26 +0000148 self.assertRaises(IOError, urllib.urlopen, "http://something")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000149 finally:
150 self.unfakehttp()
151
Brett Cannon19691362003-04-29 05:08:06 +0000152class urlretrieve_FileTests(unittest.TestCase):
Brett Cannon74bfd702003-04-25 09:39:47 +0000153 """Test urllib.urlretrieve() on local files"""
Skip Montanaro080c9972001-01-28 21:12:22 +0000154
Brett Cannon19691362003-04-29 05:08:06 +0000155 def setUp(self):
Georg Brandl5a650a22005-08-26 08:51:34 +0000156 # Create a list of temporary files. Each item in the list is a file
157 # name (absolute path or relative to the current working directory).
158 # All files in this list will be deleted in the tearDown method. Note,
159 # this only helps to makes sure temporary files get deleted, but it
160 # does nothing about trying to close files that may still be open. It
161 # is the responsibility of the developer to properly close files even
162 # when exceptional conditions occur.
163 self.tempFiles = []
164
Brett Cannon19691362003-04-29 05:08:06 +0000165 # Create a temporary file.
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000166 self.registerFileForCleanUp(support.TESTFN)
Guido van Rossuma0982942007-07-10 08:30:03 +0000167 self.text = b'testing urllib.urlretrieve'
Georg Brandl5a650a22005-08-26 08:51:34 +0000168 try:
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000169 FILE = open(support.TESTFN, 'wb')
Georg Brandl5a650a22005-08-26 08:51:34 +0000170 FILE.write(self.text)
171 FILE.close()
172 finally:
173 try: FILE.close()
174 except: pass
Brett Cannon19691362003-04-29 05:08:06 +0000175
176 def tearDown(self):
Georg Brandl5a650a22005-08-26 08:51:34 +0000177 # Delete the temporary files.
178 for each in self.tempFiles:
179 try: os.remove(each)
180 except: pass
181
182 def constructLocalFileUrl(self, filePath):
183 return "file://%s" % urllib.pathname2url(os.path.abspath(filePath))
184
Guido van Rossum70d0dda2007-08-29 01:53:26 +0000185 def createNewTempFile(self, data=b""):
Georg Brandl5a650a22005-08-26 08:51:34 +0000186 """Creates a new temporary file containing the specified data,
187 registers the file for deletion during the test fixture tear down, and
188 returns the absolute path of the file."""
189
190 newFd, newFilePath = tempfile.mkstemp()
191 try:
192 self.registerFileForCleanUp(newFilePath)
193 newFile = os.fdopen(newFd, "wb")
194 newFile.write(data)
195 newFile.close()
196 finally:
197 try: newFile.close()
198 except: pass
199 return newFilePath
200
201 def registerFileForCleanUp(self, fileName):
202 self.tempFiles.append(fileName)
Brett Cannon19691362003-04-29 05:08:06 +0000203
204 def test_basic(self):
205 # Make sure that a local file just gets its own location returned and
206 # a headers value is returned.
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000207 result = urllib.urlretrieve("file:%s" % support.TESTFN)
208 self.assertEqual(result[0], support.TESTFN)
Brett Cannon19691362003-04-29 05:08:06 +0000209 self.assert_(isinstance(result[1], mimetools.Message),
210 "did not get a mimetools.Message instance as second "
211 "returned value")
212
213 def test_copy(self):
214 # Test that setting the filename argument works.
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000215 second_temp = "%s.2" % support.TESTFN
Georg Brandl5a650a22005-08-26 08:51:34 +0000216 self.registerFileForCleanUp(second_temp)
217 result = urllib.urlretrieve(self.constructLocalFileUrl(
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000218 support.TESTFN), second_temp)
Brett Cannon19691362003-04-29 05:08:06 +0000219 self.assertEqual(second_temp, result[0])
220 self.assert_(os.path.exists(second_temp), "copy of the file was not "
221 "made")
Alex Martelli01c77c62006-08-24 02:58:11 +0000222 FILE = open(second_temp, 'rb')
Brett Cannon19691362003-04-29 05:08:06 +0000223 try:
224 text = FILE.read()
Brett Cannon19691362003-04-29 05:08:06 +0000225 FILE.close()
Georg Brandl5a650a22005-08-26 08:51:34 +0000226 finally:
227 try: FILE.close()
228 except: pass
Brett Cannon19691362003-04-29 05:08:06 +0000229 self.assertEqual(self.text, text)
230
231 def test_reporthook(self):
232 # Make sure that the reporthook works.
233 def hooktester(count, block_size, total_size, count_holder=[0]):
234 self.assert_(isinstance(count, int))
235 self.assert_(isinstance(block_size, int))
236 self.assert_(isinstance(total_size, int))
237 self.assertEqual(count, count_holder[0])
238 count_holder[0] = count_holder[0] + 1
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000239 second_temp = "%s.2" % support.TESTFN
Georg Brandl5a650a22005-08-26 08:51:34 +0000240 self.registerFileForCleanUp(second_temp)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000241 urllib.urlretrieve(self.constructLocalFileUrl(support.TESTFN),
Georg Brandl5a650a22005-08-26 08:51:34 +0000242 second_temp, hooktester)
243
244 def test_reporthook_0_bytes(self):
245 # Test on zero length file. Should call reporthook only 1 time.
246 report = []
247 def hooktester(count, block_size, total_size, _report=report):
248 _report.append((count, block_size, total_size))
249 srcFileName = self.createNewTempFile()
250 urllib.urlretrieve(self.constructLocalFileUrl(srcFileName),
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000251 support.TESTFN, hooktester)
Georg Brandl5a650a22005-08-26 08:51:34 +0000252 self.assertEqual(len(report), 1)
253 self.assertEqual(report[0][2], 0)
254
255 def test_reporthook_5_bytes(self):
256 # Test on 5 byte file. Should call reporthook only 2 times (once when
257 # the "network connection" is established and once when the block is
258 # read). Since the block size is 8192 bytes, only one block read is
259 # required to read the entire file.
260 report = []
261 def hooktester(count, block_size, total_size, _report=report):
262 _report.append((count, block_size, total_size))
Guido van Rossum70d0dda2007-08-29 01:53:26 +0000263 srcFileName = self.createNewTempFile(b"x" * 5)
Georg Brandl5a650a22005-08-26 08:51:34 +0000264 urllib.urlretrieve(self.constructLocalFileUrl(srcFileName),
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000265 support.TESTFN, hooktester)
Georg Brandl5a650a22005-08-26 08:51:34 +0000266 self.assertEqual(len(report), 2)
267 self.assertEqual(report[0][1], 8192)
268 self.assertEqual(report[0][2], 5)
269
270 def test_reporthook_8193_bytes(self):
271 # Test on 8193 byte file. Should call reporthook only 3 times (once
272 # when the "network connection" is established, once for the next 8192
273 # bytes, and once for the last byte).
274 report = []
275 def hooktester(count, block_size, total_size, _report=report):
276 _report.append((count, block_size, total_size))
Guido van Rossum70d0dda2007-08-29 01:53:26 +0000277 srcFileName = self.createNewTempFile(b"x" * 8193)
Georg Brandl5a650a22005-08-26 08:51:34 +0000278 urllib.urlretrieve(self.constructLocalFileUrl(srcFileName),
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000279 support.TESTFN, hooktester)
Georg Brandl5a650a22005-08-26 08:51:34 +0000280 self.assertEqual(len(report), 3)
281 self.assertEqual(report[0][1], 8192)
282 self.assertEqual(report[0][2], 8193)
Skip Montanaro080c9972001-01-28 21:12:22 +0000283
Brett Cannon74bfd702003-04-25 09:39:47 +0000284class QuotingTests(unittest.TestCase):
285 """Tests for urllib.quote() and urllib.quote_plus()
Tim Petersc2659cf2003-05-12 20:19:37 +0000286
Brett Cannon74bfd702003-04-25 09:39:47 +0000287 According to RFC 2396 ("Uniform Resource Identifiers), to escape a
288 character you write it as '%' + <2 character US-ASCII hex value>. The Python
289 code of ``'%' + hex(ord(<character>))[2:]`` escapes a character properly.
290 Case does not matter on the hex letters.
291
292 The various character sets specified are:
Tim Petersc2659cf2003-05-12 20:19:37 +0000293
Brett Cannon74bfd702003-04-25 09:39:47 +0000294 Reserved characters : ";/?:@&=+$,"
295 Have special meaning in URIs and must be escaped if not being used for
296 their special meaning
297 Data characters : letters, digits, and "-_.!~*'()"
298 Unreserved and do not need to be escaped; can be, though, if desired
299 Control characters : 0x00 - 0x1F, 0x7F
300 Have no use in URIs so must be escaped
301 space : 0x20
302 Must be escaped
303 Delimiters : '<>#%"'
304 Must be escaped
305 Unwise : "{}|\^[]`"
306 Must be escaped
Tim Petersc2659cf2003-05-12 20:19:37 +0000307
Brett Cannon74bfd702003-04-25 09:39:47 +0000308 """
309
310 def test_never_quote(self):
311 # Make sure quote() does not quote letters, digits, and "_,.-"
312 do_not_quote = '' .join(["ABCDEFGHIJKLMNOPQRSTUVWXYZ",
313 "abcdefghijklmnopqrstuvwxyz",
314 "0123456789",
315 "_.-"])
316 result = urllib.quote(do_not_quote)
317 self.assertEqual(do_not_quote, result,
318 "using quote(): %s != %s" % (do_not_quote, result))
319 result = urllib.quote_plus(do_not_quote)
320 self.assertEqual(do_not_quote, result,
321 "using quote_plus(): %s != %s" % (do_not_quote, result))
322
323 def test_default_safe(self):
324 # Test '/' is default value for 'safe' parameter
Neal Norwitz221085d2007-02-25 20:55:47 +0000325 self.assertEqual(urllib.quote.__defaults__[0], '/')
Brett Cannon74bfd702003-04-25 09:39:47 +0000326
327 def test_safe(self):
328 # Test setting 'safe' parameter does what it should do
329 quote_by_default = "<>"
330 result = urllib.quote(quote_by_default, safe=quote_by_default)
331 self.assertEqual(quote_by_default, result,
332 "using quote(): %s != %s" % (quote_by_default, result))
333 result = urllib.quote_plus(quote_by_default, safe=quote_by_default)
334 self.assertEqual(quote_by_default, result,
335 "using quote_plus(): %s != %s" %
336 (quote_by_default, result))
337
338 def test_default_quoting(self):
339 # Make sure all characters that should be quoted are by default sans
340 # space (separate test for that).
341 should_quote = [chr(num) for num in range(32)] # For 0x00 - 0x1F
342 should_quote.append('<>#%"{}|\^[]`')
343 should_quote.append(chr(127)) # For 0x7F
344 should_quote = ''.join(should_quote)
345 for char in should_quote:
346 result = urllib.quote(char)
347 self.assertEqual(hexescape(char), result,
348 "using quote(): %s should be escaped to %s, not %s" %
349 (char, hexescape(char), result))
350 result = urllib.quote_plus(char)
351 self.assertEqual(hexescape(char), result,
352 "using quote_plus(): "
Tim Petersc2659cf2003-05-12 20:19:37 +0000353 "%s should be escapes to %s, not %s" %
Brett Cannon74bfd702003-04-25 09:39:47 +0000354 (char, hexescape(char), result))
355 del should_quote
356 partial_quote = "ab[]cd"
357 expected = "ab%5B%5Dcd"
358 result = urllib.quote(partial_quote)
359 self.assertEqual(expected, result,
360 "using quote(): %s != %s" % (expected, result))
361 self.assertEqual(expected, result,
362 "using quote_plus(): %s != %s" % (expected, result))
363
364 def test_quoting_space(self):
365 # Make sure quote() and quote_plus() handle spaces as specified in
366 # their unique way
367 result = urllib.quote(' ')
368 self.assertEqual(result, hexescape(' '),
369 "using quote(): %s != %s" % (result, hexescape(' ')))
370 result = urllib.quote_plus(' ')
371 self.assertEqual(result, '+',
372 "using quote_plus(): %s != +" % result)
373 given = "a b cd e f"
374 expect = given.replace(' ', hexescape(' '))
375 result = urllib.quote(given)
376 self.assertEqual(expect, result,
377 "using quote(): %s != %s" % (expect, result))
378 expect = given.replace(' ', '+')
379 result = urllib.quote_plus(given)
380 self.assertEqual(expect, result,
381 "using quote_plus(): %s != %s" % (expect, result))
382
Raymond Hettinger2bdec7b2005-09-10 14:30:09 +0000383 def test_quoting_plus(self):
384 self.assertEqual(urllib.quote_plus('alpha+beta gamma'),
385 'alpha%2Bbeta+gamma')
386 self.assertEqual(urllib.quote_plus('alpha+beta gamma', '+'),
387 'alpha+beta+gamma')
388
Brett Cannon74bfd702003-04-25 09:39:47 +0000389class UnquotingTests(unittest.TestCase):
390 """Tests for unquote() and unquote_plus()
Tim Petersc2659cf2003-05-12 20:19:37 +0000391
Brett Cannon74bfd702003-04-25 09:39:47 +0000392 See the doc string for quoting_Tests for details on quoting and such.
393
394 """
395
396 def test_unquoting(self):
397 # Make sure unquoting of all ASCII values works
398 escape_list = []
399 for num in range(128):
400 given = hexescape(chr(num))
401 expect = chr(num)
402 result = urllib.unquote(given)
403 self.assertEqual(expect, result,
404 "using unquote(): %s != %s" % (expect, result))
405 result = urllib.unquote_plus(given)
406 self.assertEqual(expect, result,
407 "using unquote_plus(): %s != %s" %
408 (expect, result))
409 escape_list.append(given)
410 escape_string = ''.join(escape_list)
411 del escape_list
412 result = urllib.unquote(escape_string)
413 self.assertEqual(result.count('%'), 1,
414 "using quote(): not all characters escaped; %s" %
415 result)
416 result = urllib.unquote(escape_string)
417 self.assertEqual(result.count('%'), 1,
418 "using unquote(): not all characters escaped: "
419 "%s" % result)
420
421 def test_unquoting_parts(self):
422 # Make sure unquoting works when have non-quoted characters
423 # interspersed
424 given = 'ab%sd' % hexescape('c')
425 expect = "abcd"
426 result = urllib.unquote(given)
427 self.assertEqual(expect, result,
428 "using quote(): %s != %s" % (expect, result))
429 result = urllib.unquote_plus(given)
430 self.assertEqual(expect, result,
431 "using unquote_plus(): %s != %s" % (expect, result))
Tim Petersc2659cf2003-05-12 20:19:37 +0000432
Brett Cannon74bfd702003-04-25 09:39:47 +0000433 def test_unquoting_plus(self):
434 # Test difference between unquote() and unquote_plus()
435 given = "are+there+spaces..."
436 expect = given
437 result = urllib.unquote(given)
438 self.assertEqual(expect, result,
439 "using unquote(): %s != %s" % (expect, result))
440 expect = given.replace('+', ' ')
441 result = urllib.unquote_plus(given)
442 self.assertEqual(expect, result,
443 "using unquote_plus(): %s != %s" % (expect, result))
444
Raymond Hettinger4b0f20d2005-10-15 16:41:53 +0000445 def test_unquote_with_unicode(self):
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000446 r = urllib.unquote('br%C3%BCckner_sapporo_20050930.doc')
447 self.assertEqual(r, 'br\xc3\xbcckner_sapporo_20050930.doc')
Raymond Hettinger4b0f20d2005-10-15 16:41:53 +0000448
Brett Cannon74bfd702003-04-25 09:39:47 +0000449class urlencode_Tests(unittest.TestCase):
450 """Tests for urlencode()"""
451
452 def help_inputtype(self, given, test_type):
453 """Helper method for testing different input types.
Tim Petersc2659cf2003-05-12 20:19:37 +0000454
Brett Cannon74bfd702003-04-25 09:39:47 +0000455 'given' must lead to only the pairs:
456 * 1st, 1
457 * 2nd, 2
458 * 3rd, 3
Tim Petersc2659cf2003-05-12 20:19:37 +0000459
Brett Cannon74bfd702003-04-25 09:39:47 +0000460 Test cannot assume anything about order. Docs make no guarantee and
461 have possible dictionary input.
Tim Petersc2659cf2003-05-12 20:19:37 +0000462
Brett Cannon74bfd702003-04-25 09:39:47 +0000463 """
464 expect_somewhere = ["1st=1", "2nd=2", "3rd=3"]
465 result = urllib.urlencode(given)
466 for expected in expect_somewhere:
467 self.assert_(expected in result,
468 "testing %s: %s not found in %s" %
469 (test_type, expected, result))
470 self.assertEqual(result.count('&'), 2,
471 "testing %s: expected 2 '&'s; got %s" %
472 (test_type, result.count('&')))
473 amp_location = result.index('&')
474 on_amp_left = result[amp_location - 1]
475 on_amp_right = result[amp_location + 1]
476 self.assert_(on_amp_left.isdigit() and on_amp_right.isdigit(),
477 "testing %s: '&' not located in proper place in %s" %
478 (test_type, result))
479 self.assertEqual(len(result), (5 * 3) + 2, #5 chars per thing and amps
480 "testing %s: "
481 "unexpected number of characters: %s != %s" %
482 (test_type, len(result), (5 * 3) + 2))
483
484 def test_using_mapping(self):
485 # Test passing in a mapping object as an argument.
486 self.help_inputtype({"1st":'1', "2nd":'2', "3rd":'3'},
487 "using dict as input type")
488
489 def test_using_sequence(self):
490 # Test passing in a sequence of two-item sequences as an argument.
491 self.help_inputtype([('1st', '1'), ('2nd', '2'), ('3rd', '3')],
492 "using sequence of two-item tuples as input")
493
494 def test_quoting(self):
495 # Make sure keys and values are quoted using quote_plus()
496 given = {"&":"="}
497 expect = "%s=%s" % (hexescape('&'), hexescape('='))
498 result = urllib.urlencode(given)
499 self.assertEqual(expect, result)
500 given = {"key name":"A bunch of pluses"}
501 expect = "key+name=A+bunch+of+pluses"
502 result = urllib.urlencode(given)
503 self.assertEqual(expect, result)
504
505 def test_doseq(self):
506 # Test that passing True for 'doseq' parameter works correctly
507 given = {'sequence':['1', '2', '3']}
508 expect = "sequence=%s" % urllib.quote_plus(str(['1', '2', '3']))
509 result = urllib.urlencode(given)
510 self.assertEqual(expect, result)
511 result = urllib.urlencode(given, True)
512 for value in given["sequence"]:
513 expect = "sequence=%s" % value
514 self.assert_(expect in result,
515 "%s not found in %s" % (expect, result))
516 self.assertEqual(result.count('&'), 2,
517 "Expected 2 '&'s, got %s" % result.count('&'))
518
519class Pathname_Tests(unittest.TestCase):
520 """Test pathname2url() and url2pathname()"""
521
522 def test_basic(self):
523 # Make sure simple tests pass
524 expected_path = os.path.join("parts", "of", "a", "path")
525 expected_url = "parts/of/a/path"
526 result = urllib.pathname2url(expected_path)
527 self.assertEqual(expected_url, result,
528 "pathname2url() failed; %s != %s" %
529 (result, expected_url))
530 result = urllib.url2pathname(expected_url)
531 self.assertEqual(expected_path, result,
532 "url2pathame() failed; %s != %s" %
533 (result, expected_path))
534
535 def test_quoting(self):
536 # Test automatic quoting and unquoting works for pathnam2url() and
537 # url2pathname() respectively
538 given = os.path.join("needs", "quot=ing", "here")
539 expect = "needs/%s/here" % urllib.quote("quot=ing")
540 result = urllib.pathname2url(given)
541 self.assertEqual(expect, result,
542 "pathname2url() failed; %s != %s" %
543 (expect, result))
544 expect = given
545 result = urllib.url2pathname(result)
546 self.assertEqual(expect, result,
547 "url2pathname() failed; %s != %s" %
548 (expect, result))
549 given = os.path.join("make sure", "using_quote")
550 expect = "%s/using_quote" % urllib.quote("make sure")
551 result = urllib.pathname2url(given)
552 self.assertEqual(expect, result,
553 "pathname2url() failed; %s != %s" %
554 (expect, result))
555 given = "make+sure/using_unquote"
556 expect = os.path.join("make+sure", "using_unquote")
557 result = urllib.url2pathname(given)
558 self.assertEqual(expect, result,
559 "url2pathname() failed; %s != %s" %
560 (expect, result))
Tim Petersc2659cf2003-05-12 20:19:37 +0000561
Guido van Rossume7ba4952007-06-06 23:52:48 +0000562# Just commented them out.
563# Can't really tell why keep failing in windows and sparc.
564# Everywhere else they work ok, but on those machines, someteimes
565# fail in one of the tests, sometimes in other. I have a linux, and
566# the tests go ok.
567# If anybody has one of the problematic enviroments, please help!
568# . Facundo
569#
570# def server(evt):
571# serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
572# serv.settimeout(3)
573# serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
574# serv.bind(("", 9093))
575# serv.listen(5)
576# try:
577# conn, addr = serv.accept()
578# conn.send("1 Hola mundo\n")
579# cantdata = 0
580# while cantdata < 13:
581# data = conn.recv(13-cantdata)
582# cantdata += len(data)
583# time.sleep(.3)
584# conn.send("2 No more lines\n")
585# conn.close()
586# except socket.timeout:
587# pass
588# finally:
589# serv.close()
590# evt.set()
591#
592# class FTPWrapperTests(unittest.TestCase):
593#
594# def setUp(self):
595# ftplib.FTP.port = 9093
596# self.evt = threading.Event()
597# threading.Thread(target=server, args=(self.evt,)).start()
598# time.sleep(.1)
599#
600# def tearDown(self):
601# self.evt.wait()
602#
603# def testBasic(self):
604# # connects
605# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
606# ftp.ftp.sock.close()
607#
608# def testTimeoutDefault(self):
609# # default
610# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
611# self.assertTrue(ftp.ftp.sock.gettimeout() is None)
612# ftp.ftp.sock.close()
613#
614# def testTimeoutValue(self):
615# # a value
616# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [], timeout=30)
617# self.assertEqual(ftp.ftp.sock.gettimeout(), 30)
618# ftp.ftp.sock.close()
619#
620# def testTimeoutNone(self):
621# # None, having other default
622# previous = socket.getdefaulttimeout()
623# socket.setdefaulttimeout(30)
624# try:
625# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [])
626# finally:
627# socket.setdefaulttimeout(previous)
628# self.assertEqual(ftp.ftp.sock.gettimeout(), 30)
629# ftp.ftp.close()
630#
631
Skip Montanaro080c9972001-01-28 21:12:22 +0000632
633
Brett Cannon74bfd702003-04-25 09:39:47 +0000634def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000635 support.run_unittest(
Walter Dörwald21d3a322003-05-01 17:45:56 +0000636 urlopen_FileTests,
Hye-Shik Chang39aef792004-06-05 13:30:56 +0000637 urlopen_HttpTests,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000638 urlretrieve_FileTests,
639 QuotingTests,
640 UnquotingTests,
641 urlencode_Tests,
Guido van Rossume7ba4952007-06-06 23:52:48 +0000642 Pathname_Tests,
643 #FTPWrapperTests,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000644 )
Brett Cannon74bfd702003-04-25 09:39:47 +0000645
646
647
648if __name__ == '__main__':
649 test_main()