From 7b75efd09ecc1aeb0b5c7a020d9cf17b5dad8bad Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Sat, 11 Feb 2012 20:15:39 +0000 Subject: [PATCH] Write some tests prescribed by missing code coverage. I found a bug where our nonexecute prefix code causes a problem. --- .../com/google/gson/stream/JsonReader.java | 15 +- .../google/gson/stream/JsonReaderTest.java | 171 +++++++++++++++++- 2 files changed, 175 insertions(+), 11 deletions(-) diff --git a/gson/src/main/java/com/google/gson/stream/JsonReader.java b/gson/src/main/java/com/google/gson/stream/JsonReader.java index 4a54dc15..0c12b8a6 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonReader.java +++ b/gson/src/main/java/com/google/gson/stream/JsonReader.java @@ -401,6 +401,9 @@ public class JsonReader implements Closeable { * Consumes the non-execute prefix if it exists. */ private void consumeNonExecutePrefix() throws IOException { + // TODO: there's a bug here. We're going to call nextNonWhitespace and we have a character that + // we can't necessarily push back (because pos could be 0) + // fast forward through the leading whitespace nextNonWhitespace(true); pos--; @@ -1122,7 +1125,8 @@ public class JsonReader implements Closeable { } @Override public String toString() { - return getClass().getSimpleName() + " near " + getSnippet(); + return getClass().getSimpleName() + + " at line " + getLineNumber() + " column " + getColumnNumber(); } /** @@ -1299,15 +1303,6 @@ public class JsonReader implements Closeable { + " at line " + getLineNumber() + " column " + getColumnNumber()); } - private CharSequence getSnippet() { - StringBuilder snippet = new StringBuilder(); - int beforePos = Math.min(pos, 20); - snippet.append(buffer, pos - beforePos, beforePos); - int afterPos = Math.min(limit - pos, 20); - snippet.append(buffer, pos, afterPos); - return snippet; - } - static { JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() { @Override public void promoteNameToValue(JsonReader reader) throws IOException { diff --git a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java index ba0f8666..4f73c238 100644 --- a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java +++ b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java @@ -16,6 +16,7 @@ package com.google.gson.stream; +import java.io.EOFException; import java.io.IOException; import java.io.StringReader; import java.util.Arrays; @@ -183,6 +184,17 @@ public final class JsonReaderTest extends TestCase { } } + public void testUnescapingTruncatedSequence() throws IOException { + String json = "[\"\\"; + JsonReader reader = new JsonReader(new StringReader(json)); + reader.beginArray(); + try { + reader.nextString(); + fail(); + } catch (IOException expected) { + } + } + public void testIntegersWithFractionalPartSpecified() throws IOException { JsonReader reader = new JsonReader(new StringReader("[1.0,1.0,1.0]")); reader.beginArray(); @@ -227,6 +239,17 @@ public final class JsonReaderTest extends TestCase { } } + public void testStrictQuotedNonFiniteDoubles() throws IOException { + String json = "[\"NaN\"]"; + JsonReader reader = new JsonReader(new StringReader(json)); + reader.beginArray(); + try { + reader.nextDouble(); + fail(); + } catch (MalformedJsonException expected) { + } + } + public void testLenientNonFiniteDoubles() throws IOException { String json = "[NaN, -Infinity, Infinity]"; JsonReader reader = new JsonReader(new StringReader(json)); @@ -238,6 +261,17 @@ public final class JsonReaderTest extends TestCase { reader.endArray(); } + public void testLenientQuotedNonFiniteDoubles() throws IOException { + String json = "[\"NaN\", \"-Infinity\", \"Infinity\"]"; + JsonReader reader = new JsonReader(new StringReader(json)); + reader.setLenient(true); + reader.beginArray(); + assertTrue(Double.isNaN(reader.nextDouble())); + assertEquals(Double.NEGATIVE_INFINITY, reader.nextDouble()); + assertEquals(Double.POSITIVE_INFINITY, reader.nextDouble()); + reader.endArray(); + } + public void testStrictNonFiniteDoublesWithSkipValue() throws IOException { String json = "[NaN]"; JsonReader reader = new JsonReader(new StringReader(json)); @@ -1008,7 +1042,7 @@ public final class JsonReaderTest extends TestCase { testFailWithPosition("Expected literal value at line 1 column 4", "\ufeff[0,}]"); } - + private void testFailWithPosition(String message, String json) throws IOException { JsonReader reader = new JsonReader(new StringReader(json)); reader.beginArray(); @@ -1020,6 +1054,141 @@ public final class JsonReaderTest extends TestCase { assertEquals(message, expected.getMessage()); } } + + public void testDeeplyNestedArrays() throws IOException { + // this is nested 40 levels deep; Gson is tuned for nesting is 30 levels deep or fewer + JsonReader reader = new JsonReader(new StringReader( + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]")); + for (int i = 0; i < 40; i++) { + reader.beginArray(); + } + for (int i = 0; i < 40; i++) { + reader.endArray(); + } + assertEquals(JsonToken.END_DOCUMENT, reader.peek()); + } + + public void testDeeplyNestedObjects() throws IOException { + // Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 40 levels deep + String array = "{\"a\":%s}"; + String json = "true"; + for (int i = 0; i < 40; i++) { + json = String.format(array, json); + } + + JsonReader reader = new JsonReader(new StringReader(json)); + for (int i = 0; i < 40; i++) { + reader.beginObject(); + assertEquals("a", reader.nextName()); + } + assertEquals(true, reader.nextBoolean()); + for (int i = 0; i < 40; i++) { + reader.endObject(); + } + assertEquals(JsonToken.END_DOCUMENT, reader.peek()); + } + + // http://code.google.com/p/google-gson/issues/detail?id=409 + public void testStringEndingInSlash() throws IOException { + JsonReader reader = new JsonReader(new StringReader("/")); + reader.setLenient(true); + assertEquals(JsonToken.END_DOCUMENT, reader.peek()); + } + + public void testStringWithLeadingSlash() throws IOException { + JsonReader reader = new JsonReader(new StringReader("/x")); + reader.setLenient(true); + try { + reader.peek(); + fail(); + } catch (MalformedJsonException expected) { + } + } + + public void testUnterminatedObject() throws IOException { + JsonReader reader = new JsonReader(new StringReader("{\"a\":\"android\"x")); + reader.setLenient(true); + reader.beginObject(); + assertEquals("a", reader.nextName()); + assertEquals("android", reader.nextString()); + try { + reader.peek(); + fail(); + } catch (MalformedJsonException expected) { + } + } + + public void testVeryLongQuotedString() throws IOException { + char[] stringChars = new char[1024 * 16]; + Arrays.fill(stringChars, 'x'); + String string = new String(stringChars); + String json = "[\"" + string + "\"]"; + JsonReader reader = new JsonReader(new StringReader(json)); + reader.beginArray(); + assertEquals(string, reader.nextString()); + reader.endArray(); + } + + public void testVeryLongUnquotedString() throws IOException { + char[] stringChars = new char[1024 * 16]; + Arrays.fill(stringChars, 'x'); + String string = new String(stringChars); + String json = "[" + string + "]"; + JsonReader reader = new JsonReader(new StringReader(json)); + reader.setLenient(true); + reader.beginArray(); + assertEquals(string, reader.nextString()); + reader.endArray(); + } + + public void testVeryLongUnterminatedString() throws IOException { + char[] stringChars = new char[1024 * 16]; + Arrays.fill(stringChars, 'x'); + String string = new String(stringChars); + String json = "[" + string; + JsonReader reader = new JsonReader(new StringReader(json)); + reader.setLenient(true); + reader.beginArray(); + assertEquals(string, reader.nextString()); + try { + reader.peek(); + fail(); + } catch (EOFException expected) { + } + } + + public void testSkipVeryLongUnquotedString() throws IOException { + char[] stringChars = new char[1024 * 16]; + Arrays.fill(stringChars, 'x'); + String string = new String(stringChars); + String json = "[" + string + "]"; + JsonReader reader = new JsonReader(new StringReader(json)); + reader.setLenient(true); + reader.beginArray(); + reader.skipValue(); + reader.endArray(); + } + + public void testStringAsNumberWithTruncatedExponent() throws IOException { + JsonReader reader = new JsonReader(new StringReader("[123e]")); + reader.setLenient(true); + reader.beginArray(); + assertEquals(JsonToken.STRING, reader.peek()); + } + + public void testStringAsNumberWithDigitAndNonDigitExponent() throws IOException { + JsonReader reader = new JsonReader(new StringReader("[123e4b]")); + reader.setLenient(true); + reader.beginArray(); + assertEquals(JsonToken.STRING, reader.peek()); + } + + public void testStringAsNumberWithNonDigitExponent() throws IOException { + JsonReader reader = new JsonReader(new StringReader("[123eb]")); + reader.setLenient(true); + reader.beginArray(); + assertEquals(JsonToken.STRING, reader.peek()); + } private String repeat(char c, int count) { char[] array = new char[count];