1 from test
.test_support
import run_unittest
, check_warnings
9 # The regression test will have real values in sys.argv, which
10 # will completely confuse the test of the cgi module
14 cgi
.sys
= HackedSysModule()
17 from cStringIO
import StringIO
19 from StringIO
import StringIO
21 class ComparableException
:
22 def __init__(self
, err
):
28 def __cmp__(self
, anExc
):
29 if not isinstance(anExc
, Exception):
31 x
= cmp(self
.err
.__class
__, anExc
.__class
__)
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
):
43 env
['REQUEST_METHOD'] = 'GET'
44 env
['QUERY_STRING'] = buf
45 elif method
== "POST":
47 env
['REQUEST_METHOD'] = 'POST'
48 env
['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
49 env
['CONTENT_LENGTH'] = str(len(buf
))
51 raise ValueError, "unknown method: %s" % method
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?
67 # This rest seem to make sense
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'],
86 'expire': ['964546263'],
87 'kid': ['130003.300038'],
89 'order_id': ['0bb2e248638833d48cb7fed300000f1b'],
94 ("group_id=5470&set=custom&_assigned_to=31392&_status=1&_category=100&SUBMIT=Browse",
95 {'SUBMIT': ['Browse'],
96 '_assigned_to': ['31392'],
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
)
113 form
= cgi
.FieldStorage(fp
=fake_stdin
, environ
=environ
)
116 for k
, v
in dict(form
).items():
117 result
[k
] = isinstance(v
, list) and form
.getlist(k
) or v
.value
121 class CgiTests(unittest
.TestCase
):
123 def test_strict(self
):
124 for orig
, expect
in parse_strict_test_cases
:
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:
159 self
.assertFalse(single_value
)
160 self
.assertEqual(fs
.getvalue(key
), expect_val
)
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
)
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())
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"):
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):
206 def readline(self
, size
=None):
209 return self
.file.readline(size
)
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
)
220 f
= TestReadlineFile(tempfile
.TemporaryFile())
221 f
.write('x' * 256 * 1024)
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"
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
247 -----------------------------721837373350705526688164684
248 Content-Disposition: form-data; name="submit"
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
)
266 'key2': ['value2x', 'value2y'],
270 def testQSAndUrlEncode(self
):
271 data
= "key2=value2x&key3=value3&key4=value4"
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
):
284 Content-Disposition: form-data; name="key2"
288 Content-Disposition: form-data; name="key3"
292 Content-Disposition: form-data; name="key4"
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
):
309 Content-Disposition: form-data; name="key2"
313 Content-Disposition: form-data; name="key3"
317 Content-Disposition: form-data; name="key4"
321 Content-Disposition: form-data; name="upload"; filename="fake.txt"
322 Content-Type: text/plain
324 this is the content of the fake file
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()
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
):
357 cgi
.parse_header("text/plain"),
360 cgi
.parse_header("text/vnd.just.made.this.up ; "),
361 ("text/vnd.just.made.this.up", {}))
363 cgi
.parse_header("text/plain;charset=us-ascii"),
364 ("text/plain", {"charset": "us-ascii"}))
366 cgi
.parse_header('text/plain ; charset="us-ascii"'),
367 ("text/plain", {"charset": "us-ascii"}))
369 cgi
.parse_header('text/plain ; charset="us-ascii"; another=opt'),
370 ("text/plain", {"charset": "us-ascii", "another": "opt"}))
372 cgi
.parse_header('attachment; filename="silly.txt"'),
373 ("attachment", {"filename": "silly.txt"}))
375 cgi
.parse_header('attachment; filename="strange;name"'),
376 ("attachment", {"filename": "strange;name"}))
378 cgi
.parse_header('attachment; filename="strange;name";size=123;'),
379 ("attachment", {"filename": "strange;name", "size": "123"}))
383 run_unittest(CgiTests
)
385 if __name__
== '__main__':