123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- // Copyright 2013 The Closure Library Authors. 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.
- goog.provide('goog.jsonTest');
- goog.setTestOnly('goog.jsonTest');
- goog.require('goog.functions');
- goog.require('goog.json');
- goog.require('goog.testing.jsunit');
- goog.require('goog.userAgent');
- function allChars(start, end) {
- var sb = [];
- for (var i = start; i < end; i++) {
- sb.push(String.fromCharCode(i));
- }
- return sb.join('');
- }
- // serialization
- function testStringSerialize() {
- assertSerialize('""', '');
- assertSerialize('"true"', 'true');
- assertSerialize('"false"', 'false');
- assertSerialize('"null"', 'null');
- assertSerialize('"0"', '0');
- // Unicode and control characters
- assertSerialize('"\\n"', '\n');
- assertSerialize('"\\u001f"', '\x1f');
- assertSerialize('"\\u20ac"', '\u20AC');
- assertSerialize('"\\ud83d\\ud83d"', '\ud83d\ud83d');
- var str = allChars(0, 10000);
- assertEquals(str, eval(goog.json.serialize(str)));
- }
- function testNullSerialize() {
- assertSerialize('null', null);
- assertSerialize('null', undefined);
- assertSerialize('null', NaN);
- assertSerialize('0', 0);
- assertSerialize('""', '');
- assertSerialize('false', false);
- }
- function testNullPropertySerialize() {
- assertSerialize('{"a":null}', {'a': null});
- assertSerialize('{"a":null}', {'a': undefined});
- }
- function testNumberSerialize() {
- assertSerialize('0', 0);
- assertSerialize('12345', 12345);
- assertSerialize('-12345', -12345);
- assertSerialize('0.1', 0.1);
- // the leading zero may not be omitted
- assertSerialize('0.1', .1);
- // no leading +
- assertSerialize('1', +1);
- // either format is OK
- var s = goog.json.serialize(1e50);
- assertTrue(
- '1e50', s == '1e50' || s == '1E50' || s == '1e+50' || s == '1E+50');
- // either format is OK
- s = goog.json.serialize(1e-50);
- assertTrue('1e50', s == '1e-50' || s == '1E-50');
- // These numbers cannot be represented in JSON
- assertSerialize('null', NaN);
- assertSerialize('null', Infinity);
- assertSerialize('null', -Infinity);
- }
- function testBooleanSerialize() {
- assertSerialize('true', true);
- assertSerialize('"true"', 'true');
- assertSerialize('false', false);
- assertSerialize('"false"', 'false');
- }
- function testArraySerialize() {
- assertSerialize('[]', []);
- assertSerialize('[1]', [1]);
- assertSerialize('[1,2]', [1, 2]);
- assertSerialize('[1,2,3]', [1, 2, 3]);
- assertSerialize('[[]]', [[]]);
- assertSerialize('[null,null]', [function() {}, function() {}]);
- assertNotEquals('{length:0}', goog.json.serialize({length: 0}), '[]');
- }
- function testFunctionSerialize() {
- assertSerialize('null', function() {});
- }
- function testObjectSerialize_emptyObject() {
- assertSerialize('{}', {});
- }
- function testObjectSerialize_oneItem() {
- assertSerialize('{"a":"b"}', {a: 'b'});
- }
- function testObjectSerialize_twoItems() {
- assertEquals(
- '{"a":"b","c":"d"}', goog.json.serialize({a: 'b', c: 'd'}),
- '{"a":"b","c":"d"}');
- }
- function testObjectSerialize_whitespace() {
- assertSerialize('{" ":" "}', {' ': ' '});
- }
- function testSerializeSkipFunction() {
- var object =
- {s: 'string value', b: true, i: 100, f: function() { var x = 'x'; }};
- assertSerialize('null', object.f);
- assertSerialize('{"s":"string value","b":true,"i":100}', object);
- }
- function testObjectSerialize_array() {
- assertNotEquals('[0,1]', goog.json.serialize([0, 1]), '{"0":"0","1":"1"}');
- }
- function testObjectSerialize_recursion() {
- if (goog.userAgent.WEBKIT) {
- return; // this makes safari 4 crash.
- }
- var anObject = {};
- anObject.thisObject = anObject;
- assertThrows('expected recursion exception', function() {
- goog.json.serialize(anObject);
- });
- }
- function testObjectSerializeWithHasOwnProperty() {
- var object = {'hasOwnProperty': null};
- if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) {
- assertEquals('{}', goog.json.serialize(object));
- } else {
- assertEquals('{"hasOwnProperty":null}', goog.json.serialize(object));
- }
- }
- function testWrappedObjects() {
- assertSerialize('"foo"', new String('foo'));
- assertSerialize('42', new Number(42));
- assertSerialize('null', new Number('a NaN'));
- assertSerialize('true', new Boolean(true));
- }
- // parsing
- function testStringParse() {
- assertEquals('Empty string', goog.json.parse('""'), '');
- assertEquals('whitespace string', goog.json.parse('" "'), ' ');
- // unicode without the control characters 0x00 - 0x1f, 0x7f - 0x9f
- var str = allChars(32, 1000);
- var jsonString = goog.json.serialize(str);
- var a = eval(jsonString);
- assertEquals('unicode string', goog.json.parse(jsonString), a);
- assertEquals('true as a string', goog.json.parse('"true"'), 'true');
- assertEquals('false as a string', goog.json.parse('"false"'), 'false');
- assertEquals('null as a string', goog.json.parse('"null"'), 'null');
- assertEquals('number as a string', goog.json.parse('"0"'), '0');
- }
- function testStringUnsafeParse() {
- assertEquals('Empty string', goog.json.unsafeParse('""'), '');
- assertEquals('whitespace string', goog.json.unsafeParse('" "'), ' ');
- // unicode
- var str = allChars(32, 1000);
- var jsonString = goog.json.serialize(str);
- var a = eval(jsonString);
- assertEquals('unicode string', goog.json.unsafeParse(jsonString), a);
- assertEquals('true as a string', goog.json.unsafeParse('"true"'), 'true');
- assertEquals('false as a string', goog.json.unsafeParse('"false"'), 'false');
- assertEquals('null as a string', goog.json.unsafeParse('"null"'), 'null');
- assertEquals('number as a string', goog.json.unsafeParse('"0"'), '0');
- }
- function testNullParse() {
- assertEquals('null', goog.json.parse(null), null);
- assertEquals('null', goog.json.parse('null'), null);
- assertNotEquals('0', goog.json.parse('0'), null);
- assertNotEquals('""', goog.json.parse('""'), null);
- assertNotEquals('false', goog.json.parse('false'), null);
- }
- function testNullUnsafeParse() {
- assertEquals('null', goog.json.unsafeParse(null), null);
- assertEquals('null', goog.json.unsafeParse('null'), null);
- assertNotEquals('0', goog.json.unsafeParse('0'), null);
- assertNotEquals('""', goog.json.unsafeParse('""'), null);
- assertNotEquals('false', goog.json.unsafeParse('false'), null);
- }
- function testNumberParse() {
- assertEquals('0', goog.json.parse('0'), 0);
- assertEquals('12345', goog.json.parse('12345'), 12345);
- assertEquals('-12345', goog.json.parse('-12345'), -12345);
- assertEquals('0.1', goog.json.parse('0.1'), 0.1);
- // either format is OK
- assertEquals(1e50, goog.json.parse('1e50'));
- assertEquals(1e50, goog.json.parse('1E50'));
- assertEquals(1e50, goog.json.parse('1e+50'));
- assertEquals(1e50, goog.json.parse('1E+50'));
- // either format is OK
- assertEquals(1e-50, goog.json.parse('1e-50'));
- assertEquals(1e-50, goog.json.parse('1E-50'));
- }
- function testNumberUnsafeParse() {
- assertEquals('0', goog.json.unsafeParse('0'), 0);
- assertEquals('12345', goog.json.unsafeParse('12345'), 12345);
- assertEquals('-12345', goog.json.unsafeParse('-12345'), -12345);
- assertEquals('0.1', goog.json.unsafeParse('0.1'), 0.1);
- // either format is OK
- assertEquals(1e50, goog.json.unsafeParse('1e50'));
- assertEquals(1e50, goog.json.unsafeParse('1E50'));
- assertEquals(1e50, goog.json.unsafeParse('1e+50'));
- assertEquals(1e50, goog.json.unsafeParse('1E+50'));
- // either format is OK
- assertEquals(1e-50, goog.json.unsafeParse('1e-50'));
- assertEquals(1e-50, goog.json.unsafeParse('1E-50'));
- }
- function testBooleanParse() {
- assertEquals('true', goog.json.parse('true'), true);
- assertEquals('false', goog.json.parse('false'), false);
- assertNotEquals('0', goog.json.parse('0'), false);
- assertNotEquals('"false"', goog.json.parse('"false"'), false);
- assertNotEquals('null', goog.json.parse('null'), false);
- assertNotEquals('1', goog.json.parse('1'), true);
- assertNotEquals('"true"', goog.json.parse('"true"'), true);
- assertNotEquals('{}', goog.json.parse('{}'), true);
- assertNotEquals('[]', goog.json.parse('[]'), true);
- }
- function testBooleanUnsafeParse() {
- assertEquals('true', goog.json.unsafeParse('true'), true);
- assertEquals('false', goog.json.unsafeParse('false'), false);
- assertNotEquals('0', goog.json.unsafeParse('0'), false);
- assertNotEquals('"false"', goog.json.unsafeParse('"false"'), false);
- assertNotEquals('null', goog.json.unsafeParse('null'), false);
- assertNotEquals('1', goog.json.unsafeParse('1'), true);
- assertNotEquals('"true"', goog.json.unsafeParse('"true"'), true);
- assertNotEquals('{}', goog.json.unsafeParse('{}'), true);
- assertNotEquals('[]', goog.json.unsafeParse('[]'), true);
- }
- function testArrayParse() {
- assertArrayEquals([], goog.json.parse('[]'));
- assertArrayEquals([1], goog.json.parse('[1]'));
- assertArrayEquals([1, 2], goog.json.parse('[1,2]'));
- assertArrayEquals([1, 2, 3], goog.json.parse('[1,2,3]'));
- assertArrayEquals([[]], goog.json.parse('[[]]'));
- // Note that array-holes are not valid json. However, goog.json.parse
- // supports them so that clients can reap the security benefits of
- // goog.json.parse even if they are using this non-standard format.
- assertArrayEquals([1, /* hole */, 3], goog.json.parse('[1,,3]'));
- // make sure we do not get an array for something that looks like an array
- assertFalse('{length:0}', 'push' in goog.json.parse('{"length":0}'));
- }
- function testArrayUnsafeParse() {
- function arrayEquals(a1, a2) {
- if (a1.length != a2.length) {
- return false;
- }
- for (var i = 0; i < a1.length; i++) {
- if (a1[i] != a2[i]) {
- return false;
- }
- }
- return true;
- }
- assertTrue('[]', arrayEquals(goog.json.unsafeParse('[]'), []));
- assertTrue('[1]', arrayEquals(goog.json.unsafeParse('[1]'), [1]));
- assertTrue('[1,2]', arrayEquals(goog.json.unsafeParse('[1,2]'), [1, 2]));
- assertTrue(
- '[1,2,3]', arrayEquals(goog.json.unsafeParse('[1,2,3]'), [1, 2, 3]));
- assertTrue('[[]]', arrayEquals(goog.json.unsafeParse('[[]]')[0], []));
- // make sure we do not get an array for something that looks like an array
- assertFalse('{length:0}', 'push' in goog.json.unsafeParse('{"length":0}'));
- }
- function testObjectParse() {
- function objectEquals(a1, a2) {
- for (var key in a1) {
- if (a1[key] != a2[key]) {
- return false;
- }
- }
- return true;
- }
- assertTrue('{}', objectEquals(goog.json.parse('{}'), {}));
- assertTrue('{"a":"b"}', objectEquals(goog.json.parse('{"a":"b"}'), {a: 'b'}));
- assertTrue(
- '{"a":"b","c":"d"}',
- objectEquals(goog.json.parse('{"a":"b","c":"d"}'), {a: 'b', c: 'd'}));
- assertTrue(
- '{" ":" "}', objectEquals(goog.json.parse('{" ":" "}'), {' ': ' '}));
- // make sure we do not get an Object when it is really an array
- assertTrue('[0,1]', 'length' in goog.json.parse('[0,1]'));
- }
- function testObjectUnsafeParse() {
- function objectEquals(a1, a2) {
- for (var key in a1) {
- if (a1[key] != a2[key]) {
- return false;
- }
- }
- return true;
- }
- assertTrue('{}', objectEquals(goog.json.unsafeParse('{}'), {}));
- assertTrue(
- '{"a":"b"}', objectEquals(goog.json.unsafeParse('{"a":"b"}'), {a: 'b'}));
- assertTrue(
- '{"a":"b","c":"d"}',
- objectEquals(
- goog.json.unsafeParse('{"a":"b","c":"d"}'), {a: 'b', c: 'd'}));
- assertTrue(
- '{" ":" "}',
- objectEquals(goog.json.unsafeParse('{" ":" "}'), {' ': ' '}));
- // make sure we do not get an Object when it is really an array
- assertTrue('[0,1]', 'length' in goog.json.unsafeParse('[0,1]'));
- }
- function testForValidJson() {
- function error_(msg, s) {
- assertThrows(
- msg + ', Should have raised an exception: ' + s,
- goog.partial(goog.json.parse, s));
- }
- error_('Non closed string', '"dasdas');
- error_('undefined is not valid json', 'undefined');
- // These numbers cannot be represented in JSON
- error_('NaN cannot be presented in JSON', 'NaN');
- error_('Infinity cannot be presented in JSON', 'Infinity');
- error_('-Infinity cannot be presented in JSON', '-Infinity');
- }
- function testIsNotValid() {
- assertFalse(goog.json.isValid('t'));
- assertFalse(goog.json.isValid('r'));
- assertFalse(goog.json.isValid('u'));
- assertFalse(goog.json.isValid('e'));
- assertFalse(goog.json.isValid('f'));
- assertFalse(goog.json.isValid('a'));
- assertFalse(goog.json.isValid('l'));
- assertFalse(goog.json.isValid('s'));
- assertFalse(goog.json.isValid('n'));
- assertFalse(goog.json.isValid('E'));
- assertFalse(goog.json.isValid('+'));
- assertFalse(goog.json.isValid('-'));
- assertFalse(goog.json.isValid('t++'));
- assertFalse(goog.json.isValid('++t'));
- assertFalse(goog.json.isValid('t--'));
- assertFalse(goog.json.isValid('--t'));
- assertFalse(goog.json.isValid('-t'));
- assertFalse(goog.json.isValid('+t'));
- assertFalse(goog.json.isValid('"\\"')); // "\"
- assertFalse(goog.json.isValid('"\\')); // "\
- // multiline string using \ at the end is not valid
- assertFalse(goog.json.isValid('"a\\\nb"'));
- assertFalse(goog.json.isValid('"\n"'));
- assertFalse(goog.json.isValid('"\r"'));
- assertFalse(goog.json.isValid('"\r\n"'));
- // Disallow the unicode newlines
- assertFalse(goog.json.isValid('"\u2028"'));
- assertFalse(goog.json.isValid('"\u2029"'));
- assertFalse(goog.json.isValid(' '));
- assertFalse(goog.json.isValid('\n'));
- assertFalse(goog.json.isValid('\r'));
- assertFalse(goog.json.isValid('\r\n'));
- assertFalse(goog.json.isValid('t.r'));
- assertFalse(goog.json.isValid('1e'));
- assertFalse(goog.json.isValid('1e-'));
- assertFalse(goog.json.isValid('1e+'));
- assertFalse(goog.json.isValid('1e-'));
- assertFalse(goog.json.isValid('"\\\u200D\\"'));
- assertFalse(goog.json.isValid('"\\\0\\"'));
- assertFalse(goog.json.isValid('"\\\0"'));
- assertFalse(goog.json.isValid('"\\0"'));
- assertFalse(goog.json.isValid('"\x0c"'));
- assertFalse(goog.json.isValid('"\\\u200D\\", alert(\'foo\') //"\n'));
- // Disallow referencing variables with names built up from primitives
- assertFalse(goog.json.isValid('truefalse'));
- assertFalse(goog.json.isValid('null0'));
- assertFalse(goog.json.isValid('null0.null0'));
- assertFalse(goog.json.isValid('[truefalse]'));
- assertFalse(goog.json.isValid('{"a": null0}'));
- assertFalse(goog.json.isValid('{"a": null0, "b": 1}'));
- }
- function testIsValid() {
- assertTrue(goog.json.isValid('\n""\n'));
- assertTrue(goog.json.isValid('[1\n,2\r,3\u2028\n,4\u2029]'));
- assertTrue(goog.json.isValid('"\x7f"'));
- assertTrue(goog.json.isValid('"\x09"'));
- // Test tab characters in json.
- assertTrue(goog.json.isValid('{"\t":"\t"}'));
- }
- function testDoNotSerializeProto() {
- function F(){};
- F.prototype = {c: 3};
- var obj = new F;
- obj.a = 1;
- obj.b = 2;
- assertEquals(
- 'Should not follow the prototype chain', '{"a":1,"b":2}',
- goog.json.serialize(obj));
- }
- function testEscape() {
- var unescaped = '1a*/]';
- assertEquals(
- 'Should not escape', '"' + unescaped + '"',
- goog.json.serialize(unescaped));
- var escaped = '\n\x7f\u1049';
- assertEquals(
- 'Should escape', '',
- findCommonChar(escaped, goog.json.serialize(escaped)));
- assertEquals(
- 'Should eval to the same string after escaping', escaped,
- goog.json.parse(goog.json.serialize(escaped)));
- }
- function testReplacer() {
- assertSerialize('[null,null,0]', [, , 0]);
- assertSerialize('[0,0,{"x":0}]', [, , {x: 0}], function(k, v) {
- if (v === undefined && goog.isArray(this)) {
- return 0;
- }
- return v;
- });
- assertSerialize('[0,1,2,3]', [0, 0, 0, 0], function(k, v) {
- var kNum = Number(k);
- if (k && !isNaN(kNum)) {
- return kNum;
- }
- return v;
- });
- var f = function(k, v) { return typeof v == 'number' ? v + 1 : v; };
- assertSerialize('{"a":1,"b":{"c":2}}', {'a': 0, 'b': {'c': 1}}, f);
- }
- function testDateSerialize() {
- assertSerialize('{}', new Date(0));
- }
- function testToJSONSerialize() {
- assertSerialize('{}', {toJSON: goog.functions.constant('serialized')});
- assertSerialize('{"toJSON":"normal"}', {toJSON: 'normal'});
- }
- function testTryNativeJson() {
- goog.json.TRY_NATIVE_JSON = true;
- var error;
- goog.json.setErrorLogger(function(message, ex) {
- error = message;
- });
- goog.json.unsafeParse('{"a":[,1]}');
- assertEquals('Invalid JSON: {"a":[,1]}', error);
- error = undefined;
- goog.json.parse('{"a":[,1]}');
- assertEquals('Invalid JSON: {"a":[,1]}', error);
- goog.json.TRY_NATIVE_JSON = false;
- goog.json.setErrorLogger(goog.nullFunction);
- }
- /**
- * Asserts that the given object serializes to the given value.
- * If the current browser has an implementation of JSON.serialize,
- * we make sure our version matches that one.
- */
- function assertSerialize(expected, obj, opt_replacer) {
- assertEquals(expected, goog.json.serialize(obj, opt_replacer));
- // goog.json.serialize escapes non-ASCI characters while JSON.stringify
- // doesn’t. This is expected so do not compare the results.
- if (typeof obj == 'string' && obj.charCodeAt(0) > 0x7f) return;
- // I'm pretty sure that the goog.json.serialize behavior is correct by the ES5
- // spec, but JSON.stringify(undefined) is undefined on all browsers.
- if (obj === undefined) return;
- // Browsers don't serialize undefined properties, but goog.json.serialize does
- if (goog.isObject(obj) && ('a' in obj) && obj['a'] === undefined) return;
- // Replacers are broken on IE and older versions of firefox.
- if (opt_replacer && !goog.userAgent.WEBKIT) return;
- // goog.json.serialize does not stringify dates the same way.
- if (obj instanceof Date) return;
- // goog.json.serialize does not stringify functions the same way.
- if (obj instanceof Function) return;
- // goog.json.serialize doesn't use the toJSON method.
- if (goog.isObject(obj) && goog.isFunction(obj.toJSON)) return;
- if (typeof JSON != 'undefined') {
- assertEquals(
- 'goog.json.serialize does not match JSON.stringify', expected,
- JSON.stringify(obj, opt_replacer));
- }
- }
- /**
- * @param {string} a
- * @param {string} b
- * @return {string} any common character between two strings a and b.
- */
- function findCommonChar(a, b) {
- for (var i = 0; i < b.length; i++) {
- if (a.indexOf(b.charAt(i)) >= 0) {
- return b.charAt(i);
- }
- }
- return '';
- }
|