|
1 from test.test_support import verify, verbose |
|
2 import cgi |
|
3 import os |
|
4 import sys |
|
5 import tempfile |
|
6 from StringIO import StringIO |
|
7 |
|
8 class HackedSysModule: |
|
9 # The regression test will have real values in sys.argv, which |
|
10 # will completely confuse the test of the cgi module |
|
11 argv = [] |
|
12 stdin = sys.stdin |
|
13 |
|
14 cgi.sys = HackedSysModule() |
|
15 |
|
16 try: |
|
17 from cStringIO import StringIO |
|
18 except ImportError: |
|
19 from StringIO import StringIO |
|
20 |
|
21 class ComparableException: |
|
22 def __init__(self, err): |
|
23 self.err = err |
|
24 |
|
25 def __str__(self): |
|
26 return str(self.err) |
|
27 |
|
28 def __cmp__(self, anExc): |
|
29 if not isinstance(anExc, Exception): |
|
30 return -1 |
|
31 x = cmp(self.err.__class__, anExc.__class__) |
|
32 if x != 0: |
|
33 return x |
|
34 return cmp(self.err.args, anExc.args) |
|
35 |
|
36 def __getattr__(self, attr): |
|
37 return getattr(self.err, attr) |
|
38 |
|
39 def do_test(buf, method): |
|
40 env = {} |
|
41 if method == "GET": |
|
42 fp = None |
|
43 env['REQUEST_METHOD'] = 'GET' |
|
44 env['QUERY_STRING'] = buf |
|
45 elif method == "POST": |
|
46 fp = StringIO(buf) |
|
47 env['REQUEST_METHOD'] = 'POST' |
|
48 env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' |
|
49 env['CONTENT_LENGTH'] = str(len(buf)) |
|
50 else: |
|
51 raise ValueError, "unknown method: %s" % method |
|
52 try: |
|
53 return cgi.parse(fp, env, strict_parsing=1) |
|
54 except StandardError, err: |
|
55 return ComparableException(err) |
|
56 |
|
57 # A list of test cases. Each test case is a a two-tuple that contains |
|
58 # a string with the query and a dictionary with the expected result. |
|
59 |
|
60 parse_qsl_test_cases = [ |
|
61 ("", []), |
|
62 ("&", []), |
|
63 ("&&", []), |
|
64 ("=", [('', '')]), |
|
65 ("=a", [('', 'a')]), |
|
66 ("a", [('a', '')]), |
|
67 ("a=", [('a', '')]), |
|
68 ("a=", [('a', '')]), |
|
69 ("&a=b", [('a', 'b')]), |
|
70 ("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]), |
|
71 ("a=1&a=2", [('a', '1'), ('a', '2')]), |
|
72 ] |
|
73 |
|
74 parse_strict_test_cases = [ |
|
75 ("", ValueError("bad query field: ''")), |
|
76 ("&", ValueError("bad query field: ''")), |
|
77 ("&&", ValueError("bad query field: ''")), |
|
78 (";", ValueError("bad query field: ''")), |
|
79 (";&;", ValueError("bad query field: ''")), |
|
80 # Should the next few really be valid? |
|
81 ("=", {}), |
|
82 ("=&=", {}), |
|
83 ("=;=", {}), |
|
84 # This rest seem to make sense |
|
85 ("=a", {'': ['a']}), |
|
86 ("&=a", ValueError("bad query field: ''")), |
|
87 ("=a&", ValueError("bad query field: ''")), |
|
88 ("=&a", ValueError("bad query field: 'a'")), |
|
89 ("b=a", {'b': ['a']}), |
|
90 ("b+=a", {'b ': ['a']}), |
|
91 ("a=b=a", {'a': ['b=a']}), |
|
92 ("a=+b=a", {'a': [' b=a']}), |
|
93 ("&b=a", ValueError("bad query field: ''")), |
|
94 ("b&=a", ValueError("bad query field: 'b'")), |
|
95 ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}), |
|
96 ("a=a+b&a=b+a", {'a': ['a b', 'b a']}), |
|
97 ("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']}), |
|
99 ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), |
|
100 ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env", |
|
101 {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'], |
|
102 'cuyer': ['r'], |
|
103 'expire': ['964546263'], |
|
104 'kid': ['130003.300038'], |
|
105 'lobale': ['en-US'], |
|
106 'order_id': ['0bb2e248638833d48cb7fed300000f1b'], |
|
107 'ss': ['env'], |
|
108 'view': ['bustomer'], |
|
109 }), |
|
110 |
|
111 ("group_id=5470&set=custom&_assigned_to=31392&_status=1&_category=100&SUBMIT=Browse", |
|
112 {'SUBMIT': ['Browse'], |
|
113 '_assigned_to': ['31392'], |
|
114 '_category': ['100'], |
|
115 '_status': ['1'], |
|
116 'group_id': ['5470'], |
|
117 'set': ['custom'], |
|
118 }) |
|
119 ] |
|
120 |
|
121 def norm(list): |
|
122 if type(list) == type([]): |
|
123 list.sort() |
|
124 return list |
|
125 |
|
126 def first_elts(list): |
|
127 return map(lambda x:x[0], list) |
|
128 |
|
129 def first_second_elts(list): |
|
130 return map(lambda p:(p[0], p[1][0]), list) |
|
131 |
|
132 def main(): |
|
133 for orig, expect in parse_qsl_test_cases: |
|
134 result = cgi.parse_qsl(orig, keep_blank_values=True) |
|
135 print repr(orig), '=>', result |
|
136 verify(result == expect, "Error parsing %s" % repr(orig)) |
|
137 |
|
138 for orig, expect in parse_strict_test_cases: |
|
139 # Test basic parsing |
|
140 print repr(orig) |
|
141 d = do_test(orig, "GET") |
|
142 verify(d == expect, "Error parsing %s" % repr(orig)) |
|
143 d = do_test(orig, "POST") |
|
144 verify(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 verify(len(expect) == len(fcd)) |
|
153 verify(norm(expect.keys()) == norm(fcd.keys())) |
|
154 verify(norm(expect.values()) == norm(fcd.values())) |
|
155 verify(norm(expect.items()) == norm(fcd.items())) |
|
156 verify(fcd.get("nonexistent field", "default") == "default") |
|
157 verify(len(sd) == len(fs)) |
|
158 verify(norm(sd.keys()) == norm(fs.keys())) |
|
159 verify(fs.getvalue("nonexistent field", "default") == "default") |
|
160 # test individual fields |
|
161 for key in expect.keys(): |
|
162 expect_val = expect[key] |
|
163 verify(fcd.has_key(key)) |
|
164 verify(norm(fcd[key]) == norm(expect[key])) |
|
165 verify(fcd.get(key, "default") == fcd[key]) |
|
166 verify(fs.has_key(key)) |
|
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 verify(not single_value) |
|
175 verify(fs.getvalue(key) == expect_val) |
|
176 else: |
|
177 verify(single_value) |
|
178 verify(val == expect_val[0]) |
|
179 verify(fs.getvalue(key) == expect_val[0]) |
|
180 verify(norm(sd.getlist(key)) == norm(expect_val)) |
|
181 if single_value: |
|
182 verify(norm(sd.values()) == \ |
|
183 first_elts(norm(expect.values()))) |
|
184 verify(norm(sd.items()) == \ |
|
185 first_second_elts(norm(expect.items()))) |
|
186 |
|
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 verify(d[k] == v) |
|
193 for k, v in d.items(): |
|
194 verify(expect[k] == v) |
|
195 verify(norm(expect.values()) == norm(d.values())) |
|
196 |
|
197 print "Testing log" |
|
198 cgi.log("Testing") |
|
199 cgi.logfp = sys.stdout |
|
200 cgi.initlog("%s", "Testing initlog 1") |
|
201 cgi.log("%s", "Testing log 2") |
|
202 if os.path.exists("/dev/null"): |
|
203 cgi.logfp = None |
|
204 cgi.logfile = "/dev/null" |
|
205 cgi.initlog("%s", "Testing log 3") |
|
206 cgi.log("Testing log 4") |
|
207 |
|
208 print "Test FieldStorage methods that use readline" |
|
209 # FieldStorage uses readline, which has the capacity to read all |
|
210 # contents of the input file into memory; we use readline's size argument |
|
211 # to prevent that for files that do not contain any newlines in |
|
212 # non-GET/HEAD requests |
|
213 class TestReadlineFile: |
|
214 def __init__(self, file): |
|
215 self.file = file |
|
216 self.numcalls = 0 |
|
217 |
|
218 def readline(self, size=None): |
|
219 self.numcalls += 1 |
|
220 if size: |
|
221 return self.file.readline(size) |
|
222 else: |
|
223 return self.file.readline() |
|
224 |
|
225 def __getattr__(self, name): |
|
226 file = self.__dict__['file'] |
|
227 a = getattr(file, name) |
|
228 if not isinstance(a, int): |
|
229 setattr(self, name, a) |
|
230 return a |
|
231 |
|
232 f = TestReadlineFile(tempfile.TemporaryFile()) |
|
233 f.write('x' * 256 * 1024) |
|
234 f.seek(0) |
|
235 env = {'REQUEST_METHOD':'PUT'} |
|
236 fs = cgi.FieldStorage(fp=f, environ=env) |
|
237 # if we're not chunking properly, readline is only called twice |
|
238 # (by read_binary); if we are chunking properly, it will be called 5 times |
|
239 # as long as the chunksize is 1 << 16. |
|
240 verify(f.numcalls > 2) |
|
241 |
|
242 print "Test basic FieldStorage multipart parsing" |
|
243 env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------721837373350705526688164684', 'CONTENT_LENGTH':'558'} |
|
244 postdata = """-----------------------------721837373350705526688164684 |
|
245 Content-Disposition: form-data; name="id" |
|
246 |
|
247 1234 |
|
248 -----------------------------721837373350705526688164684 |
|
249 Content-Disposition: form-data; name="title" |
|
250 |
|
251 |
|
252 -----------------------------721837373350705526688164684 |
|
253 Content-Disposition: form-data; name="file"; filename="test.txt" |
|
254 Content-Type: text/plain |
|
255 |
|
256 Testing 123. |
|
257 |
|
258 -----------------------------721837373350705526688164684 |
|
259 Content-Disposition: form-data; name="submit" |
|
260 |
|
261 Add\x20 |
|
262 -----------------------------721837373350705526688164684-- |
|
263 """ |
|
264 fs = cgi.FieldStorage(fp=StringIO(postdata), environ=env) |
|
265 verify(len(fs.list) == 4) |
|
266 expect = [{'name':'id', 'filename':None, 'value':'1234'}, |
|
267 {'name':'title', 'filename':None, 'value':''}, |
|
268 {'name':'file', 'filename':'test.txt','value':'Testing 123.\n'}, |
|
269 {'name':'submit', 'filename':None, 'value':' Add '}] |
|
270 for x in range(len(fs.list)): |
|
271 for k, exp in expect[x].items(): |
|
272 got = getattr(fs.list[x], k) |
|
273 verify(got == exp) |
|
274 |
|
275 main() |