blob: c3c2c6cddc0a143ada73fed24cbd591ba5438472 [file] [log] [blame]
Georg Brandle1844332006-10-29 20:09:12 +00001from test.test_support import run_unittest
Jeremy Hyltond9827c42000-08-03 22:11:43 +00002import cgi
3import os
4import sys
Guido van Rossum9568b732006-08-10 17:41:07 +00005import tempfile
Georg Brandle1844332006-10-29 20:09:12 +00006import unittest
Guido van Rossum9568b732006-08-10 17:41:07 +00007from StringIO import StringIO
Jeremy Hyltond9827c42000-08-03 22:11:43 +00008
9class HackedSysModule:
10 # The regression test will have real values in sys.argv, which
Fred Drake004d5e62000-10-23 17:22:08 +000011 # will completely confuse the test of the cgi module
Jeremy Hyltond9827c42000-08-03 22:11:43 +000012 argv = []
13 stdin = sys.stdin
14
15cgi.sys = HackedSysModule()
16
17try:
18 from cStringIO import StringIO
19except ImportError:
20 from StringIO import StringIO
21
22class ComparableException:
23 def __init__(self, err):
24 self.err = err
25
26 def __str__(self):
27 return str(self.err)
28
29 def __cmp__(self, anExc):
30 if not isinstance(anExc, Exception):
31 return -1
32 x = cmp(self.err.__class__, anExc.__class__)
33 if x != 0:
34 return x
35 return cmp(self.err.args, anExc.args)
36
37 def __getattr__(self, attr):
Guido van Rossum846d6db2001-01-17 15:08:37 +000038 return getattr(self.err, attr)
Jeremy Hyltond9827c42000-08-03 22:11:43 +000039
40def do_test(buf, method):
41 env = {}
42 if method == "GET":
43 fp = None
44 env['REQUEST_METHOD'] = 'GET'
45 env['QUERY_STRING'] = buf
46 elif method == "POST":
47 fp = StringIO(buf)
48 env['REQUEST_METHOD'] = 'POST'
49 env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
50 env['CONTENT_LENGTH'] = str(len(buf))
51 else:
52 raise ValueError, "unknown method: %s" % method
53 try:
54 return cgi.parse(fp, env, strict_parsing=1)
55 except StandardError, err:
56 return ComparableException(err)
57
58# A list of test cases. Each test case is a a two-tuple that contains
59# a string with the query and a dictionary with the expected result.
Fred Drake004d5e62000-10-23 17:22:08 +000060
Neil Schemenauer66edb622004-07-19 15:38:11 +000061parse_qsl_test_cases = [
62 ("", []),
63 ("&", []),
64 ("&&", []),
65 ("=", [('', '')]),
66 ("=a", [('', 'a')]),
67 ("a", [('a', '')]),
68 ("a=", [('a', '')]),
69 ("a=", [('a', '')]),
70 ("&a=b", [('a', 'b')]),
71 ("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]),
72 ("a=1&a=2", [('a', '1'), ('a', '2')]),
73]
74
75parse_strict_test_cases = [
Jeremy Hyltond9827c42000-08-03 22:11:43 +000076 ("", ValueError("bad query field: ''")),
77 ("&", ValueError("bad query field: ''")),
78 ("&&", ValueError("bad query field: ''")),
Jeremy Hyltonafde7e22000-09-15 20:06:57 +000079 (";", ValueError("bad query field: ''")),
80 (";&;", ValueError("bad query field: ''")),
Jeremy Hyltond9827c42000-08-03 22:11:43 +000081 # Should the next few really be valid?
82 ("=", {}),
83 ("=&=", {}),
Jeremy Hyltonafde7e22000-09-15 20:06:57 +000084 ("=;=", {}),
Jeremy Hyltond9827c42000-08-03 22:11:43 +000085 # This rest seem to make sense
86 ("=a", {'': ['a']}),
87 ("&=a", ValueError("bad query field: ''")),
88 ("=a&", ValueError("bad query field: ''")),
89 ("=&a", ValueError("bad query field: 'a'")),
90 ("b=a", {'b': ['a']}),
91 ("b+=a", {'b ': ['a']}),
92 ("a=b=a", {'a': ['b=a']}),
93 ("a=+b=a", {'a': [' b=a']}),
94 ("&b=a", ValueError("bad query field: ''")),
95 ("b&=a", ValueError("bad query field: 'b'")),
96 ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}),
97 ("a=a+b&a=b+a", {'a': ['a b', 'b a']}),
98 ("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
Jeremy Hyltonafde7e22000-09-15 20:06:57 +000099 ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
100 ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000101 ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env",
102 {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'],
103 'cuyer': ['r'],
104 'expire': ['964546263'],
105 'kid': ['130003.300038'],
106 'lobale': ['en-US'],
107 'order_id': ['0bb2e248638833d48cb7fed300000f1b'],
108 'ss': ['env'],
109 'view': ['bustomer'],
110 }),
Fred Drake004d5e62000-10-23 17:22:08 +0000111
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000112 ("group_id=5470&set=custom&_assigned_to=31392&_status=1&_category=100&SUBMIT=Browse",
113 {'SUBMIT': ['Browse'],
114 '_assigned_to': ['31392'],
115 '_category': ['100'],
116 '_status': ['1'],
117 'group_id': ['5470'],
118 'set': ['custom'],
119 })
120 ]
121
122def norm(list):
123 if type(list) == type([]):
124 list.sort()
125 return list
126
127def first_elts(list):
128 return map(lambda x:x[0], list)
129
130def first_second_elts(list):
131 return map(lambda p:(p[0], p[1][0]), list)
132
Georg Brandle1844332006-10-29 20:09:12 +0000133class CgiTests(unittest.TestCase):
Neil Schemenauer66edb622004-07-19 15:38:11 +0000134
Georg Brandle1844332006-10-29 20:09:12 +0000135 def test_qsl(self):
136 for orig, expect in parse_qsl_test_cases:
137 result = cgi.parse_qsl(orig, keep_blank_values=True)
138 self.assertEqual(result, expect, "Error parsing %s" % repr(orig))
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000139
Georg Brandle1844332006-10-29 20:09:12 +0000140 def test_strict(self):
141 for orig, expect in parse_strict_test_cases:
142 # Test basic parsing
143 d = do_test(orig, "GET")
144 self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
145 d = do_test(orig, "POST")
146 self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
147
148 env = {'QUERY_STRING': orig}
149 fcd = cgi.FormContentDict(env)
150 sd = cgi.SvFormContentDict(env)
151 fs = cgi.FieldStorage(environ=env)
152 if type(expect) == type({}):
153 # test dict interface
154 self.assertEqual(len(expect), len(fcd))
155 self.assertEqual(norm(expect.keys()), norm(fcd.keys()))
156 self.assertEqual(norm(expect.values()), norm(fcd.values()))
157 self.assertEqual(norm(expect.items()), norm(fcd.items()))
158 self.assertEqual(fcd.get("nonexistent field", "default"), "default")
159 self.assertEqual(len(sd), len(fs))
160 self.assertEqual(norm(sd.keys()), norm(fs.keys()))
161 self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
162 # test individual fields
163 for key in expect.keys():
164 expect_val = expect[key]
165 self.assert_(fcd.has_key(key))
166 self.assertEqual(norm(fcd[key]), norm(expect[key]))
167 self.assertEqual(fcd.get(key, "default"), fcd[key])
168 self.assert_(fs.has_key(key))
169 if len(expect_val) > 1:
170 single_value = 0
171 else:
172 single_value = 1
173 try:
174 val = sd[key]
175 except IndexError:
176 self.failIf(single_value)
177 self.assertEqual(fs.getvalue(key), expect_val)
178 else:
179 self.assert_(single_value)
180 self.assertEqual(val, expect_val[0])
181 self.assertEqual(fs.getvalue(key), expect_val[0])
182 self.assertEqual(norm(sd.getlist(key)), norm(expect_val))
183 if single_value:
184 self.assertEqual(norm(sd.values()),
185 first_elts(norm(expect.values())))
186 self.assertEqual(norm(sd.items()),
187 first_second_elts(norm(expect.items())))
188
189 def test_weird_formcontentdict(self):
190 # Test the weird FormContentDict classes
191 env = {'QUERY_STRING': "x=1&y=2.0&z=2-3.%2b0&1=1abc"}
192 expect = {'x': 1, 'y': 2.0, 'z': '2-3.+0', '1': '1abc'}
193 d = cgi.InterpFormContentDict(env)
194 for k, v in expect.items():
195 self.assertEqual(d[k], v)
196 for k, v in d.items():
197 self.assertEqual(expect[k], v)
198 self.assertEqual(norm(expect.values()), norm(d.values()))
199
200 def test_log(self):
201 cgi.log("Testing")
202
203 cgi.logfp = StringIO()
204 cgi.initlog("%s", "Testing initlog 1")
205 cgi.log("%s", "Testing log 2")
206 self.assertEqual(cgi.logfp.getvalue(), "Testing initlog 1\nTesting log 2\n")
207 if os.path.exists("/dev/null"):
208 cgi.logfp = None
209 cgi.logfile = "/dev/null"
210 cgi.initlog("%s", "Testing log 3")
211 cgi.log("Testing log 4")
212
213 def test_fieldstorage_readline(self):
214 # FieldStorage uses readline, which has the capacity to read all
215 # contents of the input file into memory; we use readline's size argument
216 # to prevent that for files that do not contain any newlines in
217 # non-GET/HEAD requests
218 class TestReadlineFile:
219 def __init__(self, file):
220 self.file = file
221 self.numcalls = 0
222
223 def readline(self, size=None):
224 self.numcalls += 1
225 if size:
226 return self.file.readline(size)
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000227 else:
Georg Brandle1844332006-10-29 20:09:12 +0000228 return self.file.readline()
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000229
Georg Brandle1844332006-10-29 20:09:12 +0000230 def __getattr__(self, name):
231 file = self.__dict__['file']
232 a = getattr(file, name)
233 if not isinstance(a, int):
234 setattr(self, name, a)
235 return a
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000236
Georg Brandle1844332006-10-29 20:09:12 +0000237 f = TestReadlineFile(tempfile.TemporaryFile())
238 f.write('x' * 256 * 1024)
239 f.seek(0)
240 env = {'REQUEST_METHOD':'PUT'}
241 fs = cgi.FieldStorage(fp=f, environ=env)
242 # if we're not chunking properly, readline is only called twice
243 # (by read_binary); if we are chunking properly, it will be called 5 times
244 # as long as the chunksize is 1 << 16.
245 self.assert_(f.numcalls > 2)
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000246
Georg Brandle1844332006-10-29 20:09:12 +0000247 def test_fieldstorage_multipart(self):
248 #Test basic FieldStorage multipart parsing
249 env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------721837373350705526688164684', 'CONTENT_LENGTH':'558'}
250 postdata = """-----------------------------721837373350705526688164684
Guido van Rossum9568b732006-08-10 17:41:07 +0000251Content-Disposition: form-data; name="id"
252
2531234
254-----------------------------721837373350705526688164684
255Content-Disposition: form-data; name="title"
256
257
258-----------------------------721837373350705526688164684
259Content-Disposition: form-data; name="file"; filename="test.txt"
260Content-Type: text/plain
261
262Testing 123.
263
264-----------------------------721837373350705526688164684
265Content-Disposition: form-data; name="submit"
266
Tim Petersb7ad1eb2006-08-10 23:22:13 +0000267 Add\x20
Guido van Rossum9568b732006-08-10 17:41:07 +0000268-----------------------------721837373350705526688164684--
269"""
Georg Brandle1844332006-10-29 20:09:12 +0000270 fs = cgi.FieldStorage(fp=StringIO(postdata), environ=env)
271 self.assertEquals(len(fs.list), 4)
272 expect = [{'name':'id', 'filename':None, 'value':'1234'},
273 {'name':'title', 'filename':None, 'value':''},
274 {'name':'file', 'filename':'test.txt','value':'Testing 123.\n'},
275 {'name':'submit', 'filename':None, 'value':' Add '}]
276 for x in range(len(fs.list)):
277 for k, exp in expect[x].items():
278 got = getattr(fs.list[x], k)
279 self.assertEquals(got, exp)
Guido van Rossum9568b732006-08-10 17:41:07 +0000280
Georg Brandle1844332006-10-29 20:09:12 +0000281def test_main():
282 run_unittest(CgiTests)
283
284if __name__ == '__main__':
285 test_main()