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