Home | History | Annotate | Download | only in test
      1 from test.test_support import run_unittest, check_warnings
      2 import cgi
      3 import os
      4 import sys
      5 import tempfile
      6 import unittest
      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 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?
     64     ("=", {}),
     65     ("=&=", {}),
     66     ("=;=", {}),
     67     # This rest seem to make sense
     68     ("=a", {'': ['a']}),
     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'],
     85       'cuyer': ['r'],
     86       'expire': ['964546263'],
     87       'kid': ['130003.300038'],
     88       'lobale': ['en-US'],
     89       'order_id': ['0bb2e248638833d48cb7fed300000f1b'],
     90       'ss': ['env'],
     91       'view': ['bustomer'],
     92       }),
     93 
     94     ("group_id=5470&set=custom&_assigned_to=31392&_status=1&_category=100&SUBMIT=Browse",
     95      {'SUBMIT': ['Browse'],
     96       '_assigned_to': ['31392'],
     97       '_category': ['100'],
     98       '_status': ['1'],
     99       'group_id': ['5470'],
    100       'set': ['custom'],
    101       })
    102     ]
    103 
    104 def first_elts(list):
    105     return map(lambda x:x[0], list)
    106 
    107 def first_second_elts(list):
    108     return map(lambda p:(p[0], p[1][0]), list)
    109 
    110 def gen_result(data, environ):
    111     fake_stdin = StringIO(data)
    112     fake_stdin.seek(0)
    113     form = cgi.FieldStorage(fp=fake_stdin, environ=environ)
    114 
    115     result = {}
    116     for k, v in dict(form).items():
    117         result[k] = isinstance(v, list) and form.getlist(k) or v.value
    118 
    119     return result
    120 
    121 class CgiTests(unittest.TestCase):
    122 
    123     def test_escape(self):
    124         self.assertEqual("test & string", cgi.escape("test & string"))
    125         self.assertEqual("&lt;test string&gt;", cgi.escape("<test string>"))
    126         self.assertEqual("&quot;test string&quot;", cgi.escape('"test string"', True))
    127 
    128     def test_strict(self):
    129         for orig, expect in parse_strict_test_cases:
    130             # Test basic parsing
    131             d = do_test(orig, "GET")
    132             self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
    133             d = do_test(orig, "POST")
    134             self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
    135 
    136             env = {'QUERY_STRING': orig}
    137             fcd = cgi.FormContentDict(env)
    138             sd = cgi.SvFormContentDict(env)
    139             fs = cgi.FieldStorage(environ=env)
    140             if isinstance(expect, dict):
    141                 # test dict interface
    142                 self.assertEqual(len(expect), len(fcd))
    143                 self.assertItemsEqual(expect.keys(), fcd.keys())
    144                 self.assertItemsEqual(expect.values(), fcd.values())
    145                 self.assertItemsEqual(expect.items(), fcd.items())
    146                 self.assertEqual(fcd.get("nonexistent field", "default"), "default")
    147                 self.assertEqual(len(sd), len(fs))
    148                 self.assertItemsEqual(sd.keys(), fs.keys())
    149                 self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
    150                 # test individual fields
    151                 for key in expect.keys():
    152                     expect_val = expect[key]
    153                     self.assertTrue(fcd.has_key(key))
    154                     self.assertItemsEqual(fcd[key], expect[key])
    155                     self.assertEqual(fcd.get(key, "default"), fcd[key])
    156                     self.assertTrue(fs.has_key(key))
    157                     if len(expect_val) > 1:
    158                         single_value = 0
    159                     else:
    160                         single_value = 1
    161                     try:
    162                         val = sd[key]
    163                     except IndexError:
    164                         self.assertFalse(single_value)
    165                         self.assertEqual(fs.getvalue(key), expect_val)
    166                     else:
    167                         self.assertTrue(single_value)
    168                         self.assertEqual(val, expect_val[0])
    169                         self.assertEqual(fs.getvalue(key), expect_val[0])
    170                     self.assertItemsEqual(sd.getlist(key), expect_val)
    171                     if single_value:
    172                         self.assertItemsEqual(sd.values(),
    173                                                 first_elts(expect.values()))
    174                         self.assertItemsEqual(sd.items(),
    175                                                 first_second_elts(expect.items()))
    176 
    177     def test_weird_formcontentdict(self):
    178         # Test the weird FormContentDict classes
    179         env = {'QUERY_STRING': "x=1&y=2.0&z=2-3.%2b0&1=1abc"}
    180         expect = {'x': 1, 'y': 2.0, 'z': '2-3.+0', '1': '1abc'}
    181         d = cgi.InterpFormContentDict(env)
    182         for k, v in expect.items():
    183             self.assertEqual(d[k], v)
    184         for k, v in d.items():
    185             self.assertEqual(expect[k], v)
    186         self.assertItemsEqual(expect.values(), d.values())
    187 
    188     def test_log(self):
    189         cgi.log("Testing")
    190 
    191         cgi.logfp = StringIO()
    192         cgi.initlog("%s", "Testing initlog 1")
    193         cgi.log("%s", "Testing log 2")
    194         self.assertEqual(cgi.logfp.getvalue(), "Testing initlog 1\nTesting log 2\n")
    195         if os.path.exists("/dev/null"):
    196             cgi.logfp = None
    197             cgi.logfile = "/dev/null"
    198             cgi.initlog("%s", "Testing log 3")
    199             cgi.log("Testing log 4")
    200 
    201     def test_fieldstorage_readline(self):
    202         # FieldStorage uses readline, which has the capacity to read all
    203         # contents of the input file into memory; we use readline's size argument
    204         # to prevent that for files that do not contain any newlines in
    205         # non-GET/HEAD requests
    206         class TestReadlineFile:
    207             def __init__(self, file):
    208                 self.file = file
    209                 self.numcalls = 0
    210 
    211             def readline(self, size=None):
    212                 self.numcalls += 1
    213                 if size:
    214                     return self.file.readline(size)
    215                 else:
    216                     return self.file.readline()
    217 
    218             def __getattr__(self, name):
    219                 file = self.__dict__['file']
    220                 a = getattr(file, name)
    221                 if not isinstance(a, int):
    222                     setattr(self, name, a)
    223                 return a
    224 
    225         f = TestReadlineFile(tempfile.TemporaryFile())
    226         f.write('x' * 256 * 1024)
    227         f.seek(0)
    228         env = {'REQUEST_METHOD':'PUT'}
    229         fs = cgi.FieldStorage(fp=f, environ=env)
    230         # if we're not chunking properly, readline is only called twice
    231         # (by read_binary); if we are chunking properly, it will be called 5 times
    232         # as long as the chunksize is 1 << 16.
    233         self.assertTrue(f.numcalls > 2)
    234 
    235     def test_fieldstorage_multipart(self):
    236         #Test basic FieldStorage multipart parsing
    237         env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------721837373350705526688164684', 'CONTENT_LENGTH':'558'}
    238         postdata = """-----------------------------721837373350705526688164684
    239 Content-Disposition: form-data; name="id"
    240 
    241 1234
    242 -----------------------------721837373350705526688164684
    243 Content-Disposition: form-data; name="title"
    244 
    245 
    246 -----------------------------721837373350705526688164684
    247 Content-Disposition: form-data; name="file"; filename="test.txt"
    248 Content-Type: text/plain
    249 
    250 Testing 123.
    251 
    252 -----------------------------721837373350705526688164684
    253 Content-Disposition: form-data; name="submit"
    254 
    255  Add\x20
    256 -----------------------------721837373350705526688164684--
    257 """
    258         fs = cgi.FieldStorage(fp=StringIO(postdata), environ=env)
    259         self.assertEqual(len(fs.list), 4)
    260         expect = [{'name':'id', 'filename':None, 'value':'1234'},
    261                   {'name':'title', 'filename':None, 'value':''},
    262                   {'name':'file', 'filename':'test.txt','value':'Testing 123.\n'},
    263                   {'name':'submit', 'filename':None, 'value':' Add '}]
    264         for x in range(len(fs.list)):
    265             for k, exp in expect[x].items():
    266                 got = getattr(fs.list[x], k)
    267                 self.assertEqual(got, exp)
    268 
    269     _qs_result = {
    270         'key1': 'value1',
    271         'key2': ['value2x', 'value2y'],
    272         'key3': 'value3',
    273         'key4': 'value4'
    274     }
    275     def testQSAndUrlEncode(self):
    276         data = "key2=value2x&key3=value3&key4=value4"
    277         environ = {
    278             'CONTENT_LENGTH':   str(len(data)),
    279             'CONTENT_TYPE':     'application/x-www-form-urlencoded',
    280             'QUERY_STRING':     'key1=value1&key2=value2y',
    281             'REQUEST_METHOD':   'POST',
    282         }
    283         v = gen_result(data, environ)
    284         self.assertEqual(self._qs_result, v)
    285 
    286     def testQSAndFormData(self):
    287         data = """
    288 ---123
    289 Content-Disposition: form-data; name="key2"
    290 
    291 value2y
    292 ---123
    293 Content-Disposition: form-data; name="key3"
    294 
    295 value3
    296 ---123
    297 Content-Disposition: form-data; name="key4"
    298 
    299 value4
    300 ---123--
    301 """
    302         environ = {
    303             'CONTENT_LENGTH':   str(len(data)),
    304             'CONTENT_TYPE':     'multipart/form-data; boundary=-123',
    305             'QUERY_STRING':     'key1=value1&key2=value2x',
    306             'REQUEST_METHOD':   'POST',
    307         }
    308         v = gen_result(data, environ)
    309         self.assertEqual(self._qs_result, v)
    310 
    311     def testQSAndFormDataFile(self):
    312         data = """
    313 ---123
    314 Content-Disposition: form-data; name="key2"
    315 
    316 value2y
    317 ---123
    318 Content-Disposition: form-data; name="key3"
    319 
    320 value3
    321 ---123
    322 Content-Disposition: form-data; name="key4"
    323 
    324 value4
    325 ---123
    326 Content-Disposition: form-data; name="upload"; filename="fake.txt"
    327 Content-Type: text/plain
    328 
    329 this is the content of the fake file
    330 
    331 ---123--
    332 """
    333         environ = {
    334             'CONTENT_LENGTH':   str(len(data)),
    335             'CONTENT_TYPE':     'multipart/form-data; boundary=-123',
    336             'QUERY_STRING':     'key1=value1&key2=value2x',
    337             'REQUEST_METHOD':   'POST',
    338         }
    339         result = self._qs_result.copy()
    340         result.update({
    341             'upload': 'this is the content of the fake file\n'
    342         })
    343         v = gen_result(data, environ)
    344         self.assertEqual(result, v)
    345 
    346     def test_deprecated_parse_qs(self):
    347         # this func is moved to urlparse, this is just a sanity check
    348         with check_warnings(('cgi.parse_qs is deprecated, use urlparse.'
    349                              'parse_qs instead', PendingDeprecationWarning)):
    350             self.assertEqual({'a': ['A1'], 'B': ['B3'], 'b': ['B2']},
    351                              cgi.parse_qs('a=A1&b=B2&B=B3'))
    352 
    353     def test_deprecated_parse_qsl(self):
    354         # this func is moved to urlparse, this is just a sanity check
    355         with check_warnings(('cgi.parse_qsl is deprecated, use urlparse.'
    356                              'parse_qsl instead', PendingDeprecationWarning)):
    357             self.assertEqual([('a', 'A1'), ('b', 'B2'), ('B', 'B3')],
    358                              cgi.parse_qsl('a=A1&b=B2&B=B3'))
    359 
    360     def test_parse_header(self):
    361         self.assertEqual(
    362             cgi.parse_header("text/plain"),
    363             ("text/plain", {}))
    364         self.assertEqual(
    365             cgi.parse_header("text/vnd.just.made.this.up ; "),
    366             ("text/vnd.just.made.this.up", {}))
    367         self.assertEqual(
    368             cgi.parse_header("text/plain;charset=us-ascii"),
    369             ("text/plain", {"charset": "us-ascii"}))
    370         self.assertEqual(
    371             cgi.parse_header('text/plain ; charset="us-ascii"'),
    372             ("text/plain", {"charset": "us-ascii"}))
    373         self.assertEqual(
    374             cgi.parse_header('text/plain ; charset="us-ascii"; another=opt'),
    375             ("text/plain", {"charset": "us-ascii", "another": "opt"}))
    376         self.assertEqual(
    377             cgi.parse_header('attachment; filename="silly.txt"'),
    378             ("attachment", {"filename": "silly.txt"}))
    379         self.assertEqual(
    380             cgi.parse_header('attachment; filename="strange;name"'),
    381             ("attachment", {"filename": "strange;name"}))
    382         self.assertEqual(
    383             cgi.parse_header('attachment; filename="strange;name";size=123;'),
    384             ("attachment", {"filename": "strange;name", "size": "123"}))
    385         self.assertEqual(
    386             cgi.parse_header('form-data; name="files"; filename="fo\\"o;bar"'),
    387             ("form-data", {"name": "files", "filename": 'fo"o;bar'}))
    388 
    389 
    390 def test_main():
    391     run_unittest(CgiTests)
    392 
    393 if __name__ == '__main__':
    394     test_main()
    395