/* * Copyright 2010 Google Inc. * * 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. */ package elemental.json; import com.google.gwt.junit.DoNotRunWith; import com.google.gwt.junit.Platform; import com.google.gwt.junit.client.GWTTestCase; import elemental.json.impl.JsonUtil; /** * Tests for {@link JsonUtil} */ @DoNotRunWith(Platform.Prod) public class JsonUtilTest extends GWTTestCase { @Override public String getModuleName() { return "elemental.Elemental"; } public void testCoercions() { // test boolean coercions JsonBoolean boolTrue = Json.create(true); JsonBoolean boolFalse = Json.create(false); // true -> 1, false -> 0 assertEquals(true, boolTrue.asBoolean()); assertEquals(false, boolFalse.asBoolean()); JsonString trueString = Json.create("true"); JsonString falseString = Json.create(""); // "" -> false, others true assertEquals(true, trueString.asBoolean()); assertEquals(false, falseString.asBoolean()); // != 0 -> true, otherwise if 0.0 or -0.0 false JsonNumber trueNumber = Json.create(1.0); JsonNumber falseNumber = Json.create(0.0); JsonNumber falseNumber2 = Json.create(-0.0); assertEquals(true, trueNumber.asBoolean()); assertEquals(false, falseNumber.asBoolean()); assertEquals(false, falseNumber2.asBoolean()); // array or object is true assertEquals(true, Json.createArray().asBoolean()); assertEquals(true, Json.createObject().asBoolean()); // null is false assertEquals(false, Json.createNull().asBoolean()); // test number coercions assertEquals(1.0, boolTrue.asNumber()); assertEquals(0.0, boolFalse.asNumber()); assertEquals(42.0, Json.create("42").asNumber()); // non numbers are NaN assertTrue(Double.isNaN(trueString.asNumber())); // null is 0 assertEquals(0.0, Json.createNull().asNumber()); // "" is 0 assertEquals(0.0, falseString.asNumber()); // [] -> 0 assertEquals(0.0, Json.createArray().asNumber()); // [[42]] -> 42 JsonArray nested = Json.createArray(); JsonArray outer = Json.createArray(); outer.set(0, nested); nested.set(0, 42); assertEquals(42.0, outer.asNumber()); // [[42, 45]] -> NaN nested.set(1, 45); assertTrue(Double.isNaN(outer.asNumber())); // object -> NaN assertTrue(Double.isNaN(Json.createObject().asNumber())); // test string coercions assertEquals("true", boolTrue.asString()); assertEquals("false", boolFalse.asString()); assertEquals("true", trueString.asString()); assertEquals("null", Json.createNull().asString()); assertEquals("42", Json.create(42).asString()); // [[42, 45], [52, 55]] -> "42, 45, 52, 55" JsonArray inner2 = Json.createArray(); inner2.set(0, 52); inner2.set(1, 55); outer.set(1, inner2); assertEquals("42, 45, 52, 55", outer.asString()); // object -> [object Object] assertEquals("[object Object]", Json.createObject().asString()); } public void testEscapeControlChars() { String unicodeString = "\u2060Test\ufeffis a test\u17b5"; assertEquals("\\u2060Test\\ufeffis a test\\u17b5", JsonUtil.escapeControlChars(unicodeString)); } public void testIllegalParse() { try { JsonUtil.parse("{ \"a\": new String() }"); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } } public void testUnclosedString() { try { JsonUtil.parse("\"a"); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } } public void testUnclosedEmptyString() { try { JsonUtil.parse("\""); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } } public void testUnclosedArray() { try { JsonUtil.parse("[1"); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } } public void testUnclosedEmptyArray() { try { JsonUtil.parse("["); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } } public void testUnclosedObject() { try { JsonUtil.parse("{'a"); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } try { JsonUtil.parse("{'a'"); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } try { JsonUtil.parse("{'a':"); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } } public void testUnclosedEmptyObject() { try { JsonUtil.parse("{"); fail("Expected JsonException to be thrown"); } catch (JsonException je) { // Expected } } public void testLegalParse() { JsonValue obj = JsonUtil.parse( "{ \"a\":1, \"b\":\"hello\", \"c\": true," + "\"d\": null, \"e\": [1,2,3,4], \"f\": {} }"); assertNotNull(obj); } public void testNative() { JsonObject obj = Json.createObject(); obj.put("x", 42); Object nativeObj = obj.toNative(); JsonObject result = nativeMethod(nativeObj); assertEquals(43.0, result.get("y").asNumber()); } public void testQuote() { String badString = "\bThis\"is\ufeff\ta\\bad\nstring\u2029\u2029"; assertEquals("\"\\bThis\\\"is\\ufeff\\ta\\\\bad\\nstring" + "\\u2029\\u2029\"", JsonUtil.quote(badString)); } public void testStringify() { String json = "{\"a\":1,\"b\":\"hello\",\"c\":true," + "\"d\":null,\"e\":[1,2,3,4],\"f\":{\"x\":1}}"; assertEquals(json, JsonUtil.stringify(JsonUtil.parse(json))); } public void testStringifyCycle() { String json = "{\"a\":1,\"b\":\"hello\",\"c\":true," + "\"d\":null,\"e\":[1,2,3,4],\"f\":{\"x\":1}}"; JsonObject obj = JsonUtil.parse(json); obj.put("cycle", obj); try { elemental.json.impl.JsonUtil.stringify(obj); fail("Expected JsonException for object cycle"); } catch (JsonException je) { } } public void testStringifyIndent() { // test string taken from native Chrome window.JSON.stringify String json = "{\n" + " \"a\": 1,\n" + " \"b\": \"hello\",\n" + " \"c\": true,\n" + " \"d\": null,\n" + " \"e\": [\n" + " 1,\n" + " 2,\n" + " 3,\n" + " 4\n" + " ],\n" + " \"f\": {\n" + " \"x\": 1\n" + " }\n" + "}"; assertEquals(json, JsonUtil.stringify(JsonUtil.parse(json), 2)); } public void testStringifyNonCycle() { String json = "{\"a\":1,\"b\":\"hello\",\"c\":true," + "\"d\":null,\"e\":[1,2,3,4],\"f\":{\"x\":1}}"; JsonObject obj = JsonUtil.parse(json); JsonObject obj2 = JsonUtil.parse("{\"x\": 1, \"y\":2}"); obj.put("nocycle", obj2); obj.put("nocycle2", obj2); try { JsonUtil.stringify(obj); } catch (JsonException je) { fail("JsonException for object cycle when none exists: " + je); } } public void testStringifyOrder() { JsonObject obj = Json.instance().createObject(); obj.put("x", "hello"); obj.put("a", "world"); obj.put("2", 21); obj.put("1", 42); // numbers come first, in ascending order, non-numbers in order of assignment assertEquals("{\"1\":42,\"2\":21,\"x\":\"hello\",\"a\":\"world\"}", obj.toJson()); } public void testStringifySkipKeys() { String expectedJson = "{\"a\":1,\"b\":\"hello\",\"c\":true," + "\"d\":null,\"e\":[1,2,3,4],\"f\":{\"x\":1}}"; String json = "{\"a\":1,\"b\":\"hello\",\"c\":true," + "\"$H\": 1," + "\"__gwt_ObjectId\": 1," + "\"d\":null,\"e\":[1,2,3,4],\"f\":{\"x\":1}}"; assertEquals(expectedJson, JsonUtil.stringify( JsonUtil.parse(json))); } public void testStringifyDoubleNanInfinity() { JsonNumber json = Json.create(Double.NaN); assertEquals("null",JsonUtil.stringify(json)); json = Json.create(Double.POSITIVE_INFINITY); assertEquals("null",JsonUtil.stringify(json)); json = Json.create(Double.NEGATIVE_INFINITY); assertEquals("null",JsonUtil.stringify(json)); } public void testJsonNumberToJsonDoubleNanInfinity() { JsonNumber json = Json.create(Double.NaN); assertEquals("null",json.toJson()); json = Json.create(Double.POSITIVE_INFINITY); assertEquals("null",json.toJson()); json = Json.create(Double.NEGATIVE_INFINITY); assertEquals("null",json.toJson()); } private native JsonObject nativeMethod(Object o) /*-{ o.y = o.x + 1; return o; }-*/; public void testQuoteCharacters() { // See spec at https://tools.ietf.org/html/rfc7159 for (int i = 0; i < 0xffff; i++) { String unencodedString = String.valueOf((char) i); String res = JsonUtil.quote(unencodedString); if (res.equals("\"" + unencodedString + "\"")) { // passed through unescaped if (i == 0x20 || i == 0x21 || (i >= 0x23 && i <= 0x5b) || i >= 0x5d) { // ok for %x20-21 / %x23-5B / %x5D-10FFFF } else { fail("Character " + i + " must be escaped in JSON"); } } else { // Was escaped, should be in format \\X or \\uXXXX if (res.length() == 4) { // "\\X" char escapedChar = res.charAt(2); // btnfr\" if (escapedChar == 'b') { assertEquals('\b',i); } else if (escapedChar == 't') { assertEquals('\t',i); } else if (escapedChar == 'n') { assertEquals('\n',i); } else if (escapedChar == 'f') { assertEquals('\f',i); } else if (escapedChar == 'r') { assertEquals('\r',i); } else if (escapedChar == '"') { assertEquals('"',i); } else if (escapedChar == '\\') { assertEquals('\\',i); } else { fail("Character" + i + " was unexpectedly escaped as "+escapedChar); } } else { assertTrue("Character " + i + " was incorrectly encoded as " + res,res.matches("\"\\\\u....\"")); } } } } }