diff options
author | Nick Babcock <nbabcock19@hotmail.com> | 2015-07-10 17:25:50 -0400 |
---|---|---|
committer | Nick Babcock <nbabcock19@hotmail.com> | 2015-07-10 17:25:50 -0400 |
commit | a9e5e7256c9fb27126813c9430be1dab5f27f7ba (patch) | |
tree | 488eb7357fd5e781395161b7aa6be5aabea6a8ed /simplejson/tests | |
parent | 49724ee06f038d27ab3d4adbe4ed403692799aa1 (diff) | |
parent | 0bcdf20cc525c1343b796cb8f247ea5213c6557e (diff) | |
download | simplejson-a9e5e7256c9fb27126813c9430be1dab5f27f7ba.tar.gz |
Merge branch 'master' into iterable_as_array-gh1
Conflicts:
CHANGES.txt
conf.py
index.rst
setup.py
simplejson/__init__.py
simplejson/_speedups.c
simplejson/encoder.py
simplejson/tests/test_tuple.py
Diffstat (limited to 'simplejson/tests')
27 files changed, 1060 insertions, 206 deletions
diff --git a/simplejson/tests/__init__.py b/simplejson/tests/__init__.py index 8d2e443..8c1a4f1 100644 --- a/simplejson/tests/__init__.py +++ b/simplejson/tests/__init__.py @@ -1,18 +1,26 @@ +from __future__ import absolute_import import unittest import doctest +import sys -class OptionalExtensionTestSuite(unittest.TestSuite): +class NoExtensionTestSuite(unittest.TestSuite): def run(self, result): import simplejson - run = unittest.TestSuite.run - run(self, result) simplejson._toggle_speedups(False) - run(self, result) + result = unittest.TestSuite.run(self, result) simplejson._toggle_speedups(True) return result +class TestMissingSpeedups(unittest.TestCase): + def runTest(self): + if hasattr(sys, 'pypy_translation_info'): + "PyPy doesn't need speedups! :)" + elif hasattr(self, 'skipTest'): + self.skipTest('_speedups.so is missing!') + + def additional_tests(suite=None): import simplejson import simplejson.encoder @@ -26,35 +34,50 @@ def additional_tests(suite=None): def all_tests_suite(): - suite = unittest.TestLoader().loadTestsFromNames([ - 'simplejson.tests.test_check_circular', - 'simplejson.tests.test_decode', - 'simplejson.tests.test_default', - 'simplejson.tests.test_dump', - 'simplejson.tests.test_encode_basestring_ascii', - 'simplejson.tests.test_encode_for_html', - 'simplejson.tests.test_errors', - 'simplejson.tests.test_fail', - 'simplejson.tests.test_float', - 'simplejson.tests.test_indent', - 'simplejson.tests.test_pass1', - 'simplejson.tests.test_pass2', - 'simplejson.tests.test_pass3', - 'simplejson.tests.test_recursion', - 'simplejson.tests.test_scanstring', - 'simplejson.tests.test_separators', - 'simplejson.tests.test_speedups', - 'simplejson.tests.test_unicode', - 'simplejson.tests.test_decimal', - 'simplejson.tests.test_tuple', - 'simplejson.tests.test_namedtuple', - ]) - suite = additional_tests(suite) - return OptionalExtensionTestSuite([suite]) + def get_suite(): + return additional_tests( + unittest.TestLoader().loadTestsFromNames([ + 'simplejson.tests.test_bitsize_int_as_string', + 'simplejson.tests.test_bigint_as_string', + 'simplejson.tests.test_check_circular', + 'simplejson.tests.test_decode', + 'simplejson.tests.test_default', + 'simplejson.tests.test_dump', + 'simplejson.tests.test_encode_basestring_ascii', + 'simplejson.tests.test_encode_for_html', + 'simplejson.tests.test_errors', + 'simplejson.tests.test_fail', + 'simplejson.tests.test_float', + 'simplejson.tests.test_indent', + 'simplejson.tests.test_pass1', + 'simplejson.tests.test_pass2', + 'simplejson.tests.test_pass3', + 'simplejson.tests.test_recursion', + 'simplejson.tests.test_scanstring', + 'simplejson.tests.test_separators', + 'simplejson.tests.test_speedups', + 'simplejson.tests.test_unicode', + 'simplejson.tests.test_decimal', + 'simplejson.tests.test_tuple', + 'simplejson.tests.test_namedtuple', + 'simplejson.tests.test_tool', + 'simplejson.tests.test_for_json', + 'simplejson.tests.test_subclass', + ])) + suite = get_suite() + import simplejson + if simplejson._import_c_make_encoder() is None: + suite.addTest(TestMissingSpeedups()) + else: + suite = unittest.TestSuite([ + suite, + NoExtensionTestSuite([get_suite()]), + ]) + return suite def main(): - runner = unittest.TextTestRunner() + runner = unittest.TextTestRunner(verbosity=1 + sys.argv.count('-v')) suite = all_tests_suite() raise SystemExit(not runner.run(suite).wasSuccessful()) diff --git a/simplejson/tests/test_bigint_as_string.py b/simplejson/tests/test_bigint_as_string.py new file mode 100644 index 0000000..2cf2cc2 --- /dev/null +++ b/simplejson/tests/test_bigint_as_string.py @@ -0,0 +1,67 @@ +from unittest import TestCase + +import simplejson as json + + +class TestBigintAsString(TestCase): + # Python 2.5, at least the one that ships on Mac OS X, calculates + # 2 ** 53 as 0! It manages to calculate 1 << 53 correctly. + values = [(200, 200), + ((1 << 53) - 1, 9007199254740991), + ((1 << 53), '9007199254740992'), + ((1 << 53) + 1, '9007199254740993'), + (-100, -100), + ((-1 << 53), '-9007199254740992'), + ((-1 << 53) - 1, '-9007199254740993'), + ((-1 << 53) + 1, -9007199254740991)] + + options = ( + {"bigint_as_string": True}, + {"int_as_string_bitcount": 53} + ) + + def test_ints(self): + for opts in self.options: + for val, expect in self.values: + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, **opts))) + + def test_lists(self): + for opts in self.options: + for val, expect in self.values: + val = [val, val] + expect = [expect, expect] + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, **opts))) + + def test_dicts(self): + for opts in self.options: + for val, expect in self.values: + val = {'k': val} + expect = {'k': expect} + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, **opts))) + + def test_dict_keys(self): + for opts in self.options: + for val, _ in self.values: + expect = {str(val): 'value'} + val = {val: 'value'} + self.assertEqual( + expect, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, **opts))) diff --git a/simplejson/tests/test_bitsize_int_as_string.py b/simplejson/tests/test_bitsize_int_as_string.py new file mode 100644 index 0000000..fd7d103 --- /dev/null +++ b/simplejson/tests/test_bitsize_int_as_string.py @@ -0,0 +1,73 @@ +from unittest import TestCase + +import simplejson as json + + +class TestBitSizeIntAsString(TestCase): + # Python 2.5, at least the one that ships on Mac OS X, calculates + # 2 ** 31 as 0! It manages to calculate 1 << 31 correctly. + values = [ + (200, 200), + ((1 << 31) - 1, (1 << 31) - 1), + ((1 << 31), str(1 << 31)), + ((1 << 31) + 1, str((1 << 31) + 1)), + (-100, -100), + ((-1 << 31), str(-1 << 31)), + ((-1 << 31) - 1, str((-1 << 31) - 1)), + ((-1 << 31) + 1, (-1 << 31) + 1), + ] + + def test_invalid_counts(self): + for n in ['foo', -1, 0, 1.0]: + self.assertRaises( + TypeError, + json.dumps, 0, int_as_string_bitcount=n) + + def test_ints_outside_range_fails(self): + self.assertNotEqual( + str(1 << 15), + json.loads(json.dumps(1 << 15, int_as_string_bitcount=16)), + ) + + def test_ints(self): + for val, expect in self.values: + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, int_as_string_bitcount=31)), + ) + + def test_lists(self): + for val, expect in self.values: + val = [val, val] + expect = [expect, expect] + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, int_as_string_bitcount=31))) + + def test_dicts(self): + for val, expect in self.values: + val = {'k': val} + expect = {'k': expect} + self.assertEqual( + val, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, int_as_string_bitcount=31))) + + def test_dict_keys(self): + for val, _ in self.values: + expect = {str(val): 'value'} + val = {val: 'value'} + self.assertEqual( + expect, + json.loads(json.dumps(val))) + self.assertEqual( + expect, + json.loads(json.dumps(val, int_as_string_bitcount=31))) diff --git a/simplejson/tests/test_decimal.py b/simplejson/tests/test_decimal.py index 09f062e..2b0940b 100644 --- a/simplejson/tests/test_decimal.py +++ b/simplejson/tests/test_decimal.py @@ -1,6 +1,7 @@ +import decimal from decimal import Decimal from unittest import TestCase -from StringIO import StringIO +from simplejson.compat import StringIO, reload_module import simplejson as json @@ -10,35 +11,40 @@ class TestDecimal(TestCase): sio = StringIO() json.dump(obj, sio, **kw) res = json.dumps(obj, **kw) - self.assertEquals(res, sio.getvalue()) + self.assertEqual(res, sio.getvalue()) return res def loads(self, s, **kw): sio = StringIO(s) res = json.loads(s, **kw) - self.assertEquals(res, json.load(sio, **kw)) + self.assertEqual(res, json.load(sio, **kw)) return res def test_decimal_encode(self): for d in map(Decimal, self.NUMS): - self.assertEquals(self.dumps(d, use_decimal=True), str(d)) - + self.assertEqual(self.dumps(d, use_decimal=True), str(d)) + def test_decimal_decode(self): for s in self.NUMS: - self.assertEquals(self.loads(s, parse_float=Decimal), Decimal(s)) - + self.assertEqual(self.loads(s, parse_float=Decimal), Decimal(s)) + + def test_stringify_key(self): + for d in map(Decimal, self.NUMS): + v = {d: d} + self.assertEqual( + self.loads( + self.dumps(v, use_decimal=True), parse_float=Decimal), + {str(d): d}) + def test_decimal_roundtrip(self): for d in map(Decimal, self.NUMS): # The type might not be the same (int and Decimal) but they # should still compare equal. - self.assertEquals( - self.loads( - self.dumps(d, use_decimal=True), parse_float=Decimal), - d) - self.assertEquals( - self.loads( - self.dumps([d], use_decimal=True), parse_float=Decimal), - [d]) + for v in [d, [d], {'': d}]: + self.assertEqual( + self.loads( + self.dumps(v, use_decimal=True), parse_float=Decimal), + v) def test_decimal_defaults(self): d = Decimal('1.1') @@ -46,10 +52,20 @@ class TestDecimal(TestCase): self.assertRaises(TypeError, json.dumps, d, use_decimal=False) self.assertEqual('1.1', json.dumps(d)) self.assertEqual('1.1', json.dumps(d, use_decimal=True)) - self.assertRaises(TypeError, json.dump, d, StringIO(), use_decimal=False) + self.assertRaises(TypeError, json.dump, d, StringIO(), + use_decimal=False) sio = StringIO() json.dump(d, sio) self.assertEqual('1.1', sio.getvalue()) sio = StringIO() json.dump(d, sio, use_decimal=True) self.assertEqual('1.1', sio.getvalue()) + + def test_decimal_reload(self): + # Simulate a subinterpreter that reloads the Python modules but not + # the C code https://github.com/simplejson/simplejson/issues/34 + global Decimal + Decimal = reload_module(decimal).Decimal + import simplejson.encoder + simplejson.encoder.Decimal = Decimal + self.test_decimal_roundtrip() diff --git a/simplejson/tests/test_decode.py b/simplejson/tests/test_decode.py index a140a13..30b692a 100644 --- a/simplejson/tests/test_decode.py +++ b/simplejson/tests/test_decode.py @@ -1,8 +1,9 @@ +from __future__ import absolute_import import decimal from unittest import TestCase -from StringIO import StringIO import simplejson as json +from simplejson.compat import StringIO from simplejson import OrderedDict class TestDecode(TestCase): @@ -13,19 +14,19 @@ class TestDecode(TestCase): def test_decimal(self): rval = json.loads('1.1', parse_float=decimal.Decimal) self.assertTrue(isinstance(rval, decimal.Decimal)) - self.assertEquals(rval, decimal.Decimal('1.1')) + self.assertEqual(rval, decimal.Decimal('1.1')) def test_float(self): rval = json.loads('1', parse_int=float) self.assertTrue(isinstance(rval, float)) - self.assertEquals(rval, 1.0) + self.assertEqual(rval, 1.0) 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 = json.loads('{ "key" : "value" , "k":"v" }') - self.assertEquals(rval, {"key":"value", "k":"v"}) + self.assertEqual(rval, {"key":"value", "k":"v"}) def test_empty_objects(self): s = '{}' @@ -81,3 +82,18 @@ class TestDecode(TestCase): self.assertEqual( ({'a': {}}, 9), cls(object_pairs_hook=dict).raw_decode("{\"a\": {}}")) + # https://github.com/simplejson/simplejson/pull/38 + self.assertEqual( + ({'a': {}}, 11), + cls().raw_decode(" \n{\"a\": {}}")) + + def test_bounds_checking(self): + # https://github.com/simplejson/simplejson/issues/98 + j = json.decoder.JSONDecoder() + for i in [4, 5, 6, -1, -2, -3, -4, -5, -6]: + self.assertRaises(ValueError, j.scan_once, '1234', i) + self.assertRaises(ValueError, j.raw_decode, '1234', i) + x, y = sorted(['128931233', '472389423'], key=id) + diff = id(x) - id(y) + self.assertRaises(ValueError, j.scan_once, y, diff) + self.assertRaises(ValueError, j.raw_decode, y, i) diff --git a/simplejson/tests/test_default.py b/simplejson/tests/test_default.py index 139e42b..d1eacb8 100644 --- a/simplejson/tests/test_default.py +++ b/simplejson/tests/test_default.py @@ -4,6 +4,6 @@ import simplejson as json class TestDefault(TestCase): def test_default(self): - self.assertEquals( + self.assertEqual( json.dumps(type, default=repr), json.dumps(repr(type))) diff --git a/simplejson/tests/test_dump.py b/simplejson/tests/test_dump.py index 8b34004..3661de0 100644 --- a/simplejson/tests/test_dump.py +++ b/simplejson/tests/test_dump.py @@ -1,27 +1,130 @@ from unittest import TestCase -from cStringIO import StringIO - +from simplejson.compat import StringIO, long_type, b, binary_type, PY3 import simplejson as json +def as_text_type(s): + if PY3 and isinstance(s, binary_type): + return s.decode('ascii') + return s + class TestDump(TestCase): def test_dump(self): sio = StringIO() json.dump({}, sio) - self.assertEquals(sio.getvalue(), '{}') + self.assertEqual(sio.getvalue(), '{}') + + def test_constants(self): + for c in [None, True, False]: + self.assertTrue(json.loads(json.dumps(c)) is c) + self.assertTrue(json.loads(json.dumps([c]))[0] is c) + self.assertTrue(json.loads(json.dumps({'a': c}))['a'] is c) + + def test_stringify_key(self): + items = [(b('bytes'), 'bytes'), + (1.0, '1.0'), + (10, '10'), + (True, 'true'), + (False, 'false'), + (None, 'null'), + (long_type(100), '100')] + for k, expect in items: + self.assertEqual( + json.loads(json.dumps({k: expect})), + {expect: expect}) + self.assertEqual( + json.loads(json.dumps({k: expect}, sort_keys=True)), + {expect: expect}) + self.assertRaises(TypeError, json.dumps, {json: 1}) + for v in [{}, {'other': 1}, {b('derp'): 1, 'herp': 2}]: + for sort_keys in [False, True]: + v0 = dict(v) + v0[json] = 1 + v1 = dict((as_text_type(key), val) for (key, val) in v.items()) + self.assertEqual( + json.loads(json.dumps(v0, skipkeys=True, sort_keys=sort_keys)), + v1) + self.assertEqual( + json.loads(json.dumps({'': v0}, skipkeys=True, sort_keys=sort_keys)), + {'': v1}) + self.assertEqual( + json.loads(json.dumps([v0], skipkeys=True, sort_keys=sort_keys)), + [v1]) def test_dumps(self): - self.assertEquals(json.dumps({}), '{}') + self.assertEqual(json.dumps({}), '{}') def test_encode_truefalse(self): - self.assertEquals(json.dumps( + self.assertEqual(json.dumps( {True: False, False: True}, sort_keys=True), '{"false": true, "true": false}') - self.assertEquals(json.dumps( - {2: 3.0, 4.0: 5L, False: 1, 6L: True, "7": 0}, sort_keys=True), - '{"false": 1, "2": 3.0, "4.0": 5, "6": true, "7": 0}') + self.assertEqual( + json.dumps( + {2: 3.0, + 4.0: long_type(5), + False: 1, + long_type(6): True, + "7": 0}, + sort_keys=True), + '{"2": 3.0, "4.0": 5, "6": true, "7": 0, "false": 1}') def test_ordered_dict(self): # http://bugs.python.org/issue6105 items = [('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)] s = json.dumps(json.OrderedDict(items)) - self.assertEqual(s, '{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}')
\ No newline at end of file + self.assertEqual( + s, + '{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}') + + def test_indent_unknown_type_acceptance(self): + """ + A test against the regression mentioned at `github issue 29`_. + + The indent parameter should accept any type which pretends to be + an instance of int or long when it comes to being multiplied by + strings, even if it is not actually an int or long, for + backwards compatibility. + + .. _github issue 29: + http://github.com/simplejson/simplejson/issue/29 + """ + + class AwesomeInt(object): + """An awesome reimplementation of integers""" + + def __init__(self, *args, **kwargs): + if len(args) > 0: + # [construct from literals, objects, etc.] + # ... + + # Finally, if args[0] is an integer, store it + if isinstance(args[0], int): + self._int = args[0] + + # [various methods] + + def __mul__(self, other): + # [various ways to multiply AwesomeInt objects] + # ... finally, if the right-hand operand is not awesome enough, + # try to do a normal integer multiplication + if hasattr(self, '_int'): + return self._int * other + else: + raise NotImplementedError("To do non-awesome things with" + " this object, please construct it from an integer!") + + s = json.dumps([0, 1, 2], indent=AwesomeInt(3)) + self.assertEqual(s, '[\n 0,\n 1,\n 2\n]') + + def test_accumulator(self): + # the C API uses an accumulator that collects after 100,000 appends + lst = [0] * 100000 + self.assertEqual(json.loads(json.dumps(lst)), lst) + + def test_sort_keys(self): + # https://github.com/simplejson/simplejson/issues/106 + for num_keys in range(2, 32): + p = dict((str(x), x) for x in range(num_keys)) + sio = StringIO() + json.dump(p, sio, sort_keys=True) + self.assertEqual(sio.getvalue(), json.dumps(p, sort_keys=True)) + self.assertEqual(json.loads(sio.getvalue()), p) diff --git a/simplejson/tests/test_encode_basestring_ascii.py b/simplejson/tests/test_encode_basestring_ascii.py index 6c40961..49706bf 100644 --- a/simplejson/tests/test_encode_basestring_ascii.py +++ b/simplejson/tests/test_encode_basestring_ascii.py @@ -1,6 +1,7 @@ from unittest import TestCase import simplejson.encoder +from simplejson.compat import b CASES = [ (u'/\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\x08\x0c\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?', '"/\\\\\\"\\ucafe\\ubabe\\uab98\\ufcde\\ubcda\\uef4a\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?"'), @@ -11,9 +12,9 @@ CASES = [ (u' s p a c e d ', '" s p a c e d "'), (u'\U0001d120', '"\\ud834\\udd20"'), (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), - ('\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'), + (b('\xce\xb1\xce\xa9'), '"\\u03b1\\u03a9"'), (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), - ('\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'), + (b('\xce\xb1\xce\xa9'), '"\\u03b1\\u03a9"'), (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), (u"`1~!@#$%^&*()_+-={':[,]}|;.</>?", '"`1~!@#$%^&*()_+-={\':[,]}|;.</>?"'), @@ -34,10 +35,10 @@ class TestEncodeBaseStringAscii(TestCase): fname = encode_basestring_ascii.__name__ for input_string, expect in CASES: result = encode_basestring_ascii(input_string) - #self.assertEquals(result, expect, + #self.assertEqual(result, expect, # '{0!r} != {1!r} for {2}({3!r})'.format( # result, expect, fname, input_string)) - self.assertEquals(result, expect, + self.assertEqual(result, expect, '%r != %r for %s(%r)' % (result, expect, fname, input_string)) def test_sorted_dict(self): diff --git a/simplejson/tests/test_encode_for_html.py b/simplejson/tests/test_encode_for_html.py index c2d5f80..f995254 100644 --- a/simplejson/tests/test_encode_for_html.py +++ b/simplejson/tests/test_encode_for_html.py @@ -1,14 +1,12 @@ import unittest -import simplejson.decoder -import simplejson.encoder - +import simplejson as json class TestEncodeForHTML(unittest.TestCase): def setUp(self): - self.decoder = simplejson.decoder.JSONDecoder() - self.encoder = simplejson.encoder.JSONEncoderForHTML() + self.decoder = json.JSONDecoder() + self.encoder = json.JSONEncoderForHTML() def test_basic_encode(self): self.assertEqual(r'"\u0026"', self.encoder.encode('&')) diff --git a/simplejson/tests/test_errors.py b/simplejson/tests/test_errors.py index 620ccf3..8dede38 100644 --- a/simplejson/tests/test_errors.py +++ b/simplejson/tests/test_errors.py @@ -1,6 +1,8 @@ +import sys, pickle from unittest import TestCase import simplejson as json +from simplejson.compat import u, b class TestErrors(TestCase): def test_string_keys_error(self): @@ -11,24 +13,39 @@ class TestErrors(TestCase): err = None try: json.loads('{}\na\nb') - except json.JSONDecodeError, e: - err = e + except json.JSONDecodeError: + err = sys.exc_info()[1] else: self.fail('Expected JSONDecodeError') - self.assertEquals(err.lineno, 2) - self.assertEquals(err.colno, 1) - self.assertEquals(err.endlineno, 3) - self.assertEquals(err.endcolno, 2) + self.assertEqual(err.lineno, 2) + self.assertEqual(err.colno, 1) + self.assertEqual(err.endlineno, 3) + self.assertEqual(err.endcolno, 2) def test_scan_error(self): err = None - for t in (str, unicode): + for t in (u, b): try: json.loads(t('{"asdf": "')) - except json.JSONDecodeError, e: - err = e + except json.JSONDecodeError: + err = sys.exc_info()[1] else: self.fail('Expected JSONDecodeError') - self.assertEquals(err.lineno, 1) - self.assertEquals(err.colno, 9) -
\ No newline at end of file + self.assertEqual(err.lineno, 1) + self.assertEqual(err.colno, 10) + + def test_error_is_pickable(self): + err = None + try: + json.loads('{}\na\nb') + except json.JSONDecodeError: + err = sys.exc_info()[1] + else: + self.fail('Expected JSONDecodeError') + s = pickle.dumps(err) + e = pickle.loads(s) + + self.assertEqual(err.msg, e.msg) + self.assertEqual(err.doc, e.doc) + self.assertEqual(err.pos, e.pos) + self.assertEqual(err.end, e.end) diff --git a/simplejson/tests/test_fail.py b/simplejson/tests/test_fail.py index 646c0f4..788f3a5 100644 --- a/simplejson/tests/test_fail.py +++ b/simplejson/tests/test_fail.py @@ -1,15 +1,16 @@ +import sys from unittest import TestCase import simplejson as json -# Fri Dec 30 18:57:26 2005 +# 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}', + '{unquoted_key: "keys must be quoted"}', # http://json.org/JSON_checker/test/fail4.json '["extra comma",]', # http://json.org/JSON_checker/test/fail5.json @@ -35,7 +36,7 @@ JSONDOCS = [ # http://json.org/JSON_checker/test/fail15.json '["Illegal backslash escape: \\x15"]', # http://json.org/JSON_checker/test/fail16.json - '["Illegal backslash escape: \\\'"]', + '[\\naked]', # http://json.org/JSON_checker/test/fail17.json '["Illegal backslash escape: \\017"]', # http://json.org/JSON_checker/test/fail18.json @@ -52,8 +53,51 @@ JSONDOCS = [ '["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 u'["A\u001FZ control characters in string"]', + # misc based on coverage + '{', + '{]', + '{"foo": "bar"]', + '{"foo": "bar"', + 'nul', + 'nulx', + '-', + '-x', + '-e', + '-e0', + '-Infinite', + '-Inf', + 'Infinit', + 'Infinite', + 'NaM', + 'NuN', + 'falsy', + 'fal', + 'trug', + 'tru', + '1e', + '1ex', + '1e-', + '1e-x', ] SKIPS = { @@ -73,7 +117,6 @@ class TestFail(TestCase): except json.JSONDecodeError: pass else: - #self.fail("Expected failure for fail{0}.json: {1!r}".format(idx, doc)) self.fail("Expected failure for fail%d.json: %r" % (idx, doc)) def test_array_decoder_issue46(self): @@ -81,11 +124,53 @@ class TestFail(TestCase): for doc in [u'[,]', '[,]']: try: json.loads(doc) - except json.JSONDecodeError, e: - self.assertEquals(e.pos, 1) - self.assertEquals(e.lineno, 1) - self.assertEquals(e.colno, 1) - except Exception, e: + except json.JSONDecodeError: + e = sys.exc_info()[1] + self.assertEqual(e.pos, 1) + self.assertEqual(e.lineno, 1) + self.assertEqual(e.colno, 2) + except Exception: + e = sys.exc_info()[1] + self.fail("Unexpected exception raised %r %s" % (e, e)) + else: + self.fail("Unexpected success parsing '[,]'") + + def test_truncated_input(self): + test_cases = [ + ('', 'Expecting value', 0), + ('[', "Expecting value or ']'", 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), + ('"', 'Unterminated string starting at', 0), + ('"spam', 'Unterminated string starting at', 0), + ('[,', "Expecting value", 1), + ] + for data, msg, idx in test_cases: + try: + json.loads(data) + except json.JSONDecodeError: + e = sys.exc_info()[1] + self.assertEqual( + e.msg[:len(msg)], + msg, + "%r doesn't start with %r for %r" % (e.msg, msg, data)) + self.assertEqual( + e.pos, idx, + "pos %r != %r for %r" % (e.pos, idx, data)) + except Exception: + e = sys.exc_info()[1] self.fail("Unexpected exception raised %r %s" % (e, e)) else: - self.fail("Unexpected success parsing '[,]'")
\ No newline at end of file + self.fail("Unexpected success parsing '%r'" % (data,)) diff --git a/simplejson/tests/test_float.py b/simplejson/tests/test_float.py index 94502c6..e382ec2 100644 --- a/simplejson/tests/test_float.py +++ b/simplejson/tests/test_float.py @@ -1,19 +1,35 @@ import math from unittest import TestCase - +from simplejson.compat import long_type, text_type import simplejson as json +from simplejson.decoder import NaN, PosInf, NegInf class TestFloat(TestCase): + def test_degenerates_allow(self): + for inf in (PosInf, NegInf): + self.assertEqual(json.loads(json.dumps(inf)), inf) + # Python 2.5 doesn't have math.isnan + nan = json.loads(json.dumps(NaN)) + self.assertTrue((0 + nan) != nan) + + def test_degenerates_ignore(self): + for f in (PosInf, NegInf, NaN): + self.assertEqual(json.loads(json.dumps(f, ignore_nan=True)), None) + + def test_degenerates_deny(self): + for f in (PosInf, NegInf, NaN): + self.assertRaises(ValueError, json.dumps, f, allow_nan=False) + def test_floats(self): for num in [1617161771.7650001, math.pi, math.pi**100, math.pi**-100, 3.1]: - self.assertEquals(float(json.dumps(num)), num) - self.assertEquals(json.loads(json.dumps(num)), num) - self.assertEquals(json.loads(unicode(json.dumps(num))), num) + self.assertEqual(float(json.dumps(num)), num) + self.assertEqual(json.loads(json.dumps(num)), num) + self.assertEqual(json.loads(text_type(json.dumps(num))), num) def test_ints(self): - for num in [1, 1L, 1<<32, 1<<64]: - self.assertEquals(json.dumps(num), str(num)) - self.assertEquals(int(json.dumps(num)), num) - self.assertEquals(json.loads(json.dumps(num)), num) - self.assertEquals(json.loads(unicode(json.dumps(num))), num) + for num in [1, long_type(1), 1<<32, 1<<64]: + self.assertEqual(json.dumps(num), str(num)) + self.assertEqual(int(json.dumps(num)), num) + self.assertEqual(json.loads(json.dumps(num)), num) + self.assertEqual(json.loads(text_type(json.dumps(num))), num) diff --git a/simplejson/tests/test_for_json.py b/simplejson/tests/test_for_json.py new file mode 100644 index 0000000..b791b88 --- /dev/null +++ b/simplejson/tests/test_for_json.py @@ -0,0 +1,97 @@ +import unittest +import simplejson as json + + +class ForJson(object): + def for_json(self): + return {'for_json': 1} + + +class NestedForJson(object): + def for_json(self): + return {'nested': ForJson()} + + +class ForJsonList(object): + def for_json(self): + return ['list'] + + +class DictForJson(dict): + def for_json(self): + return {'alpha': 1} + + +class ListForJson(list): + def for_json(self): + return ['list'] + + +class TestForJson(unittest.TestCase): + def assertRoundTrip(self, obj, other, for_json=True): + if for_json is None: + # None will use the default + s = json.dumps(obj) + else: + s = json.dumps(obj, for_json=for_json) + self.assertEqual( + json.loads(s), + other) + + def test_for_json_encodes_stand_alone_object(self): + self.assertRoundTrip( + ForJson(), + ForJson().for_json()) + + def test_for_json_encodes_object_nested_in_dict(self): + self.assertRoundTrip( + {'hooray': ForJson()}, + {'hooray': ForJson().for_json()}) + + def test_for_json_encodes_object_nested_in_list_within_dict(self): + self.assertRoundTrip( + {'list': [0, ForJson(), 2, 3]}, + {'list': [0, ForJson().for_json(), 2, 3]}) + + def test_for_json_encodes_object_nested_within_object(self): + self.assertRoundTrip( + NestedForJson(), + {'nested': {'for_json': 1}}) + + def test_for_json_encodes_list(self): + self.assertRoundTrip( + ForJsonList(), + ForJsonList().for_json()) + + def test_for_json_encodes_list_within_object(self): + self.assertRoundTrip( + {'nested': ForJsonList()}, + {'nested': ForJsonList().for_json()}) + + def test_for_json_encodes_dict_subclass(self): + self.assertRoundTrip( + DictForJson(a=1), + DictForJson(a=1).for_json()) + + def test_for_json_encodes_list_subclass(self): + self.assertRoundTrip( + ListForJson(['l']), + ListForJson(['l']).for_json()) + + def test_for_json_ignored_if_not_true_with_dict_subclass(self): + for for_json in (None, False): + self.assertRoundTrip( + DictForJson(a=1), + {'a': 1}, + for_json=for_json) + + def test_for_json_ignored_if_not_true_with_list_subclass(self): + for for_json in (None, False): + self.assertRoundTrip( + ListForJson(['l']), + ['l'], + for_json=for_json) + + def test_raises_typeerror_if_for_json_not_true_with_object(self): + self.assertRaises(TypeError, json.dumps, ForJson()) + self.assertRaises(TypeError, json.dumps, ForJson(), for_json=False) diff --git a/simplejson/tests/test_indent.py b/simplejson/tests/test_indent.py index 1e6bdb1..cea25a5 100644 --- a/simplejson/tests/test_indent.py +++ b/simplejson/tests/test_indent.py @@ -1,8 +1,8 @@ from unittest import TestCase +import textwrap import simplejson as json -import textwrap -from StringIO import StringIO +from simplejson.compat import StringIO class TestIndent(TestCase): def test_indent(self): @@ -42,26 +42,26 @@ class TestIndent(TestCase): h3 = json.loads(d3) h4 = json.loads(d4) - self.assertEquals(h1, h) - self.assertEquals(h2, h) - self.assertEquals(h3, h) - self.assertEquals(h4, h) - self.assertEquals(d3, expect.replace('\t', ' ')) - self.assertEquals(d4, expect.replace('\t', ' ')) + self.assertEqual(h1, h) + self.assertEqual(h2, h) + self.assertEqual(h3, h) + self.assertEqual(h4, h) + self.assertEqual(d3, expect.replace('\t', ' ')) + self.assertEqual(d4, expect.replace('\t', ' ')) # NOTE: Python 2.4 textwrap.dedent converts tabs to spaces, # so the following is expected to fail. Python 2.4 is not a # supported platform in simplejson 2.1.0+. - self.assertEquals(d2, expect) + self.assertEqual(d2, expect) def test_indent0(self): h = {3: 1} def check(indent, expected): d1 = json.dumps(h, indent=indent) - self.assertEquals(d1, expected) + self.assertEqual(d1, expected) sio = StringIO() json.dump(h, sio, indent=indent) - self.assertEquals(sio.getvalue(), expected) + self.assertEqual(sio.getvalue(), expected) # indent=0 should emit newlines check(0, '{\n"3": 1\n}') @@ -73,14 +73,14 @@ class TestIndent(TestCase): expect = '[\n1,\n2,\n3,\n4\n]' expect_spaces = '[\n1, \n2, \n3, \n4\n]' # Ensure that separators still works - self.assertEquals( + self.assertEqual( expect_spaces, json.dumps(lst, indent=0, separators=(', ', ': '))) # Force the new defaults - self.assertEquals( + self.assertEqual( expect, json.dumps(lst, indent=0, separators=(',', ': '))) # Added in 2.1.4 - self.assertEquals( + self.assertEqual( expect, - json.dumps(lst, indent=0))
\ No newline at end of file + json.dumps(lst, indent=0)) diff --git a/simplejson/tests/test_item_sort_key.py b/simplejson/tests/test_item_sort_key.py new file mode 100644 index 0000000..b05bfc8 --- /dev/null +++ b/simplejson/tests/test_item_sort_key.py @@ -0,0 +1,20 @@ +from unittest import TestCase + +import simplejson as json +from operator import itemgetter + +class TestItemSortKey(TestCase): + def test_simple_first(self): + a = {'a': 1, 'c': 5, 'jack': 'jill', 'pick': 'axe', 'array': [1, 5, 6, 9], 'tuple': (83, 12, 3), 'crate': 'dog', 'zeak': 'oh'} + self.assertEqual( + '{"a": 1, "c": 5, "crate": "dog", "jack": "jill", "pick": "axe", "zeak": "oh", "array": [1, 5, 6, 9], "tuple": [83, 12, 3]}', + json.dumps(a, item_sort_key=json.simple_first)) + + def test_case(self): + a = {'a': 1, 'c': 5, 'Jack': 'jill', 'pick': 'axe', 'Array': [1, 5, 6, 9], 'tuple': (83, 12, 3), 'crate': 'dog', 'zeak': 'oh'} + self.assertEqual( + '{"Array": [1, 5, 6, 9], "Jack": "jill", "a": 1, "c": 5, "crate": "dog", "pick": "axe", "tuple": [83, 12, 3], "zeak": "oh"}', + json.dumps(a, item_sort_key=itemgetter(0))) + self.assertEqual( + '{"a": 1, "Array": [1, 5, 6, 9], "c": 5, "crate": "dog", "Jack": "jill", "pick": "axe", "tuple": [83, 12, 3], "zeak": "oh"}', + json.dumps(a, item_sort_key=lambda kv: kv[0].lower())) diff --git a/simplejson/tests/test_namedtuple.py b/simplejson/tests/test_namedtuple.py index 18da218..4387894 100644 --- a/simplejson/tests/test_namedtuple.py +++ b/simplejson/tests/test_namedtuple.py @@ -1,6 +1,7 @@ +from __future__ import absolute_import import unittest import simplejson as json -from StringIO import StringIO +from simplejson.compat import StringIO try: from collections import namedtuple @@ -21,11 +22,36 @@ else: Value = namedtuple('Value', ['value']) Point = namedtuple('Point', ['x', 'y']) +class DuckValue(object): + def __init__(self, *args): + self.value = Value(*args) + + def _asdict(self): + return self.value._asdict() + +class DuckPoint(object): + def __init__(self, *args): + self.point = Point(*args) + + def _asdict(self): + return self.point._asdict() + +class DeadDuck(object): + _asdict = None + +class DeadDict(dict): + _asdict = None + +CONSTRUCTORS = [ + lambda v: v, + lambda v: [v], + lambda v: [{'key': v}], +] + class TestNamedTuple(unittest.TestCase): def test_namedtuple_dumps(self): - for v in [Value(1), Point(1, 2)]: + for v in [Value(1), Point(1, 2), DuckValue(1), DuckPoint(1, 2)]: d = v._asdict() - l = list(v) self.assertEqual(d, json.loads(json.dumps(v))) self.assertEqual( d, @@ -35,6 +61,10 @@ class TestNamedTuple(unittest.TestCase): d, json.loads(json.dumps(v, namedtuple_as_object=True, tuple_as_array=False))) + + def test_namedtuple_dumps_false(self): + for v in [Value(1), Point(1, 2)]: + l = list(v) self.assertEqual( l, json.loads(json.dumps(v, namedtuple_as_object=False))) @@ -42,9 +72,8 @@ class TestNamedTuple(unittest.TestCase): tuple_as_array=False, namedtuple_as_object=False) def test_namedtuple_dump(self): - for v in [Value(1), Point(1, 2)]: + for v in [Value(1), Point(1, 2), DuckValue(1), DuckPoint(1, 2)]: d = v._asdict() - l = list(v) sio = StringIO() json.dump(v, sio) self.assertEqual(d, json.loads(sio.getvalue())) @@ -62,6 +91,10 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual( d, json.loads(sio.getvalue())) + + def test_namedtuple_dump_false(self): + for v in [Value(1), Point(1, 2)]: + l = list(v) sio = StringIO() json.dump(v, sio, namedtuple_as_object=False) self.assertEqual( @@ -69,3 +102,21 @@ class TestNamedTuple(unittest.TestCase): json.loads(sio.getvalue())) self.assertRaises(TypeError, json.dump, v, StringIO(), tuple_as_array=False, namedtuple_as_object=False) + + def test_asdict_not_callable_dump(self): + for f in CONSTRUCTORS: + self.assertRaises(TypeError, + json.dump, f(DeadDuck()), StringIO(), namedtuple_as_object=True) + sio = StringIO() + json.dump(f(DeadDict()), sio, namedtuple_as_object=True) + self.assertEqual( + json.dumps(f({})), + sio.getvalue()) + + def test_asdict_not_callable_dumps(self): + for f in CONSTRUCTORS: + self.assertRaises(TypeError, + json.dumps, f(DeadDuck()), namedtuple_as_object=True) + self.assertEqual( + json.dumps(f({})), + json.dumps(f(DeadDict()), namedtuple_as_object=True)) diff --git a/simplejson/tests/test_pass1.py b/simplejson/tests/test_pass1.py index c3d6302..f0b5b10 100644 --- a/simplejson/tests/test_pass1.py +++ b/simplejson/tests/test_pass1.py @@ -18,7 +18,7 @@ JSON = r''' "real": -9876.543210, "e": 0.123456789e-12, "E": 1.234567890E+34, - "": 23456789012E666, + "": 23456789012E66, "zero": 0, "one": 1, "space": " ", @@ -44,8 +44,7 @@ JSON = r''' , -4 , 5 , 6 ,7 ], - "compact": [1,2,3,4,5,6,7], +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~!@#$%^&*()_+-=[]{}|;:',./<>?" @@ -56,9 +55,11 @@ JSON = r''' 99.44 , -1066 - - +1066, +1e1, +0.1e1, +1e-1, +1e00,2e+00,2e-00 ,"rosebud"] ''' @@ -67,10 +68,4 @@ class TestPass1(TestCase): # test in/out equivalence and parsing res = json.loads(JSON) out = json.dumps(res) - self.assertEquals(res, json.loads(out)) - try: - json.dumps(res, allow_nan=False) - except ValueError: - pass - else: - self.fail("23456789012E666 should be out of range") + self.assertEqual(res, json.loads(out)) diff --git a/simplejson/tests/test_pass2.py b/simplejson/tests/test_pass2.py index de4ee00..5d812b3 100644 --- a/simplejson/tests/test_pass2.py +++ b/simplejson/tests/test_pass2.py @@ -11,4 +11,4 @@ class TestPass2(TestCase): # test in/out equivalence and parsing res = json.loads(JSON) out = json.dumps(res) - self.assertEquals(res, json.loads(out)) + self.assertEqual(res, json.loads(out)) diff --git a/simplejson/tests/test_pass3.py b/simplejson/tests/test_pass3.py index f591aba..821d60b 100644 --- a/simplejson/tests/test_pass3.py +++ b/simplejson/tests/test_pass3.py @@ -17,4 +17,4 @@ class TestPass3(TestCase): # test in/out equivalence and parsing res = json.loads(JSON) out = json.dumps(res) - self.assertEquals(res, json.loads(out)) + self.assertEqual(res, json.loads(out)) diff --git a/simplejson/tests/test_recursion.py b/simplejson/tests/test_recursion.py index 83a1d88..662eb66 100644 --- a/simplejson/tests/test_recursion.py +++ b/simplejson/tests/test_recursion.py @@ -57,7 +57,7 @@ class TestRecursion(TestCase): def test_defaultrecursion(self): enc = RecursiveJSONEncoder() - self.assertEquals(enc.encode(JSONTestObject), '"JSONTestObject"') + self.assertEqual(enc.encode(JSONTestObject), '"JSONTestObject"') enc.recurse = True try: enc.encode(JSONTestObject) diff --git a/simplejson/tests/test_scanstring.py b/simplejson/tests/test_scanstring.py index a7fcd46..3d98f0d 100644 --- a/simplejson/tests/test_scanstring.py +++ b/simplejson/tests/test_scanstring.py @@ -3,8 +3,17 @@ from unittest import TestCase import simplejson as json import simplejson.decoder +from simplejson.compat import b, PY3 class TestScanString(TestCase): + # The bytes type is intentionally not used in most of these tests + # under Python 3 because the decoder immediately coerces to str before + # calling scanstring. In Python 2 we are testing the code paths + # for both unicode and str. + # + # The reason this is done is because Python 3 would require + # entirely different code paths for parsing bytes and str. + # def test_py_scanstring(self): self._test_scanstring(simplejson.decoder.py_scanstring) @@ -14,104 +23,172 @@ class TestScanString(TestCase): self._test_scanstring(simplejson.decoder.c_scanstring) def _test_scanstring(self, scanstring): - self.assertEquals( - scanstring('"z\\ud834\\udd20x"', 1, None, True), - (u'z\U0001d120x', 16)) - if sys.maxunicode == 65535: - self.assertEquals( + self.assertEqual( scanstring(u'"z\U0001d120x"', 1, None, True), (u'z\U0001d120x', 6)) else: - self.assertEquals( + self.assertEqual( scanstring(u'"z\U0001d120x"', 1, None, True), (u'z\U0001d120x', 5)) - self.assertEquals( + self.assertEqual( scanstring('"\\u007b"', 1, None, True), (u'{', 8)) - self.assertEquals( + self.assertEqual( scanstring('"A JSON payload should be an object or array, not a string."', 1, None, True), (u'A JSON payload should be an object or array, not a string.', 60)) - self.assertEquals( + self.assertEqual( scanstring('["Unclosed array"', 2, None, True), (u'Unclosed array', 17)) - self.assertEquals( + self.assertEqual( scanstring('["extra comma",]', 2, None, True), (u'extra comma', 14)) - self.assertEquals( + self.assertEqual( scanstring('["double extra comma",,]', 2, None, True), (u'double extra comma', 21)) - self.assertEquals( + self.assertEqual( scanstring('["Comma after the close"],', 2, None, True), (u'Comma after the close', 24)) - self.assertEquals( + self.assertEqual( scanstring('["Extra close"]]', 2, None, True), (u'Extra close', 14)) - self.assertEquals( + self.assertEqual( scanstring('{"Extra comma": true,}', 2, None, True), (u'Extra comma', 14)) - self.assertEquals( + self.assertEqual( scanstring('{"Extra value after close": true} "misplaced quoted value"', 2, None, True), (u'Extra value after close', 26)) - self.assertEquals( + self.assertEqual( scanstring('{"Illegal expression": 1 + 2}', 2, None, True), (u'Illegal expression', 21)) - self.assertEquals( + self.assertEqual( scanstring('{"Illegal invocation": alert()}', 2, None, True), (u'Illegal invocation', 21)) - self.assertEquals( + self.assertEqual( scanstring('{"Numbers cannot have leading zeroes": 013}', 2, None, True), (u'Numbers cannot have leading zeroes', 37)) - self.assertEquals( + self.assertEqual( scanstring('{"Numbers cannot be hex": 0x14}', 2, None, True), (u'Numbers cannot be hex', 24)) - self.assertEquals( + self.assertEqual( scanstring('[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', 21, None, True), (u'Too deep', 30)) - self.assertEquals( + self.assertEqual( scanstring('{"Missing colon" null}', 2, None, True), (u'Missing colon', 16)) - self.assertEquals( + self.assertEqual( scanstring('{"Double colon":: null}', 2, None, True), (u'Double colon', 15)) - self.assertEquals( + self.assertEqual( scanstring('{"Comma instead of colon", null}', 2, None, True), (u'Comma instead of colon', 25)) - self.assertEquals( + self.assertEqual( scanstring('["Colon instead of comma": false]', 2, None, True), (u'Colon instead of comma', 25)) - self.assertEquals( + self.assertEqual( scanstring('["Bad value", truth]', 2, None, True), (u'Bad value', 12)) + for c in map(chr, range(0x00, 0x1f)): + self.assertEqual( + scanstring(c + '"', 0, None, False), + (c, 2)) + self.assertRaises( + ValueError, + scanstring, c + '"', 0, None, True) + + self.assertRaises(ValueError, scanstring, '', 0, None, True) + self.assertRaises(ValueError, scanstring, 'a', 0, None, True) + self.assertRaises(ValueError, scanstring, '\\', 0, None, True) + self.assertRaises(ValueError, scanstring, '\\u', 0, None, True) + self.assertRaises(ValueError, scanstring, '\\u0', 0, None, True) + self.assertRaises(ValueError, scanstring, '\\u01', 0, None, True) + self.assertRaises(ValueError, scanstring, '\\u012', 0, None, True) + self.assertRaises(ValueError, scanstring, '\\u0123', 0, None, True) + if sys.maxunicode > 65535: + self.assertRaises(ValueError, + scanstring, '\\ud834\\u"', 0, None, True) + self.assertRaises(ValueError, + scanstring, '\\ud834\\x0123"', 0, None, True) + def test_issue3623(self): self.assertRaises(ValueError, json.decoder.scanstring, "xxx", 1, "xxx") self.assertRaises(UnicodeDecodeError, - json.encoder.encode_basestring_ascii, "xx\xff") + json.encoder.encode_basestring_ascii, b("xx\xff")) def test_overflow(self): - # Python 2.5 does not have maxsize - maxsize = getattr(sys, 'maxsize', sys.maxint) + # Python 2.5 does not have maxsize, Python 3 does not have maxint + maxsize = getattr(sys, 'maxsize', getattr(sys, 'maxint', None)) + assert maxsize is not None self.assertRaises(OverflowError, json.decoder.scanstring, "xxx", maxsize + 1) + def test_surrogates(self): + scanstring = json.decoder.scanstring + + def assertScan(given, expect, test_utf8=True): + givens = [given] + if not PY3 and test_utf8: + givens.append(given.encode('utf8')) + for given in givens: + (res, count) = scanstring(given, 1, None, True) + self.assertEqual(len(given), count) + self.assertEqual(res, expect) + + assertScan( + u'"z\\ud834\\u0079x"', + u'z\ud834yx') + assertScan( + u'"z\\ud834\\udd20x"', + u'z\U0001d120x') + assertScan( + u'"z\\ud834\\ud834\\udd20x"', + u'z\ud834\U0001d120x') + assertScan( + u'"z\\ud834x"', + u'z\ud834x') + assertScan( + u'"z\\udd20x"', + u'z\udd20x') + assertScan( + u'"z\ud834x"', + u'z\ud834x') + # It may look strange to join strings together, but Python is drunk. + # https://gist.github.com/etrepum/5538443 + assertScan( + u'"z\\ud834\udd20x12345"', + u''.join([u'z\ud834', u'\udd20x12345'])) + assertScan( + u'"z\ud834\\udd20x"', + u''.join([u'z\ud834', u'\udd20x'])) + # these have different behavior given UTF8 input, because the surrogate + # pair may be joined (in maxunicode > 65535 builds) + assertScan( + u''.join([u'"z\ud834', u'\udd20x"']), + u''.join([u'z\ud834', u'\udd20x']), + test_utf8=False) + + self.assertRaises(ValueError, + scanstring, u'"z\\ud83x"', 1, None, True) + self.assertRaises(ValueError, + scanstring, u'"z\\ud834\\udd2x"', 1, None, True) diff --git a/simplejson/tests/test_separators.py b/simplejson/tests/test_separators.py index cbda93c..91b4d4f 100644 --- a/simplejson/tests/test_separators.py +++ b/simplejson/tests/test_separators.py @@ -37,6 +37,6 @@ class TestSeparators(TestCase): h1 = json.loads(d1) h2 = json.loads(d2) - self.assertEquals(h1, h) - self.assertEquals(h2, h) - self.assertEquals(d2, expect) + self.assertEqual(h1, h) + self.assertEqual(h2, h) + self.assertEqual(d2, expect) diff --git a/simplejson/tests/test_speedups.py b/simplejson/tests/test_speedups.py index 825ecf2..0a2b63b 100644 --- a/simplejson/tests/test_speedups.py +++ b/simplejson/tests/test_speedups.py @@ -1,20 +1,39 @@ +import sys +import unittest from unittest import TestCase from simplejson import encoder, scanner + def has_speedups(): return encoder.c_make_encoder is not None + +def skip_if_speedups_missing(func): + def wrapper(*args, **kwargs): + if not has_speedups(): + if hasattr(unittest, 'SkipTest'): + raise unittest.SkipTest("C Extension not available") + else: + sys.stdout.write("C Extension not available") + return + return func(*args, **kwargs) + + return wrapper + + class TestDecode(TestCase): + @skip_if_speedups_missing def test_make_scanner(self): - if not has_speedups(): - return self.assertRaises(AttributeError, scanner.c_make_scanner, 1) + @skip_if_speedups_missing def test_make_encoder(self): - if not has_speedups(): - return - self.assertRaises(TypeError, encoder.c_make_encoder, + self.assertRaises( + TypeError, + encoder.c_make_encoder, None, - "\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75", - None) + ("\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7" + "\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75"), + None + ) diff --git a/simplejson/tests/test_subclass.py b/simplejson/tests/test_subclass.py new file mode 100644 index 0000000..2bae3b6 --- /dev/null +++ b/simplejson/tests/test_subclass.py @@ -0,0 +1,37 @@ +from unittest import TestCase +import simplejson as json + +from decimal import Decimal + +class AlternateInt(int): + def __repr__(self): + return 'invalid json' + __str__ = __repr__ + + +class AlternateFloat(float): + def __repr__(self): + return 'invalid json' + __str__ = __repr__ + + +# class AlternateDecimal(Decimal): +# def __repr__(self): +# return 'invalid json' + + +class TestSubclass(TestCase): + def test_int(self): + self.assertEqual(json.dumps(AlternateInt(1)), '1') + self.assertEqual(json.dumps(AlternateInt(-1)), '-1') + self.assertEqual(json.loads(json.dumps({AlternateInt(1): 1})), {'1': 1}) + + def test_float(self): + self.assertEqual(json.dumps(AlternateFloat(1.0)), '1.0') + self.assertEqual(json.dumps(AlternateFloat(-1.0)), '-1.0') + self.assertEqual(json.loads(json.dumps({AlternateFloat(1.0): 1})), {'1.0': 1}) + + # NOTE: Decimal subclasses are not supported as-is + # def test_decimal(self): + # self.assertEqual(json.dumps(AlternateDecimal('1.0')), '1.0') + # self.assertEqual(json.dumps(AlternateDecimal('-1.0')), '-1.0') diff --git a/simplejson/tests/test_tool.py b/simplejson/tests/test_tool.py new file mode 100644 index 0000000..ac2a14c --- /dev/null +++ b/simplejson/tests/test_tool.py @@ -0,0 +1,97 @@ +from __future__ import with_statement +import os +import sys +import textwrap +import unittest +import subprocess +import tempfile +try: + # Python 3.x + from test.support import strip_python_stderr +except ImportError: + # Python 2.6+ + try: + from test.test_support import strip_python_stderr + except ImportError: + # Python 2.5 + import re + def strip_python_stderr(stderr): + return re.sub( + r"\[\d+ refs\]\r?\n?$".encode(), + "".encode(), + stderr).strip() + +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 runTool(self, args=None, data=None): + argv = [sys.executable, '-m', 'simplejson.tool'] + if args: + argv.extend(args) + proc = subprocess.Popen(argv, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE) + out, err = proc.communicate(data) + self.assertEqual(strip_python_stderr(err), ''.encode()) + self.assertEqual(proc.returncode, 0) + return out + + def test_stdin_stdout(self): + self.assertEqual( + self.runTool(data=self.data.encode()), + self.expect.encode()) + + def test_infile_stdout(self): + with tempfile.NamedTemporaryFile() as infile: + infile.write(self.data.encode()) + infile.flush() + self.assertEqual( + self.runTool(args=[infile.name]), + self.expect.encode()) + + def test_infile_outfile(self): + with tempfile.NamedTemporaryFile() as infile: + infile.write(self.data.encode()) + infile.flush() + # outfile will get overwritten by tool, so the delete + # may not work on some platforms. Do it manually. + outfile = tempfile.NamedTemporaryFile() + try: + self.assertEqual( + self.runTool(args=[infile.name, outfile.name]), + ''.encode()) + with open(outfile.name, 'rb') as f: + self.assertEqual(f.read(), self.expect.encode()) + finally: + outfile.close() + if os.path.exists(outfile.name): + os.unlink(outfile.name) diff --git a/simplejson/tests/test_tuple.py b/simplejson/tests/test_tuple.py index cff9a75..4ad7b0e 100644 --- a/simplejson/tests/test_tuple.py +++ b/simplejson/tests/test_tuple.py @@ -1,6 +1,6 @@ import unittest -from StringIO import StringIO +from simplejson.compat import StringIO import simplejson as json class TestTuples(unittest.TestCase): @@ -13,7 +13,8 @@ class TestTuples(unittest.TestCase): self.assertRaises(TypeError, json.dumps, t, tuple_as_array=False) # Ensure that the "default" does not get called self.assertEqual(expect, json.dumps(t, default=repr)) - self.assertEqual(expect, json.dumps(t, tuple_as_array=True, default=repr)) + self.assertEqual(expect, json.dumps(t, tuple_as_array=True, + default=repr)) # Ensure that the "default" gets called self.assertEqual( json.dumps(repr(t)), @@ -29,7 +30,8 @@ class TestTuples(unittest.TestCase): sio = StringIO() json.dump(t, sio, tuple_as_array=True) self.assertEqual(expect, sio.getvalue()) - self.assertRaises(TypeError, json.dump, t, StringIO(), tuple_as_array=False) + self.assertRaises(TypeError, json.dump, t, StringIO(), + tuple_as_array=False) # Ensure that the "default" does not get called sio = StringIO() json.dump(t, sio, default=repr) diff --git a/simplejson/tests/test_unicode.py b/simplejson/tests/test_unicode.py index 83fe65b..3b37f65 100644 --- a/simplejson/tests/test_unicode.py +++ b/simplejson/tests/test_unicode.py @@ -1,6 +1,9 @@ +import sys +import codecs from unittest import TestCase import simplejson as json +from simplejson.compat import unichr, text_type, b, u, BytesIO class TestUnicode(TestCase): def test_encoding1(self): @@ -9,51 +12,51 @@ class TestUnicode(TestCase): s = u.encode('utf-8') ju = encoder.encode(u) js = encoder.encode(s) - self.assertEquals(ju, js) + self.assertEqual(ju, js) def test_encoding2(self): u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' s = u.encode('utf-8') ju = json.dumps(u, encoding='utf-8') js = json.dumps(s, encoding='utf-8') - self.assertEquals(ju, js) + self.assertEqual(ju, js) def test_encoding3(self): u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' j = json.dumps(u) - self.assertEquals(j, '"\\u03b1\\u03a9"') + self.assertEqual(j, '"\\u03b1\\u03a9"') def test_encoding4(self): u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' j = json.dumps([u]) - self.assertEquals(j, '["\\u03b1\\u03a9"]') + self.assertEqual(j, '["\\u03b1\\u03a9"]') def test_encoding5(self): u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' j = json.dumps(u, ensure_ascii=False) - self.assertEquals(j, u'"' + u + u'"') + self.assertEqual(j, u'"' + u + u'"') def test_encoding6(self): u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' j = json.dumps([u], ensure_ascii=False) - self.assertEquals(j, u'["' + u + u'"]') + self.assertEqual(j, u'["' + u + u'"]') def test_big_unicode_encode(self): u = u'\U0001d120' - self.assertEquals(json.dumps(u), '"\\ud834\\udd20"') - self.assertEquals(json.dumps(u, ensure_ascii=False), u'"\U0001d120"') + self.assertEqual(json.dumps(u), '"\\ud834\\udd20"') + self.assertEqual(json.dumps(u, ensure_ascii=False), u'"\U0001d120"') def test_big_unicode_decode(self): u = u'z\U0001d120x' - self.assertEquals(json.loads('"' + u + '"'), u) - self.assertEquals(json.loads('"z\\ud834\\udd20x"'), u) + self.assertEqual(json.loads('"' + u + '"'), u) + self.assertEqual(json.loads('"z\\ud834\\udd20x"'), u) def test_unicode_decode(self): for i in range(0, 0xd7ff): u = unichr(i) #s = '"\\u{0:04x}"'.format(i) s = '"\\u%04x"' % (i,) - self.assertEquals(json.loads(s), u) + self.assertEqual(json.loads(s), u) def test_object_pairs_hook_with_unicode(self): s = u'{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' @@ -72,38 +75,79 @@ class TestUnicode(TestCase): def test_default_encoding(self): - self.assertEquals(json.loads(u'{"a": "\xe9"}'.encode('utf-8')), + self.assertEqual(json.loads(u'{"a": "\xe9"}'.encode('utf-8')), {'a': u'\xe9'}) def test_unicode_preservation(self): - self.assertEquals(type(json.loads(u'""')), unicode) - self.assertEquals(type(json.loads(u'"a"')), unicode) - self.assertEquals(type(json.loads(u'["a"]')[0]), unicode) + self.assertEqual(type(json.loads(u'""')), text_type) + self.assertEqual(type(json.loads(u'"a"')), text_type) + self.assertEqual(type(json.loads(u'["a"]')[0]), text_type) def test_ensure_ascii_false_returns_unicode(self): # http://code.google.com/p/simplejson/issues/detail?id=48 - self.assertEquals(type(json.dumps([], ensure_ascii=False)), unicode) - self.assertEquals(type(json.dumps(0, ensure_ascii=False)), unicode) - self.assertEquals(type(json.dumps({}, ensure_ascii=False)), unicode) - self.assertEquals(type(json.dumps("", ensure_ascii=False)), unicode) + self.assertEqual(type(json.dumps([], ensure_ascii=False)), text_type) + self.assertEqual(type(json.dumps(0, ensure_ascii=False)), text_type) + self.assertEqual(type(json.dumps({}, ensure_ascii=False)), text_type) + self.assertEqual(type(json.dumps("", ensure_ascii=False)), text_type) def test_ensure_ascii_false_bytestring_encoding(self): # http://code.google.com/p/simplejson/issues/detail?id=48 - doc1 = {u'quux': 'Arr\xc3\xaat sur images'} - doc2 = {u'quux': u'Arr\xeat sur images'} + doc1 = {u'quux': b('Arr\xc3\xaat sur images')} + doc2 = {u'quux': u('Arr\xeat sur images')} doc_ascii = '{"quux": "Arr\\u00eat sur images"}' doc_unicode = u'{"quux": "Arr\xeat sur images"}' - self.assertEquals(json.dumps(doc1), doc_ascii) - self.assertEquals(json.dumps(doc2), doc_ascii) - self.assertEquals(json.dumps(doc1, ensure_ascii=False), doc_unicode) - self.assertEquals(json.dumps(doc2, ensure_ascii=False), doc_unicode) + self.assertEqual(json.dumps(doc1), doc_ascii) + self.assertEqual(json.dumps(doc2), doc_ascii) + self.assertEqual(json.dumps(doc1, ensure_ascii=False), doc_unicode) + self.assertEqual(json.dumps(doc2, ensure_ascii=False), doc_unicode) def test_ensure_ascii_linebreak_encoding(self): # http://timelessrepo.com/json-isnt-a-javascript-subset s1 = u'\u2029\u2028' s2 = s1.encode('utf8') expect = '"\\u2029\\u2028"' - self.assertEquals(json.dumps(s1), expect) - self.assertEquals(json.dumps(s2), expect) - self.assertEquals(json.dumps(s1, ensure_ascii=False), expect) - self.assertEquals(json.dumps(s2, ensure_ascii=False), expect) + self.assertEqual(json.dumps(s1), expect) + self.assertEqual(json.dumps(s2), expect) + self.assertEqual(json.dumps(s1, ensure_ascii=False), expect) + self.assertEqual(json.dumps(s2, ensure_ascii=False), expect) + + def test_invalid_escape_sequences(self): + # incomplete escape sequence + self.assertRaises(json.JSONDecodeError, json.loads, '"\\u') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\u1') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\u12') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\u123') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\u1234') + # invalid escape sequence + self.assertRaises(json.JSONDecodeError, json.loads, '"\\u123x"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\u12x4"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\u1x34"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ux234"') + if sys.maxunicode > 65535: + # invalid escape sequence for low surrogate + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ud800\\u"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ud800\\u0"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ud800\\u00"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ud800\\u000"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ud800\\u000x"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ud800\\u00x0"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ud800\\u0x00"') + self.assertRaises(json.JSONDecodeError, json.loads, '"\\ud800\\ux000"') + + def test_ensure_ascii_still_works(self): + # in the ascii range, ensure that everything is the same + for c in map(unichr, range(0, 127)): + self.assertEqual( + json.dumps(c, ensure_ascii=False), + json.dumps(c)) + snowman = u'\N{SNOWMAN}' + self.assertEqual( + json.dumps(c, ensure_ascii=False), + '"' + c + '"') + + def test_strip_bom(self): + content = u"\u3053\u3093\u306b\u3061\u308f" + json_doc = codecs.BOM_UTF8 + b(json.dumps(content)) + self.assertEqual(json.load(BytesIO(json_doc)), content) + for doc in json_doc, json_doc.decode('utf8'): + self.assertEqual(json.loads(doc), content) |