move sections
[python/dscho.git] / Lib / test / test_cgi.py
blob554bf06ee9c4534a30406752f202eb6199738df1
1 from test.test_support import run_unittest, check_warnings
2 import cgi
3 import os
4 import sys
5 import tempfile
6 import unittest
8 class HackedSysModule:
9 # The regression test will have real values in sys.argv, which
10 # will completely confuse the test of the cgi module
11 argv = []
12 stdin = sys.stdin
14 cgi.sys = HackedSysModule()
16 try:
17 from cStringIO import StringIO
18 except ImportError:
19 from StringIO import StringIO
21 class ComparableException:
22 def __init__(self, err):
23 self.err = err
25 def __str__(self):
26 return str(self.err)
28 def __cmp__(self, anExc):
29 if not isinstance(anExc, Exception):
30 return -1
31 x = cmp(self.err.__class__, anExc.__class__)
32 if x != 0:
33 return x
34 return cmp(self.err.args, anExc.args)
36 def __getattr__(self, attr):
37 return getattr(self.err, attr)
39 def do_test(buf, method):
40 env = {}
41 if method == "GET":
42 fp = None
43 env['REQUEST_METHOD'] = 'GET'
44 env['QUERY_STRING'] = buf
45 elif method == "POST":
46 fp = StringIO(buf)
47 env['REQUEST_METHOD'] = 'POST'
48 env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
49 env['CONTENT_LENGTH'] = str(len(buf))
50 else:
51 raise ValueError, "unknown method: %s" % method
52 try:
53 return cgi.parse(fp, env, strict_parsing=1)
54 except StandardError, err:
55 return ComparableException(err)
57 parse_strict_test_cases = [
58 ("", ValueError("bad query field: ''")),
59 ("&", ValueError("bad query field: ''")),
60 ("&&", ValueError("bad query field: ''")),
61 (";", ValueError("bad query field: ''")),
62 (";&;", ValueError("bad query field: ''")),
63 # Should the next few really be valid?
64 ("=", {}),
65 ("=&=", {}),
66 ("=;=", {}),
67 # This rest seem to make sense
68 ("=a", {'': ['a']}),
69 ("&=a", ValueError("bad query field: ''")),
70 ("=a&", ValueError("bad query field: ''")),
71 ("=&a", ValueError("bad query field: 'a'")),
72 ("b=a", {'b': ['a']}),
73 ("b+=a", {'b ': ['a']}),
74 ("a=b=a", {'a': ['b=a']}),
75 ("a=+b=a", {'a': [' b=a']}),
76 ("&b=a", ValueError("bad query field: ''")),
77 ("b&=a", ValueError("bad query field: 'b'")),
78 ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}),
79 ("a=a+b&a=b+a", {'a': ['a b', 'b a']}),
80 ("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
81 ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
82 ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
83 ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env",
84 {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'],
85 'cuyer': ['r'],
86 'expire': ['964546263'],
87 'kid': ['130003.300038'],
88 'lobale': ['en-US'],
89 'order_id': ['0bb2e248638833d48cb7fed300000f1b'],
90 'ss': ['env'],
91 'view': ['bustomer'],
92 }),
94 ("group_id=5470&set=custom&_assigned_to=31392&_status=1&_category=100&SUBMIT=Browse",
95 {'SUBMIT': ['Browse'],
96 '_assigned_to': ['31392'],
97 '_category': ['100'],
98 '_status': ['1'],
99 'group_id': ['5470'],
100 'set': ['custom'],
104 def first_elts(list):
105 return map(lambda x:x[0], list)
107 def first_second_elts(list):
108 return map(lambda p:(p[0], p[1][0]), list)
110 def gen_result(data, environ):
111 fake_stdin = StringIO(data)
112 fake_stdin.seek(0)
113 form = cgi.FieldStorage(fp=fake_stdin, environ=environ)
115 result = {}
116 for k, v in dict(form).items():
117 result[k] = isinstance(v, list) and form.getlist(k) or v.value
119 return result
121 class CgiTests(unittest.TestCase):
123 def test_strict(self):
124 for orig, expect in parse_strict_test_cases:
125 # Test basic parsing
126 d = do_test(orig, "GET")
127 self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
128 d = do_test(orig, "POST")
129 self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
131 env = {'QUERY_STRING': orig}
132 fcd = cgi.FormContentDict(env)
133 sd = cgi.SvFormContentDict(env)
134 fs = cgi.FieldStorage(environ=env)
135 if isinstance(expect, dict):
136 # test dict interface
137 self.assertEqual(len(expect), len(fcd))
138 self.assertItemsEqual(expect.keys(), fcd.keys())
139 self.assertItemsEqual(expect.values(), fcd.values())
140 self.assertItemsEqual(expect.items(), fcd.items())
141 self.assertEqual(fcd.get("nonexistent field", "default"), "default")
142 self.assertEqual(len(sd), len(fs))
143 self.assertItemsEqual(sd.keys(), fs.keys())
144 self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
145 # test individual fields
146 for key in expect.keys():
147 expect_val = expect[key]
148 self.assertTrue(fcd.has_key(key))
149 self.assertItemsEqual(fcd[key], expect[key])
150 self.assertEqual(fcd.get(key, "default"), fcd[key])
151 self.assertTrue(fs.has_key(key))
152 if len(expect_val) > 1:
153 single_value = 0
154 else:
155 single_value = 1
156 try:
157 val = sd[key]
158 except IndexError:
159 self.assertFalse(single_value)
160 self.assertEqual(fs.getvalue(key), expect_val)
161 else:
162 self.assertTrue(single_value)
163 self.assertEqual(val, expect_val[0])
164 self.assertEqual(fs.getvalue(key), expect_val[0])
165 self.assertItemsEqual(sd.getlist(key), expect_val)
166 if single_value:
167 self.assertItemsEqual(sd.values(),
168 first_elts(expect.values()))
169 self.assertItemsEqual(sd.items(),
170 first_second_elts(expect.items()))
172 def test_weird_formcontentdict(self):
173 # Test the weird FormContentDict classes
174 env = {'QUERY_STRING': "x=1&y=2.0&z=2-3.%2b0&1=1abc"}
175 expect = {'x': 1, 'y': 2.0, 'z': '2-3.+0', '1': '1abc'}
176 d = cgi.InterpFormContentDict(env)
177 for k, v in expect.items():
178 self.assertEqual(d[k], v)
179 for k, v in d.items():
180 self.assertEqual(expect[k], v)
181 self.assertItemsEqual(expect.values(), d.values())
183 def test_log(self):
184 cgi.log("Testing")
186 cgi.logfp = StringIO()
187 cgi.initlog("%s", "Testing initlog 1")
188 cgi.log("%s", "Testing log 2")
189 self.assertEqual(cgi.logfp.getvalue(), "Testing initlog 1\nTesting log 2\n")
190 if os.path.exists("/dev/null"):
191 cgi.logfp = None
192 cgi.logfile = "/dev/null"
193 cgi.initlog("%s", "Testing log 3")
194 cgi.log("Testing log 4")
196 def test_fieldstorage_readline(self):
197 # FieldStorage uses readline, which has the capacity to read all
198 # contents of the input file into memory; we use readline's size argument
199 # to prevent that for files that do not contain any newlines in
200 # non-GET/HEAD requests
201 class TestReadlineFile:
202 def __init__(self, file):
203 self.file = file
204 self.numcalls = 0
206 def readline(self, size=None):
207 self.numcalls += 1
208 if size:
209 return self.file.readline(size)
210 else:
211 return self.file.readline()
213 def __getattr__(self, name):
214 file = self.__dict__['file']
215 a = getattr(file, name)
216 if not isinstance(a, int):
217 setattr(self, name, a)
218 return a
220 f = TestReadlineFile(tempfile.TemporaryFile())
221 f.write('x' * 256 * 1024)
222 f.seek(0)
223 env = {'REQUEST_METHOD':'PUT'}
224 fs = cgi.FieldStorage(fp=f, environ=env)
225 # if we're not chunking properly, readline is only called twice
226 # (by read_binary); if we are chunking properly, it will be called 5 times
227 # as long as the chunksize is 1 << 16.
228 self.assertTrue(f.numcalls > 2)
230 def test_fieldstorage_multipart(self):
231 #Test basic FieldStorage multipart parsing
232 env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------721837373350705526688164684', 'CONTENT_LENGTH':'558'}
233 postdata = """-----------------------------721837373350705526688164684
234 Content-Disposition: form-data; name="id"
236 1234
237 -----------------------------721837373350705526688164684
238 Content-Disposition: form-data; name="title"
241 -----------------------------721837373350705526688164684
242 Content-Disposition: form-data; name="file"; filename="test.txt"
243 Content-Type: text/plain
245 Testing 123.
247 -----------------------------721837373350705526688164684
248 Content-Disposition: form-data; name="submit"
250 Add\x20
251 -----------------------------721837373350705526688164684--
253 fs = cgi.FieldStorage(fp=StringIO(postdata), environ=env)
254 self.assertEquals(len(fs.list), 4)
255 expect = [{'name':'id', 'filename':None, 'value':'1234'},
256 {'name':'title', 'filename':None, 'value':''},
257 {'name':'file', 'filename':'test.txt','value':'Testing 123.\n'},
258 {'name':'submit', 'filename':None, 'value':' Add '}]
259 for x in range(len(fs.list)):
260 for k, exp in expect[x].items():
261 got = getattr(fs.list[x], k)
262 self.assertEquals(got, exp)
264 _qs_result = {
265 'key1': 'value1',
266 'key2': ['value2x', 'value2y'],
267 'key3': 'value3',
268 'key4': 'value4'
270 def testQSAndUrlEncode(self):
271 data = "key2=value2x&key3=value3&key4=value4"
272 environ = {
273 'CONTENT_LENGTH': str(len(data)),
274 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
275 'QUERY_STRING': 'key1=value1&key2=value2y',
276 'REQUEST_METHOD': 'POST',
278 v = gen_result(data, environ)
279 self.assertEqual(self._qs_result, v)
281 def testQSAndFormData(self):
282 data = """
283 ---123
284 Content-Disposition: form-data; name="key2"
286 value2y
287 ---123
288 Content-Disposition: form-data; name="key3"
290 value3
291 ---123
292 Content-Disposition: form-data; name="key4"
294 value4
295 ---123--
297 environ = {
298 'CONTENT_LENGTH': str(len(data)),
299 'CONTENT_TYPE': 'multipart/form-data; boundary=-123',
300 'QUERY_STRING': 'key1=value1&key2=value2x',
301 'REQUEST_METHOD': 'POST',
303 v = gen_result(data, environ)
304 self.assertEqual(self._qs_result, v)
306 def testQSAndFormDataFile(self):
307 data = """
308 ---123
309 Content-Disposition: form-data; name="key2"
311 value2y
312 ---123
313 Content-Disposition: form-data; name="key3"
315 value3
316 ---123
317 Content-Disposition: form-data; name="key4"
319 value4
320 ---123
321 Content-Disposition: form-data; name="upload"; filename="fake.txt"
322 Content-Type: text/plain
324 this is the content of the fake file
326 ---123--
328 environ = {
329 'CONTENT_LENGTH': str(len(data)),
330 'CONTENT_TYPE': 'multipart/form-data; boundary=-123',
331 'QUERY_STRING': 'key1=value1&key2=value2x',
332 'REQUEST_METHOD': 'POST',
334 result = self._qs_result.copy()
335 result.update({
336 'upload': 'this is the content of the fake file\n'
338 v = gen_result(data, environ)
339 self.assertEqual(result, v)
341 def test_deprecated_parse_qs(self):
342 # this func is moved to urlparse, this is just a sanity check
343 with check_warnings(('cgi.parse_qs is deprecated, use urlparse.'
344 'parse_qs instead', PendingDeprecationWarning)):
345 self.assertEqual({'a': ['A1'], 'B': ['B3'], 'b': ['B2']},
346 cgi.parse_qs('a=A1&b=B2&B=B3'))
348 def test_deprecated_parse_qsl(self):
349 # this func is moved to urlparse, this is just a sanity check
350 with check_warnings(('cgi.parse_qsl is deprecated, use urlparse.'
351 'parse_qsl instead', PendingDeprecationWarning)):
352 self.assertEqual([('a', 'A1'), ('b', 'B2'), ('B', 'B3')],
353 cgi.parse_qsl('a=A1&b=B2&B=B3'))
355 def test_parse_header(self):
356 self.assertEqual(
357 cgi.parse_header("text/plain"),
358 ("text/plain", {}))
359 self.assertEqual(
360 cgi.parse_header("text/vnd.just.made.this.up ; "),
361 ("text/vnd.just.made.this.up", {}))
362 self.assertEqual(
363 cgi.parse_header("text/plain;charset=us-ascii"),
364 ("text/plain", {"charset": "us-ascii"}))
365 self.assertEqual(
366 cgi.parse_header('text/plain ; charset="us-ascii"'),
367 ("text/plain", {"charset": "us-ascii"}))
368 self.assertEqual(
369 cgi.parse_header('text/plain ; charset="us-ascii"; another=opt'),
370 ("text/plain", {"charset": "us-ascii", "another": "opt"}))
371 self.assertEqual(
372 cgi.parse_header('attachment; filename="silly.txt"'),
373 ("attachment", {"filename": "silly.txt"}))
374 self.assertEqual(
375 cgi.parse_header('attachment; filename="strange;name"'),
376 ("attachment", {"filename": "strange;name"}))
377 self.assertEqual(
378 cgi.parse_header('attachment; filename="strange;name";size=123;'),
379 ("attachment", {"filename": "strange;name", "size": "123"}))
382 def test_main():
383 run_unittest(CgiTests)
385 if __name__ == '__main__':
386 test_main()