diff options
author | Ezio Melotti <ezio.melotti@gmail.com> | 2013-08-08 12:03:45 (GMT) |
---|---|---|
committer | Ezio Melotti <ezio.melotti@gmail.com> | 2013-08-08 12:03:45 (GMT) |
commit | 66f2ea042acf792bf6ff432f409628e1cec99e43 (patch) | |
tree | 9d7e5a24d3f26eeeb63a0f00fa4758b5dde8d099 /Lib/test/test_json | |
parent | 0d2d2b83935a516235f4dbce25aefad789d088cf (diff) | |
download | cpython-66f2ea042acf792bf6ff432f409628e1cec99e43.zip cpython-66f2ea042acf792bf6ff432f409628e1cec99e43.tar.gz cpython-66f2ea042acf792bf6ff432f409628e1cec99e43.tar.bz2 |
#18273: move the tests in Lib/test/json_tests to Lib/test/test_json and make them discoverable by unittest. Patch by Zachary Ware.
Diffstat (limited to 'Lib/test/test_json')
-rw-r--r-- | Lib/test/test_json/__init__.py | 64 | ||||
-rw-r--r-- | Lib/test/test_json/__main__.py | 4 | ||||
-rw-r--r-- | Lib/test/test_json/test_decode.py | 74 | ||||
-rw-r--r-- | Lib/test/test_json/test_default.py | 12 | ||||
-rw-r--r-- | Lib/test/test_json/test_dump.py | 49 | ||||
-rw-r--r-- | Lib/test/test_json/test_encode_basestring_ascii.py | 44 | ||||
-rw-r--r-- | Lib/test/test_json/test_fail.py | 105 | ||||
-rw-r--r-- | Lib/test/test_json/test_float.py | 33 | ||||
-rw-r--r-- | Lib/test/test_json/test_indent.py | 63 | ||||
-rw-r--r-- | Lib/test/test_json/test_pass1.py | 75 | ||||
-rw-r--r-- | Lib/test/test_json/test_pass2.py | 18 | ||||
-rw-r--r-- | Lib/test/test_json/test_pass3.py | 24 | ||||
-rw-r--r-- | Lib/test/test_json/test_recursion.py | 100 | ||||
-rw-r--r-- | Lib/test/test_json/test_scanstring.py | 98 | ||||
-rw-r--r-- | Lib/test/test_json/test_separators.py | 44 | ||||
-rw-r--r-- | Lib/test/test_json/test_speedups.py | 24 | ||||
-rw-r--r-- | Lib/test/test_json/test_tool.py | 69 | ||||
-rw-r--r-- | Lib/test/test_json/test_unicode.py | 75 |
18 files changed, 975 insertions, 0 deletions
diff --git a/Lib/test/test_json/__init__.py b/Lib/test/test_json/__init__.py new file mode 100644 index 0000000..f994f9b --- /dev/null +++ b/Lib/test/test_json/__init__.py @@ -0,0 +1,64 @@ +import os +import sys +import json +import doctest +import unittest + +from test import support + +# import json with and without accelerations +cjson = support.import_fresh_module('json', fresh=['_json']) +pyjson = support.import_fresh_module('json', blocked=['_json']) + +# create two base classes that will be used by the other tests +class PyTest(unittest.TestCase): + json = pyjson + loads = staticmethod(pyjson.loads) + dumps = staticmethod(pyjson.dumps) + +@unittest.skipUnless(cjson, 'requires _json') +class CTest(unittest.TestCase): + if cjson is not None: + json = cjson + loads = staticmethod(cjson.loads) + dumps = staticmethod(cjson.dumps) + +# test PyTest and CTest checking if the functions come from the right module +class TestPyTest(PyTest): + def test_pyjson(self): + self.assertEqual(self.json.scanner.make_scanner.__module__, + 'json.scanner') + self.assertEqual(self.json.decoder.scanstring.__module__, + 'json.decoder') + self.assertEqual(self.json.encoder.encode_basestring_ascii.__module__, + 'json.encoder') + +class TestCTest(CTest): + def test_cjson(self): + self.assertEqual(self.json.scanner.make_scanner.__module__, '_json') + self.assertEqual(self.json.decoder.scanstring.__module__, '_json') + self.assertEqual(self.json.encoder.c_make_encoder.__module__, '_json') + self.assertEqual(self.json.encoder.encode_basestring_ascii.__module__, + '_json') + + +here = os.path.dirname(__file__) + +def load_tests(*args): + suite = additional_tests() + loader = unittest.TestLoader() + for fn in os.listdir(here): + if fn.startswith("test") and fn.endswith(".py"): + modname = "test.test_json." + fn[:-3] + __import__(modname) + module = sys.modules[modname] + suite.addTests(loader.loadTestsFromModule(module)) + return suite + +def additional_tests(): + suite = unittest.TestSuite() + for mod in (json, json.encoder, json.decoder): + suite.addTest(doctest.DocTestSuite(mod)) + suite.addTest(TestPyTest('test_pyjson')) + suite.addTest(TestCTest('test_cjson')) + return suite diff --git a/Lib/test/test_json/__main__.py b/Lib/test/test_json/__main__.py new file mode 100644 index 0000000..e756afb --- /dev/null +++ b/Lib/test/test_json/__main__.py @@ -0,0 +1,4 @@ +import unittest +from test.test_json import load_tests + +unittest.main() diff --git a/Lib/test/test_json/test_decode.py b/Lib/test/test_json/test_decode.py new file mode 100644 index 0000000..d23e285 --- /dev/null +++ b/Lib/test/test_json/test_decode.py @@ -0,0 +1,74 @@ +import decimal +from io import StringIO +from collections import OrderedDict +from test.test_json import PyTest, CTest + + +class TestDecode: + def test_decimal(self): + rval = self.loads('1.1', parse_float=decimal.Decimal) + self.assertTrue(isinstance(rval, decimal.Decimal)) + self.assertEqual(rval, decimal.Decimal('1.1')) + + def test_float(self): + rval = self.loads('1', parse_int=float) + self.assertTrue(isinstance(rval, float)) + self.assertEqual(rval, 1.0) + + def test_empty_objects(self): + self.assertEqual(self.loads('{}'), {}) + self.assertEqual(self.loads('[]'), []) + self.assertEqual(self.loads('""'), "") + + def test_object_pairs_hook(self): + s = '{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' + p = [("xkd", 1), ("kcw", 2), ("art", 3), ("hxm", 4), + ("qrt", 5), ("pad", 6), ("hoy", 7)] + self.assertEqual(self.loads(s), eval(s)) + self.assertEqual(self.loads(s, object_pairs_hook=lambda x: x), p) + self.assertEqual(self.json.load(StringIO(s), + object_pairs_hook=lambda x: x), p) + od = self.loads(s, object_pairs_hook=OrderedDict) + self.assertEqual(od, OrderedDict(p)) + self.assertEqual(type(od), OrderedDict) + # the object_pairs_hook takes priority over the object_hook + self.assertEqual(self.loads(s, object_pairs_hook=OrderedDict, + object_hook=lambda x: None), + OrderedDict(p)) + # check that empty objects literals work (see #17368) + self.assertEqual(self.loads('{}', object_pairs_hook=OrderedDict), + OrderedDict()) + self.assertEqual(self.loads('{"empty": {}}', + object_pairs_hook=OrderedDict), + OrderedDict([('empty', OrderedDict())])) + + def test_decoder_optimizations(self): + # Several optimizations were made that skip over calls to + # the whitespace regex, so this test is designed to try and + # exercise the uncommon cases. The array cases are already covered. + rval = self.loads('{ "key" : "value" , "k":"v" }') + self.assertEqual(rval, {"key":"value", "k":"v"}) + + def check_keys_reuse(self, source, loads): + rval = loads(source) + (a, b), (c, d) = sorted(rval[0]), sorted(rval[1]) + self.assertIs(a, c) + self.assertIs(b, d) + + def test_keys_reuse(self): + s = '[{"a_key": 1, "b_\xe9": 2}, {"a_key": 3, "b_\xe9": 4}]' + self.check_keys_reuse(s, self.loads) + self.check_keys_reuse(s, self.json.decoder.JSONDecoder().decode) + + def test_extra_data(self): + s = '[1, 2, 3]5' + msg = 'Extra data' + self.assertRaisesRegex(ValueError, msg, self.loads, s) + + def test_invalid_escape(self): + s = '["abc\\y"]' + msg = 'escape' + self.assertRaisesRegex(ValueError, msg, self.loads, s) + +class TestPyDecode(TestDecode, PyTest): pass +class TestCDecode(TestDecode, CTest): pass diff --git a/Lib/test/test_json/test_default.py b/Lib/test/test_json/test_default.py new file mode 100644 index 0000000..9b8325e --- /dev/null +++ b/Lib/test/test_json/test_default.py @@ -0,0 +1,12 @@ +from test.test_json import PyTest, CTest + + +class TestDefault: + def test_default(self): + self.assertEqual( + self.dumps(type, default=repr), + self.dumps(repr(type))) + + +class TestPyDefault(TestDefault, PyTest): pass +class TestCDefault(TestDefault, CTest): pass diff --git a/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py new file mode 100644 index 0000000..af19258 --- /dev/null +++ b/Lib/test/test_json/test_dump.py @@ -0,0 +1,49 @@ +from io import StringIO +from test.test_json import PyTest, CTest + +from test.support import bigmemtest, _1G + +class TestDump: + def test_dump(self): + sio = StringIO() + self.json.dump({}, sio) + self.assertEqual(sio.getvalue(), '{}') + + def test_dumps(self): + self.assertEqual(self.dumps({}), '{}') + + def test_encode_truefalse(self): + self.assertEqual(self.dumps( + {True: False, False: True}, sort_keys=True), + '{"false": true, "true": false}') + self.assertEqual(self.dumps( + {2: 3.0, 4.0: 5, False: 1, 6: True}, sort_keys=True), + '{"false": 1, "2": 3.0, "4.0": 5, "6": true}') + + # Issue 16228: Crash on encoding resized list + def test_encode_mutated(self): + a = [object()] * 10 + def crasher(obj): + del a[-1] + self.assertEqual(self.dumps(a, default=crasher), + '[null, null, null, null, null]') + + +class TestPyDump(TestDump, PyTest): pass + +class TestCDump(TestDump, CTest): + + # The size requirement here is hopefully over-estimated (actual + # memory consumption depending on implementation details, and also + # system memory management, since this may allocate a lot of + # small objects). + + @bigmemtest(size=_1G, memuse=1) + def test_large_list(self, size): + N = int(30 * 1024 * 1024 * (size / _1G)) + l = [1] * N + encoded = self.dumps(l) + self.assertEqual(len(encoded), N * 3) + self.assertEqual(encoded[:1], "[") + self.assertEqual(encoded[-2:], "1]") + self.assertEqual(encoded[1:-2], "1, " * (N - 1)) diff --git a/Lib/test/test_json/test_encode_basestring_ascii.py b/Lib/test/test_json/test_encode_basestring_ascii.py new file mode 100644 index 0000000..480afd6 --- /dev/null +++ b/Lib/test/test_json/test_encode_basestring_ascii.py @@ -0,0 +1,44 @@ +from collections import OrderedDict +from test.test_json import PyTest, CTest + + +CASES = [ + ('/\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\x08\x0c\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?', '"/\\\\\\"\\ucafe\\ubabe\\uab98\\ufcde\\ubcda\\uef4a\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?"'), + ('\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'), + ('controls', '"controls"'), + ('\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'), + ('{"object with 1 member":["array with 1 element"]}', '"{\\"object with 1 member\\":[\\"array with 1 element\\"]}"'), + (' s p a c e d ', '" s p a c e d "'), + ('\U0001d120', '"\\ud834\\udd20"'), + ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), + ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), + ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), + ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), + ("`1~!@#$%^&*()_+-={':[,]}|;.</>?", '"`1~!@#$%^&*()_+-={\':[,]}|;.</>?"'), + ('\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'), + ('\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'), +] + +class TestEncodeBasestringAscii: + def test_encode_basestring_ascii(self): + fname = self.json.encoder.encode_basestring_ascii.__name__ + for input_string, expect in CASES: + result = self.json.encoder.encode_basestring_ascii(input_string) + self.assertEqual(result, expect, + '{0!r} != {1!r} for {2}({3!r})'.format( + result, expect, fname, input_string)) + + def test_ordered_dict(self): + # See issue 6105 + items = [('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)] + s = self.dumps(OrderedDict(items)) + self.assertEqual(s, '{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}') + + def test_sorted_dict(self): + items = [('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)] + s = self.dumps(dict(items), sort_keys=True) + self.assertEqual(s, '{"five": 5, "four": 4, "one": 1, "three": 3, "two": 2}') + + +class TestPyEncodeBasestringAscii(TestEncodeBasestringAscii, PyTest): pass +class TestCEncodeBasestringAscii(TestEncodeBasestringAscii, CTest): pass diff --git a/Lib/test/test_json/test_fail.py b/Lib/test/test_json/test_fail.py new file mode 100644 index 0000000..3dd877a --- /dev/null +++ b/Lib/test/test_json/test_fail.py @@ -0,0 +1,105 @@ +from test.test_json import PyTest, CTest + +# 2007-10-05 +JSONDOCS = [ + # http://json.org/JSON_checker/test/fail1.json + '"A JSON payload should be an object or array, not a string."', + # http://json.org/JSON_checker/test/fail2.json + '["Unclosed array"', + # http://json.org/JSON_checker/test/fail3.json + '{unquoted_key: "keys must be quoted"}', + # http://json.org/JSON_checker/test/fail4.json + '["extra comma",]', + # http://json.org/JSON_checker/test/fail5.json + '["double extra comma",,]', + # http://json.org/JSON_checker/test/fail6.json + '[ , "<-- missing value"]', + # http://json.org/JSON_checker/test/fail7.json + '["Comma after the close"],', + # http://json.org/JSON_checker/test/fail8.json + '["Extra close"]]', + # http://json.org/JSON_checker/test/fail9.json + '{"Extra comma": true,}', + # http://json.org/JSON_checker/test/fail10.json + '{"Extra value after close": true} "misplaced quoted value"', + # http://json.org/JSON_checker/test/fail11.json + '{"Illegal expression": 1 + 2}', + # http://json.org/JSON_checker/test/fail12.json + '{"Illegal invocation": alert()}', + # http://json.org/JSON_checker/test/fail13.json + '{"Numbers cannot have leading zeroes": 013}', + # http://json.org/JSON_checker/test/fail14.json + '{"Numbers cannot be hex": 0x14}', + # http://json.org/JSON_checker/test/fail15.json + '["Illegal backslash escape: \\x15"]', + # http://json.org/JSON_checker/test/fail16.json + '[\\naked]', + # http://json.org/JSON_checker/test/fail17.json + '["Illegal backslash escape: \\017"]', + # http://json.org/JSON_checker/test/fail18.json + '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', + # http://json.org/JSON_checker/test/fail19.json + '{"Missing colon" null}', + # http://json.org/JSON_checker/test/fail20.json + '{"Double colon":: null}', + # http://json.org/JSON_checker/test/fail21.json + '{"Comma instead of colon", null}', + # http://json.org/JSON_checker/test/fail22.json + '["Colon instead of comma": false]', + # http://json.org/JSON_checker/test/fail23.json + '["Bad value", truth]', + # http://json.org/JSON_checker/test/fail24.json + "['single quote']", + # http://json.org/JSON_checker/test/fail25.json + '["\ttab\tcharacter\tin\tstring\t"]', + # http://json.org/JSON_checker/test/fail26.json + '["tab\\ character\\ in\\ string\\ "]', + # http://json.org/JSON_checker/test/fail27.json + '["line\nbreak"]', + # http://json.org/JSON_checker/test/fail28.json + '["line\\\nbreak"]', + # http://json.org/JSON_checker/test/fail29.json + '[0e]', + # http://json.org/JSON_checker/test/fail30.json + '[0e+]', + # http://json.org/JSON_checker/test/fail31.json + '[0e+-1]', + # http://json.org/JSON_checker/test/fail32.json + '{"Comma instead if closing brace": true,', + # http://json.org/JSON_checker/test/fail33.json + '["mismatch"}', + # http://code.google.com/p/simplejson/issues/detail?id=3 + '["A\u001FZ control characters in string"]', +] + +SKIPS = { + 1: "why not have a string payload?", + 18: "spec doesn't specify any nesting limitations", +} + +class TestFail: + def test_failures(self): + for idx, doc in enumerate(JSONDOCS): + idx = idx + 1 + if idx in SKIPS: + self.loads(doc) + continue + try: + self.loads(doc) + except ValueError: + pass + else: + self.fail("Expected failure for fail{0}.json: {1!r}".format(idx, doc)) + + def test_non_string_keys_dict(self): + data = {'a' : 1, (1, 2) : 2} + + #This is for c encoder + self.assertRaises(TypeError, self.dumps, data) + + #This is for python encoder + self.assertRaises(TypeError, self.dumps, data, indent=True) + + +class TestPyFail(TestFail, PyTest): pass +class TestCFail(TestFail, CTest): pass diff --git a/Lib/test/test_json/test_float.py b/Lib/test/test_json/test_float.py new file mode 100644 index 0000000..d0c7214 --- /dev/null +++ b/Lib/test/test_json/test_float.py @@ -0,0 +1,33 @@ +import math +from test.test_json import PyTest, CTest + + +class TestFloat: + def test_floats(self): + for num in [1617161771.7650001, math.pi, math.pi**100, math.pi**-100, 3.1]: + self.assertEqual(float(self.dumps(num)), num) + self.assertEqual(self.loads(self.dumps(num)), num) + + def test_ints(self): + for num in [1, 1<<32, 1<<64]: + self.assertEqual(self.dumps(num), str(num)) + self.assertEqual(int(self.dumps(num)), num) + + def test_out_of_range(self): + self.assertEqual(self.loads('[23456789012E666]'), [float('inf')]) + self.assertEqual(self.loads('[-23456789012E666]'), [float('-inf')]) + + def test_allow_nan(self): + for val in (float('inf'), float('-inf'), float('nan')): + out = self.dumps([val]) + if val == val: # inf + self.assertEqual(self.loads(out), [val]) + else: # nan + res = self.loads(out) + self.assertEqual(len(res), 1) + self.assertNotEqual(res[0], res[0]) + self.assertRaises(ValueError, self.dumps, [val], allow_nan=False) + + +class TestPyFloat(TestFloat, PyTest): pass +class TestCFloat(TestFloat, CTest): pass diff --git a/Lib/test/test_json/test_indent.py b/Lib/test/test_json/test_indent.py new file mode 100644 index 0000000..a4d4d20 --- /dev/null +++ b/Lib/test/test_json/test_indent.py @@ -0,0 +1,63 @@ +import textwrap +from io import StringIO +from test.test_json import PyTest, CTest + + +class TestIndent: + def test_indent(self): + h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', 'i-vhbjkhnth', + {'nifty': 87}, {'field': 'yes', 'morefield': False} ] + + expect = textwrap.dedent("""\ + [ + \t[ + \t\t"blorpie" + \t], + \t[ + \t\t"whoops" + \t], + \t[], + \t"d-shtaeou", + \t"d-nthiouh", + \t"i-vhbjkhnth", + \t{ + \t\t"nifty": 87 + \t}, + \t{ + \t\t"field": "yes", + \t\t"morefield": false + \t} + ]""") + + d1 = self.dumps(h) + d2 = self.dumps(h, indent=2, sort_keys=True, separators=(',', ': ')) + d3 = self.dumps(h, indent='\t', sort_keys=True, separators=(',', ': ')) + + h1 = self.loads(d1) + h2 = self.loads(d2) + h3 = self.loads(d3) + + self.assertEqual(h1, h) + self.assertEqual(h2, h) + self.assertEqual(h3, h) + self.assertEqual(d2, expect.expandtabs(2)) + self.assertEqual(d3, expect) + + def test_indent0(self): + h = {3: 1} + def check(indent, expected): + d1 = self.dumps(h, indent=indent) + self.assertEqual(d1, expected) + + sio = StringIO() + self.json.dump(h, sio, indent=indent) + self.assertEqual(sio.getvalue(), expected) + + # indent=0 should emit newlines + check(0, '{\n"3": 1\n}') + # indent=None is more compact + check(None, '{"3": 1}') + + +class TestPyIndent(TestIndent, PyTest): pass +class TestCIndent(TestIndent, CTest): pass diff --git a/Lib/test/test_json/test_pass1.py b/Lib/test/test_json/test_pass1.py new file mode 100644 index 0000000..15e64b0 --- /dev/null +++ b/Lib/test/test_json/test_pass1.py @@ -0,0 +1,75 @@ +from test.test_json import PyTest, CTest + + +# from http://json.org/JSON_checker/test/pass1.json +JSON = r''' +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E66, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* <!-- --", + "# -- --> */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066, +1e1, +0.1e1, +1e-1, +1e00,2e+00,2e-00 +,"rosebud"] +''' + +class TestPass1: + def test_parse(self): + # test in/out equivalence and parsing + res = self.loads(JSON) + out = self.dumps(res) + self.assertEqual(res, self.loads(out)) + + +class TestPyPass1(TestPass1, PyTest): pass +class TestCPass1(TestPass1, CTest): pass diff --git a/Lib/test/test_json/test_pass2.py b/Lib/test/test_json/test_pass2.py new file mode 100644 index 0000000..3507524 --- /dev/null +++ b/Lib/test/test_json/test_pass2.py @@ -0,0 +1,18 @@ +from test.test_json import PyTest, CTest + + +# from http://json.org/JSON_checker/test/pass2.json +JSON = r''' +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] +''' + +class TestPass2: + def test_parse(self): + # test in/out equivalence and parsing + res = self.loads(JSON) + out = self.dumps(res) + self.assertEqual(res, self.loads(out)) + + +class TestPyPass2(TestPass2, PyTest): pass +class TestCPass2(TestPass2, CTest): pass diff --git a/Lib/test/test_json/test_pass3.py b/Lib/test/test_json/test_pass3.py new file mode 100644 index 0000000..cd0cf17 --- /dev/null +++ b/Lib/test/test_json/test_pass3.py @@ -0,0 +1,24 @@ +from test.test_json import PyTest, CTest + + +# from http://json.org/JSON_checker/test/pass3.json +JSON = r''' +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} +''' + + +class TestPass3: + def test_parse(self): + # test in/out equivalence and parsing + res = self.loads(JSON) + out = self.dumps(res) + self.assertEqual(res, self.loads(out)) + + +class TestPyPass3(TestPass3, PyTest): pass +class TestCPass3(TestPass3, CTest): pass diff --git a/Lib/test/test_json/test_recursion.py b/Lib/test/test_json/test_recursion.py new file mode 100644 index 0000000..1a76254 --- /dev/null +++ b/Lib/test/test_json/test_recursion.py @@ -0,0 +1,100 @@ +from test.test_json import PyTest, CTest + + +class JSONTestObject: + pass + + +class TestRecursion: + def test_listrecursion(self): + x = [] + x.append(x) + try: + self.dumps(x) + except ValueError: + pass + else: + self.fail("didn't raise ValueError on list recursion") + x = [] + y = [x] + x.append(y) + try: + self.dumps(x) + except ValueError: + pass + else: + self.fail("didn't raise ValueError on alternating list recursion") + y = [] + x = [y, y] + # ensure that the marker is cleared + self.dumps(x) + + def test_dictrecursion(self): + x = {} + x["test"] = x + try: + self.dumps(x) + except ValueError: + pass + else: + self.fail("didn't raise ValueError on dict recursion") + x = {} + y = {"a": x, "b": x} + # ensure that the marker is cleared + self.dumps(x) + + def test_defaultrecursion(self): + class RecursiveJSONEncoder(self.json.JSONEncoder): + recurse = False + def default(self, o): + if o is JSONTestObject: + if self.recurse: + return [JSONTestObject] + else: + return 'JSONTestObject' + return pyjson.JSONEncoder.default(o) + + enc = RecursiveJSONEncoder() + self.assertEqual(enc.encode(JSONTestObject), '"JSONTestObject"') + enc.recurse = True + try: + enc.encode(JSONTestObject) + except ValueError: + pass + else: + self.fail("didn't raise ValueError on default recursion") + + + def test_highly_nested_objects_decoding(self): + # test that loading highly-nested objects doesn't segfault when C + # accelerations are used. See #12017 + with self.assertRaises(RuntimeError): + self.loads('{"a":' * 100000 + '1' + '}' * 100000) + with self.assertRaises(RuntimeError): + self.loads('{"a":' * 100000 + '[1]' + '}' * 100000) + with self.assertRaises(RuntimeError): + self.loads('[' * 100000 + '1' + ']' * 100000) + + def test_highly_nested_objects_encoding(self): + # See #12051 + l, d = [], {} + for x in range(100000): + l, d = [l], {'k':d} + with self.assertRaises(RuntimeError): + self.dumps(l) + with self.assertRaises(RuntimeError): + self.dumps(d) + + def test_endless_recursion(self): + # See #12051 + class EndlessJSONEncoder(self.json.JSONEncoder): + def default(self, o): + """If check_circular is False, this will keep adding another list.""" + return [o] + + with self.assertRaises(RuntimeError): + EndlessJSONEncoder(check_circular=False).encode(5j) + + +class TestPyRecursion(TestRecursion, PyTest): pass +class TestCRecursion(TestRecursion, CTest): pass diff --git a/Lib/test/test_json/test_scanstring.py b/Lib/test/test_json/test_scanstring.py new file mode 100644 index 0000000..2e3a291 --- /dev/null +++ b/Lib/test/test_json/test_scanstring.py @@ -0,0 +1,98 @@ +import sys +from test.test_json import PyTest, CTest + + +class TestScanstring: + def test_scanstring(self): + scanstring = self.json.decoder.scanstring + self.assertEqual( + scanstring('"z\\ud834\\udd20x"', 1, True), + ('z\U0001d120x', 16)) + + self.assertEqual( + scanstring('"z\U0001d120x"', 1, True), + ('z\U0001d120x', 5)) + + self.assertEqual( + scanstring('"\\u007b"', 1, True), + ('{', 8)) + + self.assertEqual( + scanstring('"A JSON payload should be an object or array, not a string."', 1, True), + ('A JSON payload should be an object or array, not a string.', 60)) + + self.assertEqual( + scanstring('["Unclosed array"', 2, True), + ('Unclosed array', 17)) + + self.assertEqual( + scanstring('["extra comma",]', 2, True), + ('extra comma', 14)) + + self.assertEqual( + scanstring('["double extra comma",,]', 2, True), + ('double extra comma', 21)) + + self.assertEqual( + scanstring('["Comma after the close"],', 2, True), + ('Comma after the close', 24)) + + self.assertEqual( + scanstring('["Extra close"]]', 2, True), + ('Extra close', 14)) + + self.assertEqual( + scanstring('{"Extra comma": true,}', 2, True), + ('Extra comma', 14)) + + self.assertEqual( + scanstring('{"Extra value after close": true} "misplaced quoted value"', 2, True), + ('Extra value after close', 26)) + + self.assertEqual( + scanstring('{"Illegal expression": 1 + 2}', 2, True), + ('Illegal expression', 21)) + + self.assertEqual( + scanstring('{"Illegal invocation": alert()}', 2, True), + ('Illegal invocation', 21)) + + self.assertEqual( + scanstring('{"Numbers cannot have leading zeroes": 013}', 2, True), + ('Numbers cannot have leading zeroes', 37)) + + self.assertEqual( + scanstring('{"Numbers cannot be hex": 0x14}', 2, True), + ('Numbers cannot be hex', 24)) + + self.assertEqual( + scanstring('[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', 21, True), + ('Too deep', 30)) + + self.assertEqual( + scanstring('{"Missing colon" null}', 2, True), + ('Missing colon', 16)) + + self.assertEqual( + scanstring('{"Double colon":: null}', 2, True), + ('Double colon', 15)) + + self.assertEqual( + scanstring('{"Comma instead of colon", null}', 2, True), + ('Comma instead of colon', 25)) + + self.assertEqual( + scanstring('["Colon instead of comma": false]', 2, True), + ('Colon instead of comma', 25)) + + self.assertEqual( + scanstring('["Bad value", truth]', 2, True), + ('Bad value', 12)) + + def test_overflow(self): + with self.assertRaises(OverflowError): + self.json.decoder.scanstring(b"xxx", sys.maxsize+1) + + +class TestPyScanstring(TestScanstring, PyTest): pass +class TestCScanstring(TestScanstring, CTest): pass diff --git a/Lib/test/test_json/test_separators.py b/Lib/test/test_json/test_separators.py new file mode 100644 index 0000000..84a2be9 --- /dev/null +++ b/Lib/test/test_json/test_separators.py @@ -0,0 +1,44 @@ +import textwrap +from test.test_json import PyTest, CTest + + +class TestSeparators: + def test_separators(self): + h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', 'i-vhbjkhnth', + {'nifty': 87}, {'field': 'yes', 'morefield': False} ] + + expect = textwrap.dedent("""\ + [ + [ + "blorpie" + ] , + [ + "whoops" + ] , + [] , + "d-shtaeou" , + "d-nthiouh" , + "i-vhbjkhnth" , + { + "nifty" : 87 + } , + { + "field" : "yes" , + "morefield" : false + } + ]""") + + + d1 = self.dumps(h) + d2 = self.dumps(h, indent=2, sort_keys=True, separators=(' ,', ' : ')) + + h1 = self.loads(d1) + h2 = self.loads(d2) + + self.assertEqual(h1, h) + self.assertEqual(h2, h) + self.assertEqual(d2, expect) + + +class TestPySeparators(TestSeparators, PyTest): pass +class TestCSeparators(TestSeparators, CTest): pass diff --git a/Lib/test/test_json/test_speedups.py b/Lib/test/test_json/test_speedups.py new file mode 100644 index 0000000..109a246 --- /dev/null +++ b/Lib/test/test_json/test_speedups.py @@ -0,0 +1,24 @@ +from test.test_json import CTest + + +class TestSpeedups(CTest): + def test_scanstring(self): + self.assertEqual(self.json.decoder.scanstring.__module__, "_json") + self.assertIs(self.json.decoder.scanstring, self.json.decoder.c_scanstring) + + def test_encode_basestring_ascii(self): + self.assertEqual(self.json.encoder.encode_basestring_ascii.__module__, + "_json") + self.assertIs(self.json.encoder.encode_basestring_ascii, + self.json.encoder.c_encode_basestring_ascii) + + +class TestDecode(CTest): + def test_make_scanner(self): + self.assertRaises(AttributeError, self.json.scanner.c_make_scanner, 1) + + def test_make_encoder(self): + self.assertRaises(TypeError, self.json.encoder.c_make_encoder, + (True, False), + b"\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75", + None) diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py new file mode 100644 index 0000000..0c39e56 --- /dev/null +++ b/Lib/test/test_json/test_tool.py @@ -0,0 +1,69 @@ +import os +import sys +import textwrap +import unittest +import subprocess +from test import support +from test.script_helper import assert_python_ok + +class TestTool(unittest.TestCase): + data = """ + + [["blorpie"],[ "whoops" ] , [ + ],\t"d-shtaeou",\r"d-nthiouh", + "i-vhbjkhnth", {"nifty":87}, {"morefield" :\tfalse,"field" + :"yes"} ] + """ + + expect = textwrap.dedent("""\ + [ + [ + "blorpie" + ], + [ + "whoops" + ], + [], + "d-shtaeou", + "d-nthiouh", + "i-vhbjkhnth", + { + "nifty": 87 + }, + { + "field": "yes", + "morefield": false + } + ] + """) + + def test_stdin_stdout(self): + with subprocess.Popen( + (sys.executable, '-m', 'json.tool'), + stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc: + out, err = proc.communicate(self.data.encode()) + self.assertEqual(out.splitlines(), self.expect.encode().splitlines()) + self.assertEqual(err, None) + + def _create_infile(self): + infile = support.TESTFN + with open(infile, "w") as fp: + self.addCleanup(os.remove, infile) + fp.write(self.data) + return infile + + def test_infile_stdout(self): + infile = self._create_infile() + rc, out, err = assert_python_ok('-m', 'json.tool', infile) + self.assertEqual(out.splitlines(), self.expect.encode().splitlines()) + self.assertEqual(err, b'') + + def test_infile_outfile(self): + infile = self._create_infile() + outfile = support.TESTFN + '.out' + rc, out, err = assert_python_ok('-m', 'json.tool', infile, outfile) + self.addCleanup(os.remove, outfile) + with open(outfile, "r") as fp: + self.assertEqual(fp.read(), self.expect) + self.assertEqual(out, b'') + self.assertEqual(err, b'') diff --git a/Lib/test/test_json/test_unicode.py b/Lib/test/test_json/test_unicode.py new file mode 100644 index 0000000..c7cc8a7 --- /dev/null +++ b/Lib/test/test_json/test_unicode.py @@ -0,0 +1,75 @@ +from collections import OrderedDict +from test.test_json import PyTest, CTest + + +class TestUnicode: + # test_encoding1 and test_encoding2 from 2.x are irrelevant (only str + # is supported as input, not bytes). + + def test_encoding3(self): + u = '\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + j = self.dumps(u) + self.assertEqual(j, '"\\u03b1\\u03a9"') + + def test_encoding4(self): + u = '\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + j = self.dumps([u]) + self.assertEqual(j, '["\\u03b1\\u03a9"]') + + def test_encoding5(self): + u = '\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + j = self.dumps(u, ensure_ascii=False) + self.assertEqual(j, '"{0}"'.format(u)) + + def test_encoding6(self): + u = '\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + j = self.dumps([u], ensure_ascii=False) + self.assertEqual(j, '["{0}"]'.format(u)) + + def test_big_unicode_encode(self): + u = '\U0001d120' + self.assertEqual(self.dumps(u), '"\\ud834\\udd20"') + self.assertEqual(self.dumps(u, ensure_ascii=False), '"\U0001d120"') + + def test_big_unicode_decode(self): + u = 'z\U0001d120x' + self.assertEqual(self.loads('"' + u + '"'), u) + self.assertEqual(self.loads('"z\\ud834\\udd20x"'), u) + + def test_unicode_decode(self): + for i in range(0, 0xd7ff): + u = chr(i) + s = '"\\u{0:04x}"'.format(i) + self.assertEqual(self.loads(s), u) + + def test_unicode_preservation(self): + self.assertEqual(type(self.loads('""')), str) + self.assertEqual(type(self.loads('"a"')), str) + self.assertEqual(type(self.loads('["a"]')[0]), str) + + def test_bytes_encode(self): + self.assertRaises(TypeError, self.dumps, b"hi") + self.assertRaises(TypeError, self.dumps, [b"hi"]) + + def test_bytes_decode(self): + self.assertRaises(TypeError, self.loads, b'"hi"') + self.assertRaises(TypeError, self.loads, b'["hi"]') + + + def test_object_pairs_hook_with_unicode(self): + s = '{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' + p = [("xkd", 1), ("kcw", 2), ("art", 3), ("hxm", 4), + ("qrt", 5), ("pad", 6), ("hoy", 7)] + self.assertEqual(self.loads(s), eval(s)) + self.assertEqual(self.loads(s, object_pairs_hook = lambda x: x), p) + od = self.loads(s, object_pairs_hook = OrderedDict) + self.assertEqual(od, OrderedDict(p)) + self.assertEqual(type(od), OrderedDict) + # the object_pairs_hook takes priority over the object_hook + self.assertEqual(self.loads(s, object_pairs_hook = OrderedDict, + object_hook = lambda x: None), + OrderedDict(p)) + + +class TestPyUnicode(TestUnicode, PyTest): pass +class TestCUnicode(TestUnicode, CTest): pass |