blob: 260d9b2a85ecb47b8aba311dfeb1af2b3c5f24bd [file] [log] [blame]
Thomas Wouters89f507f2006-12-13 04:49:30 +00001from test.test_support import run_unittest
Jeremy Hyltond9827c42000-08-03 22:11:43 +00002import cgi
3import os
4import sys
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00005import tempfile
Thomas Wouters89f507f2006-12-13 04:49:30 +00006import unittest
Thomas Wouters00ee7ba2006-08-21 19:07:27 +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
Guido van Rossum47b9ff62006-08-24 00:41:19 +000029 def __eq__(self, anExc):
Jeremy Hyltond9827c42000-08-03 22:11:43 +000030 if not isinstance(anExc, Exception):
Guido van Rossum47b9ff62006-08-24 00:41:19 +000031 return NotImplemented
32 return (self.err.__class__ == anExc.__class__ and
33 self.err.args == anExc.args)
Jeremy Hyltond9827c42000-08-03 22:11:43 +000034
35 def __getattr__(self, attr):
Guido van Rossum846d6db2001-01-17 15:08:37 +000036 return getattr(self.err, attr)
Jeremy Hyltond9827c42000-08-03 22:11:43 +000037
38def do_test(buf, method):
39 env = {}
40 if method == "GET":
41 fp = None
42 env['REQUEST_METHOD'] = 'GET'
43 env['QUERY_STRING'] = buf
44 elif method == "POST":
45 fp = StringIO(buf)
46 env['REQUEST_METHOD'] = 'POST'
47 env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
48 env['CONTENT_LENGTH'] = str(len(buf))
49 else:
50 raise ValueError, "unknown method: %s" % method
51 try:
52 return cgi.parse(fp, env, strict_parsing=1)
Guido van Rossumb940e112007-01-10 16:19:56 +000053 except StandardError as err:
Jeremy Hyltond9827c42000-08-03 22:11:43 +000054 return ComparableException(err)
55
56# A list of test cases. Each test case is a a two-tuple that contains
57# a string with the query and a dictionary with the expected result.
Fred Drake004d5e62000-10-23 17:22:08 +000058
Neil Schemenauer66edb622004-07-19 15:38:11 +000059parse_qsl_test_cases = [
60 ("", []),
61 ("&", []),
62 ("&&", []),
63 ("=", [('', '')]),
64 ("=a", [('', 'a')]),
65 ("a", [('a', '')]),
66 ("a=", [('a', '')]),
67 ("a=", [('a', '')]),
68 ("&a=b", [('a', 'b')]),
69 ("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]),
70 ("a=1&a=2", [('a', '1'), ('a', '2')]),
71]
72
73parse_strict_test_cases = [
Jeremy Hyltond9827c42000-08-03 22:11:43 +000074 ("", ValueError("bad query field: ''")),
75 ("&", ValueError("bad query field: ''")),
76 ("&&", ValueError("bad query field: ''")),
Jeremy Hyltonafde7e22000-09-15 20:06:57 +000077 (";", ValueError("bad query field: ''")),
78 (";&;", ValueError("bad query field: ''")),
Jeremy Hyltond9827c42000-08-03 22:11:43 +000079 # Should the next few really be valid?
80 ("=", {}),
81 ("=&=", {}),
Jeremy Hyltonafde7e22000-09-15 20:06:57 +000082 ("=;=", {}),
Jeremy Hyltond9827c42000-08-03 22:11:43 +000083 # This rest seem to make sense
84 ("=a", {'': ['a']}),
85 ("&=a", ValueError("bad query field: ''")),
86 ("=a&", ValueError("bad query field: ''")),
87 ("=&a", ValueError("bad query field: 'a'")),
88 ("b=a", {'b': ['a']}),
89 ("b+=a", {'b ': ['a']}),
90 ("a=b=a", {'a': ['b=a']}),
91 ("a=+b=a", {'a': [' b=a']}),
92 ("&b=a", ValueError("bad query field: ''")),
93 ("b&=a", ValueError("bad query field: 'b'")),
94 ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}),
95 ("a=a+b&a=b+a", {'a': ['a b', 'b a']}),
96 ("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 +000097 ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
98 ("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 +000099 ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env",
100 {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'],
101 'cuyer': ['r'],
102 'expire': ['964546263'],
103 'kid': ['130003.300038'],
104 'lobale': ['en-US'],
105 'order_id': ['0bb2e248638833d48cb7fed300000f1b'],
106 'ss': ['env'],
107 'view': ['bustomer'],
108 }),
Fred Drake004d5e62000-10-23 17:22:08 +0000109
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000110 ("group_id=5470&set=custom&_assigned_to=31392&_status=1&_category=100&SUBMIT=Browse",
111 {'SUBMIT': ['Browse'],
112 '_assigned_to': ['31392'],
113 '_category': ['100'],
114 '_status': ['1'],
115 'group_id': ['5470'],
116 'set': ['custom'],
117 })
118 ]
119
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000120def norm(seq):
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000121 return sorted(seq, key=repr)
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000122
123def first_elts(list):
124 return map(lambda x:x[0], list)
125
126def first_second_elts(list):
127 return map(lambda p:(p[0], p[1][0]), list)
128
Thomas Wouters89f507f2006-12-13 04:49:30 +0000129class CgiTests(unittest.TestCase):
Neil Schemenauer66edb622004-07-19 15:38:11 +0000130
Thomas Wouters89f507f2006-12-13 04:49:30 +0000131 def test_qsl(self):
132 for orig, expect in parse_qsl_test_cases:
133 result = cgi.parse_qsl(orig, keep_blank_values=True)
134 self.assertEqual(result, expect, "Error parsing %s" % repr(orig))
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000135
Thomas Wouters89f507f2006-12-13 04:49:30 +0000136 def test_strict(self):
137 for orig, expect in parse_strict_test_cases:
138 # Test basic parsing
139 d = do_test(orig, "GET")
140 self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
141 d = do_test(orig, "POST")
142 self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
143
144 env = {'QUERY_STRING': orig}
145 fcd = cgi.FormContentDict(env)
146 sd = cgi.SvFormContentDict(env)
147 fs = cgi.FieldStorage(environ=env)
148 if type(expect) == type({}):
149 # test dict interface
150 self.assertEqual(len(expect), len(fcd))
151 self.assertEqual(norm(expect.keys()), norm(fcd.keys()))
152 self.assertEqual(norm(expect.values()), norm(fcd.values()))
153 self.assertEqual(norm(expect.items()), norm(fcd.items()))
154 self.assertEqual(fcd.get("nonexistent field", "default"), "default")
155 self.assertEqual(len(sd), len(fs))
156 self.assertEqual(norm(sd.keys()), norm(fs.keys()))
157 self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
158 # test individual fields
159 for key in expect.keys():
160 expect_val = expect[key]
161 self.assert_(key in fcd)
162 self.assertEqual(norm(fcd[key]), norm(expect[key]))
163 self.assertEqual(fcd.get(key, "default"), fcd[key])
164 self.assert_(key in fs)
165 if len(expect_val) > 1:
166 single_value = 0
167 else:
168 single_value = 1
169 try:
170 val = sd[key]
171 except IndexError:
172 self.failIf(single_value)
173 self.assertEqual(fs.getvalue(key), expect_val)
174 else:
175 self.assert_(single_value)
176 self.assertEqual(val, expect_val[0])
177 self.assertEqual(fs.getvalue(key), expect_val[0])
178 self.assertEqual(norm(sd.getlist(key)), norm(expect_val))
179 if single_value:
180 self.assertEqual(norm(sd.values()),
181 first_elts(norm(expect.values())))
182 self.assertEqual(norm(sd.items()),
183 first_second_elts(norm(expect.items())))
184
185 def test_weird_formcontentdict(self):
186 # Test the weird FormContentDict classes
187 env = {'QUERY_STRING': "x=1&y=2.0&z=2-3.%2b0&1=1abc"}
188 expect = {'x': 1, 'y': 2.0, 'z': '2-3.+0', '1': '1abc'}
189 d = cgi.InterpFormContentDict(env)
190 for k, v in expect.items():
191 self.assertEqual(d[k], v)
192 for k, v in d.items():
193 self.assertEqual(expect[k], v)
194 self.assertEqual(norm(expect.values()), norm(d.values()))
195
196 def test_log(self):
197 cgi.log("Testing")
198
199 cgi.logfp = StringIO()
200 cgi.initlog("%s", "Testing initlog 1")
201 cgi.log("%s", "Testing log 2")
202 self.assertEqual(cgi.logfp.getvalue(), "Testing initlog 1\nTesting log 2\n")
203 if os.path.exists("/dev/null"):
204 cgi.logfp = None
205 cgi.logfile = "/dev/null"
206 cgi.initlog("%s", "Testing log 3")
207 cgi.log("Testing log 4")
208
209 def test_fieldstorage_readline(self):
210 # FieldStorage uses readline, which has the capacity to read all
211 # contents of the input file into memory; we use readline's size argument
212 # to prevent that for files that do not contain any newlines in
213 # non-GET/HEAD requests
214 class TestReadlineFile:
215 def __init__(self, file):
216 self.file = file
217 self.numcalls = 0
218
219 def readline(self, size=None):
220 self.numcalls += 1
221 if size:
222 return self.file.readline(size)
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000223 else:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000224 return self.file.readline()
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000225
Thomas Wouters89f507f2006-12-13 04:49:30 +0000226 def __getattr__(self, name):
227 file = self.__dict__['file']
228 a = getattr(file, name)
229 if not isinstance(a, int):
230 setattr(self, name, a)
231 return a
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000232
Thomas Wouters89f507f2006-12-13 04:49:30 +0000233 f = TestReadlineFile(tempfile.TemporaryFile())
234 f.write('x' * 256 * 1024)
235 f.seek(0)
236 env = {'REQUEST_METHOD':'PUT'}
237 fs = cgi.FieldStorage(fp=f, environ=env)
238 # if we're not chunking properly, readline is only called twice
239 # (by read_binary); if we are chunking properly, it will be called 5 times
240 # as long as the chunksize is 1 << 16.
241 self.assert_(f.numcalls > 2)
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000242
Thomas Wouters89f507f2006-12-13 04:49:30 +0000243 def test_fieldstorage_multipart(self):
244 #Test basic FieldStorage multipart parsing
245 env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------721837373350705526688164684', 'CONTENT_LENGTH':'558'}
246 postdata = """-----------------------------721837373350705526688164684
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000247Content-Disposition: form-data; name="id"
248
2491234
250-----------------------------721837373350705526688164684
251Content-Disposition: form-data; name="title"
252
253
254-----------------------------721837373350705526688164684
255Content-Disposition: form-data; name="file"; filename="test.txt"
256Content-Type: text/plain
257
258Testing 123.
259
260-----------------------------721837373350705526688164684
261Content-Disposition: form-data; name="submit"
262
263 Add\x20
264-----------------------------721837373350705526688164684--
265"""
Thomas Wouters89f507f2006-12-13 04:49:30 +0000266 fs = cgi.FieldStorage(fp=StringIO(postdata), environ=env)
267 self.assertEquals(len(fs.list), 4)
268 expect = [{'name':'id', 'filename':None, 'value':'1234'},
269 {'name':'title', 'filename':None, 'value':''},
270 {'name':'file', 'filename':'test.txt','value':'Testing 123.\n'},
271 {'name':'submit', 'filename':None, 'value':' Add '}]
272 for x in range(len(fs.list)):
273 for k, exp in expect[x].items():
274 got = getattr(fs.list[x], k)
275 self.assertEquals(got, exp)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000276
Thomas Wouters89f507f2006-12-13 04:49:30 +0000277def test_main():
278 run_unittest(CgiTests)
279
280if __name__ == '__main__':
281 test_main()