blob: 9cf6a121ac71c2beaad19920221d7825df7b20d1 [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):
121 if isinstance(seq, list):
122 seq.sort(key=repr)
123 return seq
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000124
125def first_elts(list):
126 return map(lambda x:x[0], list)
127
128def first_second_elts(list):
129 return map(lambda p:(p[0], p[1][0]), list)
130
Thomas Wouters89f507f2006-12-13 04:49:30 +0000131class CgiTests(unittest.TestCase):
Neil Schemenauer66edb622004-07-19 15:38:11 +0000132
Thomas Wouters89f507f2006-12-13 04:49:30 +0000133 def test_qsl(self):
134 for orig, expect in parse_qsl_test_cases:
135 result = cgi.parse_qsl(orig, keep_blank_values=True)
136 self.assertEqual(result, expect, "Error parsing %s" % repr(orig))
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000137
Thomas Wouters89f507f2006-12-13 04:49:30 +0000138 def test_strict(self):
139 for orig, expect in parse_strict_test_cases:
140 # Test basic parsing
141 d = do_test(orig, "GET")
142 self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
143 d = do_test(orig, "POST")
144 self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
145
146 env = {'QUERY_STRING': orig}
147 fcd = cgi.FormContentDict(env)
148 sd = cgi.SvFormContentDict(env)
149 fs = cgi.FieldStorage(environ=env)
150 if type(expect) == type({}):
151 # test dict interface
152 self.assertEqual(len(expect), len(fcd))
153 self.assertEqual(norm(expect.keys()), norm(fcd.keys()))
154 self.assertEqual(norm(expect.values()), norm(fcd.values()))
155 self.assertEqual(norm(expect.items()), norm(fcd.items()))
156 self.assertEqual(fcd.get("nonexistent field", "default"), "default")
157 self.assertEqual(len(sd), len(fs))
158 self.assertEqual(norm(sd.keys()), norm(fs.keys()))
159 self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
160 # test individual fields
161 for key in expect.keys():
162 expect_val = expect[key]
163 self.assert_(key in fcd)
164 self.assertEqual(norm(fcd[key]), norm(expect[key]))
165 self.assertEqual(fcd.get(key, "default"), fcd[key])
166 self.assert_(key in fs)
167 if len(expect_val) > 1:
168 single_value = 0
169 else:
170 single_value = 1
171 try:
172 val = sd[key]
173 except IndexError:
174 self.failIf(single_value)
175 self.assertEqual(fs.getvalue(key), expect_val)
176 else:
177 self.assert_(single_value)
178 self.assertEqual(val, expect_val[0])
179 self.assertEqual(fs.getvalue(key), expect_val[0])
180 self.assertEqual(norm(sd.getlist(key)), norm(expect_val))
181 if single_value:
182 self.assertEqual(norm(sd.values()),
183 first_elts(norm(expect.values())))
184 self.assertEqual(norm(sd.items()),
185 first_second_elts(norm(expect.items())))
186
187 def test_weird_formcontentdict(self):
188 # Test the weird FormContentDict classes
189 env = {'QUERY_STRING': "x=1&y=2.0&z=2-3.%2b0&1=1abc"}
190 expect = {'x': 1, 'y': 2.0, 'z': '2-3.+0', '1': '1abc'}
191 d = cgi.InterpFormContentDict(env)
192 for k, v in expect.items():
193 self.assertEqual(d[k], v)
194 for k, v in d.items():
195 self.assertEqual(expect[k], v)
196 self.assertEqual(norm(expect.values()), norm(d.values()))
197
198 def test_log(self):
199 cgi.log("Testing")
200
201 cgi.logfp = StringIO()
202 cgi.initlog("%s", "Testing initlog 1")
203 cgi.log("%s", "Testing log 2")
204 self.assertEqual(cgi.logfp.getvalue(), "Testing initlog 1\nTesting log 2\n")
205 if os.path.exists("/dev/null"):
206 cgi.logfp = None
207 cgi.logfile = "/dev/null"
208 cgi.initlog("%s", "Testing log 3")
209 cgi.log("Testing log 4")
210
211 def test_fieldstorage_readline(self):
212 # FieldStorage uses readline, which has the capacity to read all
213 # contents of the input file into memory; we use readline's size argument
214 # to prevent that for files that do not contain any newlines in
215 # non-GET/HEAD requests
216 class TestReadlineFile:
217 def __init__(self, file):
218 self.file = file
219 self.numcalls = 0
220
221 def readline(self, size=None):
222 self.numcalls += 1
223 if size:
224 return self.file.readline(size)
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000225 else:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000226 return self.file.readline()
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000227
Thomas Wouters89f507f2006-12-13 04:49:30 +0000228 def __getattr__(self, name):
229 file = self.__dict__['file']
230 a = getattr(file, name)
231 if not isinstance(a, int):
232 setattr(self, name, a)
233 return a
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000234
Thomas Wouters89f507f2006-12-13 04:49:30 +0000235 f = TestReadlineFile(tempfile.TemporaryFile())
236 f.write('x' * 256 * 1024)
237 f.seek(0)
238 env = {'REQUEST_METHOD':'PUT'}
239 fs = cgi.FieldStorage(fp=f, environ=env)
240 # if we're not chunking properly, readline is only called twice
241 # (by read_binary); if we are chunking properly, it will be called 5 times
242 # as long as the chunksize is 1 << 16.
243 self.assert_(f.numcalls > 2)
Jeremy Hyltond9827c42000-08-03 22:11:43 +0000244
Thomas Wouters89f507f2006-12-13 04:49:30 +0000245 def test_fieldstorage_multipart(self):
246 #Test basic FieldStorage multipart parsing
247 env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------721837373350705526688164684', 'CONTENT_LENGTH':'558'}
248 postdata = """-----------------------------721837373350705526688164684
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000249Content-Disposition: form-data; name="id"
250
2511234
252-----------------------------721837373350705526688164684
253Content-Disposition: form-data; name="title"
254
255
256-----------------------------721837373350705526688164684
257Content-Disposition: form-data; name="file"; filename="test.txt"
258Content-Type: text/plain
259
260Testing 123.
261
262-----------------------------721837373350705526688164684
263Content-Disposition: form-data; name="submit"
264
265 Add\x20
266-----------------------------721837373350705526688164684--
267"""
Thomas Wouters89f507f2006-12-13 04:49:30 +0000268 fs = cgi.FieldStorage(fp=StringIO(postdata), environ=env)
269 self.assertEquals(len(fs.list), 4)
270 expect = [{'name':'id', 'filename':None, 'value':'1234'},
271 {'name':'title', 'filename':None, 'value':''},
272 {'name':'file', 'filename':'test.txt','value':'Testing 123.\n'},
273 {'name':'submit', 'filename':None, 'value':' Add '}]
274 for x in range(len(fs.list)):
275 for k, exp in expect[x].items():
276 got = getattr(fs.list[x], k)
277 self.assertEquals(got, exp)
Thomas Wouters00ee7ba2006-08-21 19:07:27 +0000278
Thomas Wouters89f507f2006-12-13 04:49:30 +0000279def test_main():
280 run_unittest(CgiTests)
281
282if __name__ == '__main__':
283 test_main()