from test.test_json import PyTest, CTest # 2007-10-05 JSONDOCS = [ # https://json.org/JSON_checker/test/fail1.json '"A JSON payload should be an object or array, not a string."', # https://json.org/JSON_checker/test/fail2.json '["Unclosed array"', # https://json.org/JSON_checker/test/fail3.json '{unquoted_key: "keys must be quoted"}', # https://json.org/JSON_checker/test/fail4.json '["extra comma",]', # https://json.org/JSON_checker/test/fail5.json '["double extra comma",,]', # https://json.org/JSON_checker/test/fail6.json '[ , "<-- missing value"]', # https://json.org/JSON_checker/test/fail7.json '["Comma after the close"],', # https://json.org/JSON_checker/test/fail8.json '["Extra close"]]', # https://json.org/JSON_checker/test/fail9.json '{"Extra comma": true,}', # https://json.org/JSON_checker/test/fail10.json '{"Extra value after close": true} "misplaced quoted value"', # https://json.org/JSON_checker/test/fail11.json '{"Illegal expression": 1 + 2}', # https://json.org/JSON_checker/test/fail12.json '{"Illegal invocation": alert()}', # https://json.org/JSON_checker/test/fail13.json '{"Numbers cannot have leading zeroes": 013}', # https://json.org/JSON_checker/test/fail14.json '{"Numbers cannot be hex": 0x14}', # https://json.org/JSON_checker/test/fail15.json '["Illegal backslash escape: \\x15"]', # https://json.org/JSON_checker/test/fail16.json '[\\naked]', # https://json.org/JSON_checker/test/fail17.json '["Illegal backslash escape: \\017"]', # https://json.org/JSON_checker/test/fail18.json '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', # https://json.org/JSON_checker/test/fail19.json '{"Missing colon" null}', # https://json.org/JSON_checker/test/fail20.json '{"Double colon":: null}', # https://json.org/JSON_checker/test/fail21.json '{"Comma instead of colon", null}', # https://json.org/JSON_checker/test/fail22.json '["Colon instead of comma": false]', # https://json.org/JSON_checker/test/fail23.json '["Bad value", truth]', # https://json.org/JSON_checker/test/fail24.json "['single quote']", # https://json.org/JSON_checker/test/fail25.json '["\ttab\tcharacter\tin\tstring\t"]', # https://json.org/JSON_checker/test/fail26.json '["tab\\ character\\ in\\ string\\ "]', # https://json.org/JSON_checker/test/fail27.json '["line\nbreak"]', # https://json.org/JSON_checker/test/fail28.json '["line\\\nbreak"]', # https://json.org/JSON_checker/test/fail29.json '[0e]', # https://json.org/JSON_checker/test/fail30.json '[0e+]', # https://json.org/JSON_checker/test/fail31.json '[0e+-1]', # https://json.org/JSON_checker/test/fail32.json '{"Comma instead if closing brace": true,', # https://json.org/JSON_checker/test/fail33.json '["mismatch"}', # https://code.google.com/archive/p/simplejson/issues/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 self.JSONDecodeError: pass else: self.fail(f"Expected failure for fail{idx}.json: {doc!r}") def test_non_string_keys_dict(self): data = {'a' : 1, (1, 2) : 2} with self.assertRaisesRegex(TypeError, 'keys must be str, int, float, bool or None, not tuple'): self.dumps(data) def test_not_serializable(self): import sys with self.assertRaisesRegex(TypeError, 'Object of type module is not JSON serializable'): self.dumps(sys) def test_truncated_input(self): test_cases = [ ('', 'Expecting value', 0), ('[', 'Expecting value', 1), ('[42', "Expecting ',' delimiter", 3), ('[42,', 'Expecting value', 4), ('["', 'Unterminated string starting at', 1), ('["spam', 'Unterminated string starting at', 1), ('["spam"', "Expecting ',' delimiter", 7), ('["spam",', 'Expecting value', 8), ('{', 'Expecting property name enclosed in double quotes', 1), ('{"', 'Unterminated string starting at', 1), ('{"spam', 'Unterminated string starting at', 1), ('{"spam"', "Expecting ':' delimiter", 7), ('{"spam":', 'Expecting value', 8), ('{"spam":42', "Expecting ',' delimiter", 10), ('{"spam":42,', 'Expecting property name enclosed in double quotes', 11), ] test_cases += [ ('"', 'Unterminated string starting at', 0), ('"spam', 'Unterminated string starting at', 0), ] for data, msg, idx in test_cases: with self.assertRaises(self.JSONDecodeError) as cm: self.loads(data) err = cm.exception self.assertEqual(err.msg, msg) self.assertEqual(err.pos, idx) self.assertEqual(err.lineno, 1) self.assertEqual(err.colno, idx + 1) self.assertEqual(str(err), '%s: line 1 column %d (char %d)' % (msg, idx + 1, idx)) def test_unexpected_data(self): test_cases = [ ('[,', 'Expecting value', 1), ('{"spam":[}', 'Expecting value', 9), ('[42:', "Expecting ',' delimiter", 3), ('[42 "spam"', "Expecting ',' delimiter", 4), ('[42,]', "Illegal trailing comma before end of array", 3), ('{"spam":[42}', "Expecting ',' delimiter", 11), ('["]', 'Unterminated string starting at', 1), ('["spam":', "Expecting ',' delimiter", 7), ('["spam",]', "Illegal trailing comma before end of array", 7), ('{:', 'Expecting property name enclosed in double quotes', 1), ('{,', 'Expecting property name enclosed in double quotes', 1), ('{42', 'Expecting property name enclosed in double quotes', 1), ('[{]', 'Expecting property name enclosed in double quotes', 2), ('{"spam",', "Expecting ':' delimiter", 7), ('{"spam"}', "Expecting ':' delimiter", 7), ('[{"spam"]', "Expecting ':' delimiter", 8), ('{"spam":}', 'Expecting value', 8), ('[{"spam":]', 'Expecting value', 9), ('{"spam":42 "ham"', "Expecting ',' delimiter", 11), ('[{"spam":42]', "Expecting ',' delimiter", 11), ('{"spam":42,}', "Illegal trailing comma before end of object", 10), ('{"spam":42 , }', "Illegal trailing comma before end of object", 11), ('[123 , ]', "Illegal trailing comma before end of array", 6), ] for data, msg, idx in test_cases: with self.assertRaises(self.JSONDecodeError) as cm: self.loads(data) err = cm.exception self.assertEqual(err.msg, msg) self.assertEqual(err.pos, idx) self.assertEqual(err.lineno, 1) self.assertEqual(err.colno, idx + 1) self.assertEqual(str(err), '%s: line 1 column %d (char %d)' % (msg, idx + 1, idx)) def test_extra_data(self): test_cases = [ ('[]]', 'Extra data', 2), ('{}}', 'Extra data', 2), ('[],[]', 'Extra data', 2), ('{},{}', 'Extra data', 2), ] test_cases += [ ('42,"spam"', 'Extra data', 2), ('"spam",42', 'Extra data', 6), ] for data, msg, idx in test_cases: with self.assertRaises(self.JSONDecodeError) as cm: self.loads(data) err = cm.exception self.assertEqual(err.msg, msg) self.assertEqual(err.pos, idx) self.assertEqual(err.lineno, 1) self.assertEqual(err.colno, idx + 1) self.assertEqual(str(err), '%s: line 1 column %d (char %d)' % (msg, idx + 1, idx)) def test_linecol(self): test_cases = [ ('!', 1, 1, 0), (' !', 1, 2, 1), ('\n!', 2, 1, 1), ('\n \n\n !', 4, 6, 10), ] for data, line, col, idx in test_cases: with self.assertRaises(self.JSONDecodeError) as cm: self.loads(data) err = cm.exception self.assertEqual(err.msg, 'Expecting value') self.assertEqual(err.pos, idx) self.assertEqual(err.lineno, line) self.assertEqual(err.colno, col) self.assertEqual(str(err), 'Expecting value: line %s column %d (char %d)' % (line, col, idx)) class TestPyFail(TestFail, PyTest): pass class TestCFail(TestFail, CTest): pass