blob: ac887a54b3475e1114000c50e8373040668f872c [file] [log] [blame]
/*
* 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....\""));
}
}
}
}
}