| # Copyright 2015 Google Inc. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import io |
| import math |
| import os |
| import sys |
| import unittest |
| |
| import json5 |
| |
| |
| class TestLoads(unittest.TestCase): |
| maxDiff = None |
| |
| def check(self, s, obj): |
| self.assertEqual(json5.loads(s), obj) |
| |
| def check_fail(self, s, err=None): |
| try: |
| json5.loads(s) |
| self.fail() # pragma: no cover |
| except ValueError as e: |
| if err: |
| self.assertEqual(err, str(e)) |
| |
| def test_arrays(self): |
| self.check('[]', []) |
| self.check('[0]', [0]) |
| self.check('[0,1]', [0, 1]) |
| self.check('[ 0 , 1 ]', [0, 1]) |
| |
| def test_bools(self): |
| self.check('true', True) |
| self.check('false', False) |
| |
| def test_cls_is_not_supported(self): |
| self.assertRaises(AssertionError, json5.loads, '1', cls=lambda x: x) |
| |
| def test_empty_strings_are_errors(self): |
| self.check_fail('', 'Empty strings are not legal JSON5') |
| |
| def test_encoding(self): |
| if sys.version_info[0] < 3: |
| s = '"\xf6"' |
| else: |
| s = b'"\xf6"' |
| self.assertEqual(json5.loads(s, encoding='iso-8859-1'), |
| u"\xf6") |
| |
| |
| def test_numbers(self): |
| # decimal literals |
| self.check('1', 1) |
| self.check('-1', -1) |
| self.check('+1', 1) |
| |
| # hex literals |
| self.check('0xf', 15) |
| self.check('0xfe', 254) |
| self.check('0xfff', 4095) |
| self.check('0XABCD', 43981) |
| self.check('0x123456', 1193046) |
| |
| # floats |
| self.check('1.5', 1.5) |
| self.check('1.5e3', 1500.0) |
| self.check('-0.5e-2', -0.005) |
| |
| # names |
| self.check('Infinity', float('inf')) |
| self.check('-Infinity', float('-inf')) |
| self.assertTrue(math.isnan(json5.loads('NaN'))) |
| self.assertTrue(math.isnan(json5.loads('-NaN'))) |
| |
| # syntax errors |
| self.check_fail('14d', '<string>:1 Unexpected "d" at column 3') |
| |
| def test_identifiers(self): |
| self.check('{a: 1}', {'a': 1}) |
| self.check('{$: 1}', {'$': 1}) |
| self.check('{_: 1}', {'_': 1}) |
| self.check('{a_b: 1}', {'a_b': 1}) |
| self.check('{a$: 1}', {'a$': 1}) |
| |
| # This valid JavaScript but not valid JSON5; keys must be identifiers |
| # or strings. |
| self.check_fail('{1: 1}') |
| |
| def test_identifiers_unicode(self): |
| self.check(u'{\xc3: 1}', {u'\xc3': 1}) |
| |
| def test_null(self): |
| self.check('null', None) |
| |
| def test_object_hook(self): |
| hook = lambda d: [d] |
| self.assertEqual(json5.loads('{foo: 1}', object_hook=hook), |
| [{"foo": 1}]) |
| |
| def test_object_pairs_hook(self): |
| hook = lambda pairs: pairs |
| self.assertEqual(json5.loads('{foo: 1, bar: 2}', |
| object_pairs_hook=hook), |
| [('foo', 1), ('bar', 2)]) |
| |
| def test_objects(self): |
| self.check('{}', {}) |
| self.check('{"foo": 0}', {"foo": 0}) |
| self.check('{"foo":0,"bar":1}', {"foo": 0, "bar": 1}) |
| self.check('{ "foo" : 0 , "bar" : 1 }', {"foo": 0, "bar": 1}) |
| |
| def test_parse_constant(self): |
| hook = lambda x: x |
| self.assertEqual(json5.loads('-Infinity', parse_constant=hook), |
| '-Infinity') |
| self.assertEqual(json5.loads('NaN', parse_constant=hook), |
| 'NaN') |
| |
| def test_parse_float(self): |
| hook = lambda x: x |
| self.assertEqual(json5.loads('1.0', parse_float=hook), '1.0') |
| |
| def test_parse_int(self): |
| hook = lambda x, base=10: x |
| self.assertEqual(json5.loads('1', parse_int=hook), '1') |
| |
| def test_sample_file(self): |
| path = os.path.join(os.path.dirname(__file__), '..', '..', |
| 'sample.json5') |
| with open(path) as fp: |
| obj = json5.load(fp) |
| self.assertEqual({ |
| u'oh': [ |
| u"we shouldn't forget", |
| u"arrays can have", |
| u"trailing commas too", |
| ], |
| u"this": u"is a multi-line string", |
| u"delta": 10, |
| u"hex": 3735928559, |
| u"finally": "a trailing comma", |
| u"here": "is another", |
| u"to": float("inf"), |
| u"while": True, |
| u"half": 0.5, |
| u"foo": u"bar" |
| }, obj) |
| |
| def test_strings(self): |
| self.check('"foo"', 'foo') |
| self.check("'foo'", 'foo') |
| |
| # escape chars |
| self.check("'\\b\\t\\f\\n\\r\\v\\\\'", '\b\t\f\n\r\v\\') |
| self.check("'\\''", "'") |
| self.check('"\\""', '"') |
| |
| # hex literals |
| self.check('"\\x66oo"', 'foo') |
| |
| # unicode literals |
| self.check('"\\u0066oo"', 'foo') |
| |
| # string literals w/ continuation markers at the end of the line. |
| # These should not have spaces is the result. |
| self.check('"foo\\\nbar"', 'foobar') |
| self.check("'foo\\\nbar'", 'foobar') |
| |
| # unterminated string literals. |
| self.check_fail('"\n') |
| self.check_fail("'\n") |
| |
| # bad hex literals |
| self.check_fail("'\\x0'") |
| self.check_fail("'\\xj'") |
| self.check_fail("'\\x0j'") |
| |
| # bad unicode literals |
| self.check_fail("'\\u0'") |
| self.check_fail("'\\u00'") |
| self.check_fail("'\\u000'") |
| self.check_fail("'\\u000j'") |
| self.check_fail("'\\u00j0'") |
| self.check_fail("'\\u0j00'") |
| self.check_fail("'\\uj000'") |
| |
| def test_whitespace(self): |
| self.check('\n1', 1) |
| self.check('\r1', 1) |
| self.check('\r\n1', 1) |
| self.check('\t1', 1) |
| self.check('\v1', 1) |
| self.check(u'\uFEFF 1', 1) |
| self.check(u'\u00A0 1', 1) |
| self.check(u'\u2028 1', 1) |
| self.check(u'\u2029 1', 1) |
| |
| |
| class TestDump(unittest.TestCase): |
| def test_basic(self): |
| sio = io.StringIO() |
| json5.dump(True, sio) |
| self.assertEqual('true', sio.getvalue()) |
| |
| |
| class TestDumps(unittest.TestCase): |
| maxDiff = None |
| |
| def check(self, obj, s): |
| self.assertEqual(json5.dumps(obj, compact=True), s) |
| |
| def test_arrays(self): |
| self.check([], '[]') |
| self.check([1, 2, 3], '[1,2,3]') |
| |
| def test_bools(self): |
| self.check(True, 'true') |
| self.check(False, 'false') |
| |
| def test_numbers(self): |
| self.check(15, '15') |
| |
| def test_null(self): |
| self.check(None, 'null') |
| |
| def test_objects(self): |
| self.check({'foo': 1}, '{foo:1}') |
| self.check({'foo bar': 1}, '{"foo bar":1}') |
| |
| def test_strings(self): |
| self.check("'single'", '"\'single\'"') |
| self.check('"double"', "'\"double\"'") |
| self.check("'single \\' and double \"'", |
| '"\'single \\\\\' and double \\"\'"') |