Fix some bugs and increase tests for ObjectJsonReader.

https://github.com/square/moshi/issues/89
This commit is contained in:
jwilson
2017-01-21 23:06:16 -05:00
parent bc73b075f5
commit aa9125bb81
7 changed files with 1125 additions and 756 deletions

View File

@@ -16,6 +16,7 @@
package com.squareup.moshi;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -86,16 +87,14 @@ final class ObjectJsonReader extends JsonReader {
// If the iterator isn't empty push its first value onto the stack.
if (iterator.hasNext()) {
stack[stackSize] = iterator.next();
stackSize++;
push(iterator.next());
}
}
@Override public void endArray() throws IOException {
ListIterator<?> peeked = require(ListIterator.class, Token.END_ARRAY);
if (peeked.hasNext()) {
throw new JsonDataException(
"Expected " + Token.END_ARRAY + " but was " + peek() + " at path " + getPath());
throw typeMismatch(peeked, Token.END_ARRAY);
}
remove();
}
@@ -109,16 +108,14 @@ final class ObjectJsonReader extends JsonReader {
// If the iterator isn't empty push its first value onto the stack.
if (iterator.hasNext()) {
stack[stackSize] = iterator.next();
stackSize++;
push(iterator.next());
}
}
@Override public void endObject() throws IOException {
Iterator<?> peeked = require(Iterator.class, Token.END_OBJECT);
if (peeked instanceof ListIterator || peeked.hasNext()) {
throw new JsonDataException(
"Expected " + Token.END_OBJECT + " but was " + peek() + " at path " + getPath());
throw typeMismatch(peeked, Token.END_OBJECT);
}
pathNames[stackSize - 1] = null;
remove();
@@ -148,22 +145,31 @@ final class ObjectJsonReader extends JsonReader {
if (peeked == null) return Token.NULL;
if (peeked == JSON_READER_CLOSED) throw new IllegalStateException("JsonReader is closed");
throw new JsonDataException("Expected a JSON value but was a " + peeked.getClass().getName()
+ " at path " + getPath());
throw typeMismatch(peeked, "a JSON value");
}
@Override public String nextName() throws IOException {
Object peeked = require(Map.Entry.class, Token.NAME);
Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME);
// Swap the Map.Entry for its value on the stack and return its key.
String result = (String) ((Map.Entry<?, ?>) peeked).getKey();
stack[stackSize - 1] = ((Map.Entry<?, ?>) peeked).getValue();
String result = stringKey(peeked);
stack[stackSize - 1] = peeked.getValue();
pathNames[stackSize - 2] = result;
return result;
}
@Override int selectName(Options options) throws IOException {
throw new UnsupportedOperationException();
Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME);
String name = stringKey(peeked);
for (int i = 0, length = options.strings.length; i < length; i++) {
// Swap the Map.Entry for its value on the stack and return its key.
if (options.strings[i].equals(name)) {
stack[stackSize - 1] = peeked.getValue();
pathNames[stackSize - 2] = name;
return i;
}
}
return -1;
}
@Override public String nextString() throws IOException {
@@ -173,7 +179,14 @@ final class ObjectJsonReader extends JsonReader {
}
@Override int selectString(Options options) throws IOException {
throw new UnsupportedOperationException();
String peeked = require(String.class, Token.STRING);
for (int i = 0, length = options.strings.length; i < length; i++) {
if (options.strings[i].equals(peeked)) {
remove();
return i;
}
}
return -1;
}
@Override public boolean nextBoolean() throws IOException {
@@ -189,21 +202,74 @@ final class ObjectJsonReader extends JsonReader {
}
@Override public double nextDouble() throws IOException {
Number peeked = require(Number.class, Token.NUMBER);
Object peeked = require(Object.class, Token.NUMBER);
double result;
if (peeked instanceof Number) {
result = ((Number) peeked).doubleValue();
} else if (peeked instanceof String) {
try {
result = Double.parseDouble((String) peeked);
} catch (NumberFormatException e) {
throw typeMismatch(peeked, Token.NUMBER);
}
} else {
throw typeMismatch(peeked, Token.NUMBER);
}
if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
throw new JsonEncodingException("JSON forbids NaN and infinities: " + result
+ " at path " + getPath());
}
remove();
return peeked.doubleValue(); // TODO(jwilson): precision check?
return result;
}
@Override public long nextLong() throws IOException {
Number peeked = require(Number.class, Token.NUMBER);
Object peeked = require(Object.class, Token.NUMBER);
long result;
if (peeked instanceof Number) {
result = ((Number) peeked).longValue();
} else if (peeked instanceof String) {
try {
result = Long.parseLong((String) peeked);
} catch (NumberFormatException e) {
try {
BigDecimal asDecimal = new BigDecimal((String) peeked);
result = asDecimal.longValueExact();
} catch (NumberFormatException e2) {
throw typeMismatch(peeked, Token.NUMBER);
}
}
} else {
throw typeMismatch(peeked, Token.NUMBER);
}
remove();
return peeked.longValue(); // TODO(jwilson): precision check?
return result;
}
@Override public int nextInt() throws IOException {
Number peeked = require(Number.class, Token.NUMBER);
Object peeked = require(Object.class, Token.NUMBER);
int result;
if (peeked instanceof Number) {
result = ((Number) peeked).intValue();
} else if (peeked instanceof String) {
try {
result = Integer.parseInt((String) peeked);
} catch (NumberFormatException e) {
try {
BigDecimal asDecimal = new BigDecimal((String) peeked);
result = asDecimal.intValueExact();
} catch (NumberFormatException e2) {
throw typeMismatch(peeked, Token.NUMBER);
}
}
} else {
throw typeMismatch(peeked, Token.NUMBER);
}
remove();
return peeked.intValue(); // TODO(jwilson): precision check?
return result;
}
@Override public void skipValue() throws IOException {
@@ -233,11 +299,10 @@ final class ObjectJsonReader extends JsonReader {
}
@Override void promoteNameToValue() throws IOException {
Object peeked = require(Map.Entry.class, Token.NAME);
Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME);
stackSize++;
stack[stackSize - 2] = ((Map.Entry<?, ?>) peeked).getValue();
stack[stackSize - 1] = ((Map.Entry<?, ?>) peeked).getKey();
push(peeked.getKey());
stack[stackSize - 2] = peeked.getValue();
}
@Override public void close() throws IOException {
@@ -247,6 +312,13 @@ final class ObjectJsonReader extends JsonReader {
stackSize = 1;
}
private void push(Object newTop) {
if (stackSize == stack.length) {
throw new JsonDataException("Nesting too deep at " + getPath());
}
stack[stackSize++] = newTop;
}
/**
* Returns the top of the stack which is required to be a {@code type}. Throws if this reader is
* closed, or if the type isn't what was expected.
@@ -263,8 +335,23 @@ final class ObjectJsonReader extends JsonReader {
if (peeked == JSON_READER_CLOSED) {
throw new IllegalStateException("JsonReader is closed");
}
throw new JsonDataException(
"Expected " + expected + " but was " + peek() + " at path " + getPath());
throw typeMismatch(peeked, expected);
}
private String stringKey(Map.Entry<?, ?> entry) {
Object name = entry.getKey();
if (name instanceof String) return (String) name;
throw typeMismatch(name, Token.NAME);
}
private JsonDataException typeMismatch(Object value, Object expected) {
if (value == null) {
throw new JsonDataException(
"Expected " + expected + " but was null at path " + getPath());
} else {
throw new JsonDataException("Expected " + expected + " but was " + value + ", a "
+ value.getClass().getName() + ", at path " + getPath());
}
}
/**
@@ -282,8 +369,7 @@ final class ObjectJsonReader extends JsonReader {
Object parent = stack[stackSize - 1];
if (parent instanceof Iterator && ((Iterator<?>) parent).hasNext()) {
stack[stackSize] = ((Iterator<?>) parent).next();
stackSize++;
push(((Iterator<?>) parent).next());
}
}
}

View File

@@ -19,6 +19,8 @@ import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import okio.Buffer;
import okio.ForwardingSource;
import okio.Okio;
import org.junit.Ignore;
import org.junit.Test;
@@ -32,6 +34,7 @@ import static com.squareup.moshi.JsonReader.Token.NULL;
import static com.squareup.moshi.JsonReader.Token.NUMBER;
import static com.squareup.moshi.JsonReader.Token.STRING;
import static com.squareup.moshi.TestUtil.newReader;
import static com.squareup.moshi.TestUtil.repeat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -51,34 +54,6 @@ public final class BufferedSourceJsonReaderTest {
assertThat(buffer.size()).isEqualTo(0);
}
@Test public void readArray() throws IOException {
JsonReader reader = newReader("[true, true]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
assertThat(reader.nextBoolean()).isTrue();
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readEmptyArray() throws IOException {
JsonReader reader = newReader("[]");
reader.beginArray();
assertThat(reader.hasNext()).isFalse();
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readObject() throws IOException {
JsonReader reader = newReader("{\"a\": \"android\", \"b\": \"banana\"}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
assertThat(reader.nextString()).isEqualTo("android");
assertThat(reader.nextName()).isEqualTo("b");
assertThat(reader.nextString()).isEqualTo("banana");
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readObjectBuffer() throws IOException {
Buffer buffer = new Buffer().writeUtf8("{\"a\": \"android\", \"b\": \"banana\"}");
JsonReader reader = JsonReader.of(buffer);
@@ -93,7 +68,7 @@ public final class BufferedSourceJsonReaderTest {
@Test public void readObjectSource() throws IOException {
Buffer buffer = new Buffer().writeUtf8("{\"a\": \"android\", \"b\": \"banana\"}");
JsonReader reader = JsonReader.of(buffer);
JsonReader reader = JsonReader.of(Okio.buffer(new ForwardingSource(buffer) {}));
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
assertThat(reader.nextString()).isEqualTo("android");
@@ -103,147 +78,6 @@ public final class BufferedSourceJsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readEmptyObject() throws IOException {
JsonReader reader = newReader("{}");
reader.beginObject();
assertThat(reader.hasNext()).isFalse();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipArray() throws IOException {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
assertThat(reader.nextInt()).isEqualTo(123);
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipArrayAfterPeek() throws Exception {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
assertThat(reader.peek()).isEqualTo(BEGIN_ARRAY);
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
assertThat(reader.nextInt()).isEqualTo(123);
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipTopLevelObject() throws Exception {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipObject() throws IOException {
JsonReader reader = newReader(
"{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
reader.skipValue();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipObjectAfterPeek() throws Exception {
String json = "{" + " \"one\": { \"num\": 1 }"
+ ", \"two\": { \"num\": 2 }" + ", \"three\": { \"num\": 3 }" + "}";
JsonReader reader = newReader(json);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("one");
assertThat(reader.peek()).isEqualTo(BEGIN_OBJECT);
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("two");
assertThat(reader.peek()).isEqualTo(BEGIN_OBJECT);
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("three");
reader.skipValue();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipInteger() throws IOException {
JsonReader reader = newReader("{\"a\":123456789,\"b\":-123456789}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
reader.skipValue();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipDouble() throws IOException {
JsonReader reader = newReader("{\"a\":-123.456e-789,\"b\":123456789.0}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
reader.skipValue();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void failOnUnknownFailsOnUnknownObjectValue() throws IOException {
JsonReader reader = newReader("{\"a\": 123}");
reader.setFailOnUnknown(true);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
try {
reader.skipValue();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Cannot skip unexpected NUMBER at $.a");
}
// Confirm that the reader is left in a consistent state after the exception.
reader.setFailOnUnknown(false);
assertThat(reader.nextInt()).isEqualTo(123);
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void failOnUnknownFailsOnUnknownArrayElement() throws IOException {
JsonReader reader = newReader("[\"a\", 123]");
reader.setFailOnUnknown(true);
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("a");
try {
reader.skipValue();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Cannot skip unexpected NUMBER at $[1]");
}
// Confirm that the reader is left in a consistent state after the exception.
reader.setFailOnUnknown(false);
assertThat(reader.nextInt()).isEqualTo(123);
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void helloWorld() throws IOException {
String json = "{\n" +
" \"hello\": true,\n" +
" \"foo\": [\"world\"]\n" +
"}";
JsonReader reader = newReader(json);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("hello");
assertThat(reader.nextBoolean()).isTrue();
assertThat(reader.nextName()).isEqualTo("foo");
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("world");
reader.endArray();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void nullSource() {
try {
JsonReader.of(null);
@@ -252,65 +86,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
@Test public void emptyString() throws Exception {
try {
newReader("").beginArray();
fail();
} catch (EOFException expected) {
}
try {
newReader("").beginObject();
fail();
} catch (EOFException expected) {
}
}
@Test public void characterUnescaping() throws IOException {
String json = "[\"a\","
+ "\"a\\\"\","
+ "\"\\\"\","
+ "\":\","
+ "\",\","
+ "\"\\b\","
+ "\"\\f\","
+ "\"\\n\","
+ "\"\\r\","
+ "\"\\t\","
+ "\" \","
+ "\"\\\\\","
+ "\"{\","
+ "\"}\","
+ "\"[\","
+ "\"]\","
+ "\"\\u0000\","
+ "\"\\u0019\","
+ "\"\\u20AC\""
+ "]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("a");
assertThat(reader.nextString()).isEqualTo("a\"");
assertThat(reader.nextString()).isEqualTo("\"");
assertThat(reader.nextString()).isEqualTo(":");
assertThat(reader.nextString()).isEqualTo(",");
assertThat(reader.nextString()).isEqualTo("\b");
assertThat(reader.nextString()).isEqualTo("\f");
assertThat(reader.nextString()).isEqualTo("\n");
assertThat(reader.nextString()).isEqualTo("\r");
assertThat(reader.nextString()).isEqualTo("\t");
assertThat(reader.nextString()).isEqualTo(" ");
assertThat(reader.nextString()).isEqualTo("\\");
assertThat(reader.nextString()).isEqualTo("{");
assertThat(reader.nextString()).isEqualTo("}");
assertThat(reader.nextString()).isEqualTo("[");
assertThat(reader.nextString()).isEqualTo("]");
assertThat(reader.nextString()).isEqualTo("\0");
assertThat(reader.nextString()).isEqualTo("\u0019");
assertThat(reader.nextString()).isEqualTo("\u20AC");
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void unescapingInvalidCharacters() throws IOException {
String json = "[\"\\u000g\"]";
JsonReader reader = newReader(json);
@@ -344,84 +119,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
@Test public void integersWithFractionalPartSpecified() throws IOException {
JsonReader reader = newReader("[1.0,1.0,1.0]");
reader.beginArray();
assertThat(reader.nextDouble()).isEqualTo(1.0);
assertThat(reader.nextInt()).isEqualTo(1);
assertThat(reader.nextLong()).isEqualTo(1L);
}
@Test public void doubles() throws IOException {
String json = "[-0.0,"
+ "1.0,"
+ "1.7976931348623157E308,"
+ "4.9E-324,"
+ "0.0,"
+ "-0.5,"
+ "2.2250738585072014E-308,"
+ "3.141592653589793,"
+ "2.718281828459045]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextDouble()).isEqualTo(-0.0);
assertThat(reader.nextDouble()).isEqualTo(1.0);
assertThat(reader.nextDouble()).isEqualTo(1.7976931348623157E308);
assertThat(reader.nextDouble()).isEqualTo(4.9E-324);
assertThat(reader.nextDouble()).isEqualTo(0.0);
assertThat(reader.nextDouble()).isEqualTo(-0.5);
assertThat(reader.nextDouble()).isEqualTo(2.2250738585072014E-308);
assertThat(reader.nextDouble()).isEqualTo(3.141592653589793);
assertThat(reader.nextDouble()).isEqualTo(2.718281828459045);
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void strictNonFiniteDoubles() throws IOException {
String json = "[NaN]";
JsonReader reader = newReader(json);
reader.beginArray();
try {
reader.nextDouble();
fail();
} catch (JsonEncodingException expected) {
}
}
@Test public void strictQuotedNonFiniteDoubles() throws IOException {
String json = "[\"NaN\"]";
JsonReader reader = newReader(json);
reader.beginArray();
try {
reader.nextDouble();
fail();
} catch (JsonEncodingException expected) {
assertThat(expected).hasMessageContaining("NaN");
}
}
@Test public void lenientNonFiniteDoubles() throws IOException {
String json = "[NaN, -Infinity, Infinity]";
JsonReader reader = newReader(json);
reader.setLenient(true);
reader.beginArray();
assertThat(Double.isNaN(reader.nextDouble())).isTrue();
assertThat(reader.nextDouble()).isEqualTo(Double.NEGATIVE_INFINITY);
assertThat(reader.nextDouble()).isEqualTo(Double.POSITIVE_INFINITY);
reader.endArray();
}
@Test public void lenientQuotedNonFiniteDoubles() throws IOException {
String json = "[\"NaN\", \"-Infinity\", \"Infinity\"]";
JsonReader reader = newReader(json);
reader.setLenient(true);
reader.beginArray();
assertThat(reader.nextDouble()).isNaN();
assertThat(reader.nextDouble()).isEqualTo(Double.NEGATIVE_INFINITY);
assertThat(reader.nextDouble()).isEqualTo(Double.POSITIVE_INFINITY);
reader.endArray();
}
@Test public void strictNonFiniteDoublesWithSkipValue() throws IOException {
String json = "[NaN]";
JsonReader reader = newReader(json);
@@ -433,39 +130,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
@Test public void longs() throws IOException {
String json = "[0,0,0,"
+ "1,1,1,"
+ "-1,-1,-1,"
+ "-9223372036854775808,"
+ "9223372036854775807]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextLong()).isEqualTo(0L);
assertThat(reader.nextInt()).isEqualTo(0);
assertThat(reader.nextDouble()).isEqualTo(0.0d);
assertThat(reader.nextLong()).isEqualTo(1L);
assertThat(reader.nextInt()).isEqualTo(1);
assertThat(reader.nextDouble()).isEqualTo(1.0d);
assertThat(reader.nextLong()).isEqualTo(-1L);
assertThat(reader.nextInt()).isEqualTo(-1);
assertThat(reader.nextDouble()).isEqualTo(-1.0d);
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextLong()).isEqualTo(Long.MIN_VALUE);
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextLong()).isEqualTo(Long.MAX_VALUE);
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test @Ignore public void numberWithOctalPrefix() throws IOException {
String json = "[01]";
JsonReader reader = newReader(json);
@@ -495,15 +159,6 @@ public final class BufferedSourceJsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void booleans() throws IOException {
JsonReader reader = newReader("[true,false]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
assertThat(reader.nextBoolean()).isFalse();
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void peekingUnquotedStringsPrefixedWithBooleans() throws IOException {
JsonReader reader = newReader("[truey]");
reader.setLenient(true);
@@ -749,122 +404,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
@Test public void nextFailuresDoNotAdvance() throws IOException {
JsonReader reader = newReader("{\"a\":true}");
reader.beginObject();
try {
reader.nextString();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextName()).isEqualTo("a");
try {
reader.nextName();
fail();
} catch (JsonDataException expected) {
}
try {
reader.beginArray();
fail();
} catch (JsonDataException expected) {
}
try {
reader.endArray();
fail();
} catch (JsonDataException expected) {
}
try {
reader.beginObject();
fail();
} catch (JsonDataException expected) {
}
try {
reader.endObject();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextBoolean()).isTrue();
try {
reader.nextString();
fail();
} catch (JsonDataException expected) {
}
try {
reader.nextName();
fail();
} catch (JsonDataException expected) {
}
try {
reader.beginArray();
fail();
} catch (JsonDataException expected) {
}
try {
reader.endArray();
fail();
} catch (JsonDataException expected) {
}
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
reader.close();
}
@Test public void integerMismatchWithDoubleDoesNotAdvance() throws IOException {
JsonReader reader = newReader("[1.5]");
reader.beginArray();
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextDouble()).isEqualTo(1.5d);
reader.endArray();
}
@Test public void integerMismatchWithLongDoesNotAdvance() throws IOException {
JsonReader reader = newReader("[9223372036854775807]");
reader.beginArray();
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextLong()).isEqualTo(9223372036854775807L);
reader.endArray();
}
@Test public void longMismatchWithDoubleDoesNotAdvance() throws IOException {
JsonReader reader = newReader("[1.5]");
reader.beginArray();
try {
reader.nextLong();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextDouble()).isEqualTo(1.5d);
reader.endArray();
}
@Test public void stringNullIsNotNull() throws IOException {
JsonReader reader = newReader("[\"null\"]");
reader.beginArray();
try {
reader.nextNull();
fail();
} catch (JsonDataException expected) {
}
}
@Test public void nullLiteralIsNotAString() throws IOException {
JsonReader reader = newReader("[null]");
reader.beginArray();
try {
reader.nextString();
fail();
} catch (JsonDataException expected) {
}
}
@Test public void strictNameValueSeparator() throws IOException {
JsonReader reader = newReader("{\"a\"=true}");
reader.beginObject();
@@ -1334,38 +873,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
@Test public void topLevelValueTypes() throws IOException {
JsonReader reader1 = newReader("true");
assertThat(reader1.nextBoolean()).isTrue();
assertThat(reader1.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader2 = newReader("false");
assertThat(reader2.nextBoolean()).isFalse();
assertThat(reader2.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader3 = newReader("null");
assertThat(reader3.nextNull()).isNull();
assertThat(reader3.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader4 = newReader("123");
assertThat(reader4.nextInt()).isEqualTo(123);
assertThat(reader4.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader5 = newReader("123.4");
assertThat(reader5.nextDouble()).isEqualTo(123.4);
assertThat(reader5.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader6 = newReader("\"a\"");
assertThat(reader6.nextString()).isEqualTo("a");
assertThat(reader6.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void topLevelValueTypeWithSkipValue() throws IOException {
JsonReader reader = newReader("true");
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test @Ignore public void bomIgnoredAsFirstCharacterOfDocument() throws IOException {
JsonReader reader = newReader("\ufeff[]");
reader.beginArray();
@@ -1497,19 +1004,6 @@ public final class BufferedSourceJsonReaderTest {
reader.endArray();
}
@Test public void deeplyNestedArrays() throws IOException {
JsonReader reader = newReader("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]");
for (int i = 0; i < 31; i++) {
reader.beginArray();
}
assertThat(reader.getPath()).isEqualTo("$[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]"
+ "[0][0][0][0][0][0][0][0][0][0][0][0][0]");
for (int i = 0; i < 31; i++) {
reader.endArray();
}
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void tooDeeplyNestedArrays() throws IOException {
JsonReader reader = newReader(
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]");
@@ -1525,28 +1019,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
@Test public void deeplyNestedObjects() throws IOException {
// Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 31 levels deep.
String array = "{\"a\":%s}";
String json = "true";
for (int i = 0; i < 31; i++) {
json = String.format(array, json);
}
JsonReader reader = newReader(json);
for (int i = 0; i < 31; i++) {
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
}
assertThat(reader.getPath())
.isEqualTo("$.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a");
assertThat(reader.nextBoolean()).isTrue();
for (int i = 0; i < 31; i++) {
reader.endObject();
}
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void tooDeeplyNestedObjects() throws IOException {
// Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 31 levels deep.
String array = "{\"a\":%s}";
@@ -1652,70 +1124,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
@Test public void skipVeryLongUnquotedString() throws IOException {
JsonReader reader = newReader("[" + repeat('x', 8192) + "]");
reader.setLenient(true);
reader.beginArray();
reader.skipValue();
reader.endArray();
}
@Test public void skipTopLevelUnquotedString() throws IOException {
JsonReader reader = newReader(repeat('x', 8192));
reader.setLenient(true);
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipVeryLongQuotedString() throws IOException {
JsonReader reader = newReader("[\"" + repeat('x', 8192) + "\"]");
reader.beginArray();
reader.skipValue();
reader.endArray();
}
@Test public void skipTopLevelQuotedString() throws IOException {
JsonReader reader = newReader("\"" + repeat('x', 8192) + "\"");
reader.setLenient(true);
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void stringAsNumberWithTruncatedExponent() throws IOException {
JsonReader reader = newReader("[123e]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void stringAsNumberWithDigitAndNonDigitExponent() throws IOException {
JsonReader reader = newReader("[123e4b]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void stringAsNumberWithNonDigitExponent() throws IOException {
JsonReader reader = newReader("[123eb]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void emptyStringName() throws IOException {
JsonReader reader = newReader("{\"\":true}");
reader.setLenient(true);
assertThat(reader.peek()).isEqualTo(BEGIN_OBJECT);
reader.beginObject();
assertThat(reader.peek()).isEqualTo(NAME);
assertThat(reader.nextName()).isEqualTo("");
assertThat(reader.peek()).isEqualTo(JsonReader.Token.BOOLEAN);
assertThat(reader.nextBoolean()).isTrue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_OBJECT);
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void strictExtraCommasInMaps() throws IOException {
JsonReader reader = newReader("{\"a\":\"b\",}");
reader.beginObject();
@@ -1741,12 +1149,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
private String repeat(char c, int count) {
char[] array = new char[count];
Arrays.fill(array, c);
return new String(array);
}
@Test public void malformedDocuments() throws IOException {
assertDocument("{]", BEGIN_OBJECT, JsonEncodingException.class);
assertDocument("{,", BEGIN_OBJECT, JsonEncodingException.class);
@@ -1804,12 +1206,6 @@ public final class BufferedSourceJsonReaderTest {
}
}
@Test public void validEscapes() throws IOException {
JsonReader reader = newReader("[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]");
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("\"\\/\b\f\n\r\t");
}
@Test public void invalidEscape() throws IOException {
JsonReader reader = newReader("[\"str\\ing\"]");
reader.beginArray();
@@ -1828,68 +1224,6 @@ public final class BufferedSourceJsonReaderTest {
assertThat(reader.nextString()).isEqualTo("string");
}
@Test public void selectName() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("{\"a\": 5, \"b\": 5, \"c\": 5, \"d\": 5}");
reader.beginObject();
assertEquals("$.", reader.getPath());
assertEquals(0, reader.selectName(abc));
assertEquals("$.a", reader.getPath());
assertEquals(5, reader.nextInt());
assertEquals("$.a", reader.getPath());
assertEquals(1, reader.selectName(abc));
assertEquals("$.b", reader.getPath());
assertEquals(5, reader.nextInt());
assertEquals("$.b", reader.getPath());
assertEquals(2, reader.selectName(abc));
assertEquals("$.c", reader.getPath());
assertEquals(5, reader.nextInt());
assertEquals("$.c", reader.getPath());
// A missed selectName() doesn't advance anything, not even the path.
assertEquals(-1, reader.selectName(abc));
assertEquals("$.c", reader.getPath());
assertEquals(JsonReader.Token.NAME, reader.peek());
assertEquals("d", reader.nextName());
assertEquals("$.d", reader.getPath());
assertEquals(5, reader.nextInt());
assertEquals("$.d", reader.getPath());
reader.endObject();
}
@Test public void selectString() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("[\"a\", \"b\", \"c\", \"d\"]");
reader.beginArray();
assertEquals("$[0]", reader.getPath());
assertEquals(0, reader.selectString(abc));
assertEquals("$[1]", reader.getPath());
assertEquals(1, reader.selectString(abc));
assertEquals("$[2]", reader.getPath());
assertEquals(2, reader.selectString(abc));
assertEquals("$[3]", reader.getPath());
// A missed selectName() doesn't advance anything, not even the path.
assertEquals(-1, reader.selectString(abc));
assertEquals("$[3]", reader.getPath());
assertEquals(JsonReader.Token.STRING, reader.peek());
assertEquals("d", reader.nextString());
assertEquals("$[4]", reader.getPath());
reader.endArray();
}
/** Select doesn't match unquoted strings. */
@Test public void selectStringUnquoted() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
@@ -1925,24 +1259,6 @@ public final class BufferedSourceJsonReaderTest {
reader.endArray();
}
/** Select does match necessarily escaping. The decoded value is used in the path. */
@Test public void selectNecessaryEscaping() throws IOException {
JsonReader.Options options = JsonReader.Options.of("\n", "\u0000", "\"");
JsonReader reader = newReader("{\"\\n\": 5,\"\\u0000\": 5, \"\\\"\": 5}");
reader.beginObject();
assertEquals(0, reader.selectName(options));
assertEquals(5, reader.nextInt());
assertEquals("$.\n", reader.getPath());
assertEquals(1, reader.selectName(options));
assertEquals(5, reader.nextInt());
assertEquals("$.\u0000", reader.getPath());
assertEquals(2, reader.selectName(options));
assertEquals(5, reader.nextInt());
assertEquals("$.\"", reader.getPath());
reader.endObject();
}
private void assertDocument(String document, Object... expectations) throws IOException {
JsonReader reader = newReader(document);
reader.setLenient(true);

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2017 Square, 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 com.squareup.moshi;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import okio.Buffer;
abstract class JsonReaderFactory {
public static final JsonReaderFactory BUFFERED_SOURCE = new JsonReaderFactory() {
@Override public JsonReader newReader(String json) {
Buffer buffer = new Buffer().writeUtf8(json);
return JsonReader.of(buffer);
}
};
public static final JsonReaderFactory JSON_OBJECT = new JsonReaderFactory() {
@Override public JsonReader newReader(String json) throws IOException {
Moshi moshi = new Moshi.Builder().build();
Object object = moshi.adapter(Object.class).lenient().fromJson(json);
return new ObjectJsonReader(object);
}
};
static List<Object[]> factories() {
return Arrays.asList(
new Object[] { BUFFERED_SOURCE },
new Object[] { JSON_OBJECT });
}
abstract JsonReader newReader(String json) throws IOException;
}

View File

@@ -16,9 +16,7 @@
package com.squareup.moshi;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import okio.Buffer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -30,34 +28,13 @@ import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public final class JsonReaderPathTest {
interface Factory {
Factory BUFFERED_SOURCE = new Factory() {
@Override public JsonReader newReader(String json) {
Buffer buffer = new Buffer().writeUtf8(json);
return JsonReader.of(buffer);
}
};
Factory JSON_OBJECT = new Factory() {
@Override public JsonReader newReader(String json) throws IOException {
Moshi moshi = new Moshi.Builder().build();
Object object = moshi.adapter(Object.class).fromJson(json);
return new ObjectJsonReader(object);
}
};
JsonReader newReader(String json) throws IOException;
}
@Parameter public JsonReaderFactory factory;
@Parameters(name = "{0}")
public static List<Object[]> parameters() {
return Arrays.asList(
new Object[] { Factory.BUFFERED_SOURCE},
new Object[] { Factory.JSON_OBJECT});
return JsonReaderFactory.factories();
}
@Parameter public Factory factory;
@Test public void path() throws IOException {
JsonReader reader = factory.newReader("{\"a\":[2,true,false,null,\"b\",{\"c\":\"d\"},[3]]}");
assertThat(reader.getPath()).isEqualTo("$");
@@ -208,7 +185,7 @@ public final class JsonReaderPathTest {
}
@Test public void multipleTopLevelValuesInOneDocument() throws IOException {
assumeTrue(factory != Factory.JSON_OBJECT);
assumeTrue(factory != JsonReaderFactory.JSON_OBJECT);
JsonReader reader = factory.newReader("[][]");
reader.setLenient(true);

View File

@@ -0,0 +1,800 @@
/*
* Copyright (C) 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 com.squareup.moshi;
import java.io.EOFException;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static com.squareup.moshi.JsonReader.Token.BEGIN_ARRAY;
import static com.squareup.moshi.JsonReader.Token.BEGIN_OBJECT;
import static com.squareup.moshi.JsonReader.Token.NAME;
import static com.squareup.moshi.JsonReader.Token.STRING;
import static com.squareup.moshi.TestUtil.repeat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public final class JsonReaderTest {
@Parameter public JsonReaderFactory factory;
@Parameters(name = "{0}")
public static List<Object[]> parameters() {
return JsonReaderFactory.factories();
}
JsonReader newReader(String json) throws IOException {
return factory.newReader(json);
}
@Test public void readArray() throws IOException {
JsonReader reader = newReader("[true, true]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
assertThat(reader.nextBoolean()).isTrue();
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readEmptyArray() throws IOException {
JsonReader reader = newReader("[]");
reader.beginArray();
assertThat(reader.hasNext()).isFalse();
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readObject() throws IOException {
JsonReader reader = newReader("{\"a\": \"android\", \"b\": \"banana\"}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
assertThat(reader.nextString()).isEqualTo("android");
assertThat(reader.nextName()).isEqualTo("b");
assertThat(reader.nextString()).isEqualTo("banana");
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readEmptyObject() throws IOException {
JsonReader reader = newReader("{}");
reader.beginObject();
assertThat(reader.hasNext()).isFalse();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipArray() throws IOException {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
assertThat(reader.nextInt()).isEqualTo(123);
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipArrayAfterPeek() throws Exception {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
assertThat(reader.peek()).isEqualTo(BEGIN_ARRAY);
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
assertThat(reader.nextInt()).isEqualTo(123);
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipTopLevelObject() throws Exception {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipObject() throws IOException {
JsonReader reader = newReader(
"{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
reader.skipValue();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipObjectAfterPeek() throws Exception {
String json = "{" + " \"one\": { \"num\": 1 }"
+ ", \"two\": { \"num\": 2 }" + ", \"three\": { \"num\": 3 }" + "}";
JsonReader reader = newReader(json);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("one");
assertThat(reader.peek()).isEqualTo(BEGIN_OBJECT);
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("two");
assertThat(reader.peek()).isEqualTo(BEGIN_OBJECT);
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("three");
reader.skipValue();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipInteger() throws IOException {
JsonReader reader = newReader("{\"a\":123456789,\"b\":-123456789}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
reader.skipValue();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipDouble() throws IOException {
JsonReader reader = newReader("{\"a\":-123.456e-789,\"b\":123456789.0}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
assertThat(reader.nextName()).isEqualTo("b");
reader.skipValue();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void failOnUnknownFailsOnUnknownObjectValue() throws IOException {
JsonReader reader = newReader("{\"a\": 123}");
reader.setFailOnUnknown(true);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
try {
reader.skipValue();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Cannot skip unexpected NUMBER at $.a");
}
// Confirm that the reader is left in a consistent state after the exception.
reader.setFailOnUnknown(false);
assertThat(reader.nextInt()).isEqualTo(123);
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void failOnUnknownFailsOnUnknownArrayElement() throws IOException {
JsonReader reader = newReader("[\"a\", 123]");
reader.setFailOnUnknown(true);
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("a");
try {
reader.skipValue();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Cannot skip unexpected NUMBER at $[1]");
}
// Confirm that the reader is left in a consistent state after the exception.
reader.setFailOnUnknown(false);
assertThat(reader.nextInt()).isEqualTo(123);
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void helloWorld() throws IOException {
String json = "{\n" +
" \"hello\": true,\n" +
" \"foo\": [\"world\"]\n" +
"}";
JsonReader reader = newReader(json);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("hello");
assertThat(reader.nextBoolean()).isTrue();
assertThat(reader.nextName()).isEqualTo("foo");
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("world");
reader.endArray();
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void emptyString() throws Exception {
try {
newReader("").beginArray();
fail();
} catch (EOFException expected) {
}
try {
newReader("").beginObject();
fail();
} catch (EOFException expected) {
}
}
@Test public void characterUnescaping() throws IOException {
String json = "[\"a\","
+ "\"a\\\"\","
+ "\"\\\"\","
+ "\":\","
+ "\",\","
+ "\"\\b\","
+ "\"\\f\","
+ "\"\\n\","
+ "\"\\r\","
+ "\"\\t\","
+ "\" \","
+ "\"\\\\\","
+ "\"{\","
+ "\"}\","
+ "\"[\","
+ "\"]\","
+ "\"\\u0000\","
+ "\"\\u0019\","
+ "\"\\u20AC\""
+ "]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("a");
assertThat(reader.nextString()).isEqualTo("a\"");
assertThat(reader.nextString()).isEqualTo("\"");
assertThat(reader.nextString()).isEqualTo(":");
assertThat(reader.nextString()).isEqualTo(",");
assertThat(reader.nextString()).isEqualTo("\b");
assertThat(reader.nextString()).isEqualTo("\f");
assertThat(reader.nextString()).isEqualTo("\n");
assertThat(reader.nextString()).isEqualTo("\r");
assertThat(reader.nextString()).isEqualTo("\t");
assertThat(reader.nextString()).isEqualTo(" ");
assertThat(reader.nextString()).isEqualTo("\\");
assertThat(reader.nextString()).isEqualTo("{");
assertThat(reader.nextString()).isEqualTo("}");
assertThat(reader.nextString()).isEqualTo("[");
assertThat(reader.nextString()).isEqualTo("]");
assertThat(reader.nextString()).isEqualTo("\0");
assertThat(reader.nextString()).isEqualTo("\u0019");
assertThat(reader.nextString()).isEqualTo("\u20AC");
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void integersWithFractionalPartSpecified() throws IOException {
JsonReader reader = newReader("[1.0,1.0,1.0]");
reader.beginArray();
assertThat(reader.nextDouble()).isEqualTo(1.0);
assertThat(reader.nextInt()).isEqualTo(1);
assertThat(reader.nextLong()).isEqualTo(1L);
}
@Test public void doubles() throws IOException {
String json = "[-0.0,"
+ "1.0,"
+ "1.7976931348623157E308,"
+ "4.9E-324,"
+ "0.0,"
+ "-0.5,"
+ "2.2250738585072014E-308,"
+ "3.141592653589793,"
+ "2.718281828459045]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextDouble()).isEqualTo(-0.0);
assertThat(reader.nextDouble()).isEqualTo(1.0);
assertThat(reader.nextDouble()).isEqualTo(1.7976931348623157E308);
assertThat(reader.nextDouble()).isEqualTo(4.9E-324);
assertThat(reader.nextDouble()).isEqualTo(0.0);
assertThat(reader.nextDouble()).isEqualTo(-0.5);
assertThat(reader.nextDouble()).isEqualTo(2.2250738585072014E-308);
assertThat(reader.nextDouble()).isEqualTo(3.141592653589793);
assertThat(reader.nextDouble()).isEqualTo(2.718281828459045);
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void strictNonFiniteDoubles() throws IOException {
String json = "[NaN]";
JsonReader reader = newReader(json);
reader.beginArray();
try {
reader.nextDouble();
fail();
} catch (JsonEncodingException expected) {
}
}
@Test public void strictQuotedNonFiniteDoubles() throws IOException {
String json = "[\"NaN\"]";
JsonReader reader = newReader(json);
reader.beginArray();
try {
reader.nextDouble();
fail();
} catch (JsonEncodingException expected) {
assertThat(expected).hasMessageContaining("NaN");
}
}
@Test public void lenientNonFiniteDoubles() throws IOException {
String json = "[NaN, -Infinity, Infinity]";
JsonReader reader = newReader(json);
reader.setLenient(true);
reader.beginArray();
assertThat(Double.isNaN(reader.nextDouble())).isTrue();
assertThat(reader.nextDouble()).isEqualTo(Double.NEGATIVE_INFINITY);
assertThat(reader.nextDouble()).isEqualTo(Double.POSITIVE_INFINITY);
reader.endArray();
}
@Test public void lenientQuotedNonFiniteDoubles() throws IOException {
String json = "[\"NaN\", \"-Infinity\", \"Infinity\"]";
JsonReader reader = newReader(json);
reader.setLenient(true);
reader.beginArray();
assertThat(reader.nextDouble()).isNaN();
assertThat(reader.nextDouble()).isEqualTo(Double.NEGATIVE_INFINITY);
assertThat(reader.nextDouble()).isEqualTo(Double.POSITIVE_INFINITY);
reader.endArray();
}
@Test public void longs() throws IOException {
assumeTrue(factory != JsonReaderFactory.JSON_OBJECT); // TODO(jwilson): fix precision checks.
String json = "[0,0,0,"
+ "1,1,1,"
+ "-1,-1,-1,"
+ "-9223372036854775808,"
+ "9223372036854775807]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextLong()).isEqualTo(0L);
assertThat(reader.nextInt()).isEqualTo(0);
assertThat(reader.nextDouble()).isEqualTo(0.0d);
assertThat(reader.nextLong()).isEqualTo(1L);
assertThat(reader.nextInt()).isEqualTo(1);
assertThat(reader.nextDouble()).isEqualTo(1.0d);
assertThat(reader.nextLong()).isEqualTo(-1L);
assertThat(reader.nextInt()).isEqualTo(-1);
assertThat(reader.nextDouble()).isEqualTo(-1.0d);
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextLong()).isEqualTo(Long.MIN_VALUE);
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextLong()).isEqualTo(Long.MAX_VALUE);
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void booleans() throws IOException {
JsonReader reader = newReader("[true,false]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
assertThat(reader.nextBoolean()).isFalse();
reader.endArray();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void nextFailuresDoNotAdvance() throws IOException {
JsonReader reader = newReader("{\"a\":true}");
reader.beginObject();
try {
reader.nextString();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextName()).isEqualTo("a");
try {
reader.nextName();
fail();
} catch (JsonDataException expected) {
}
try {
reader.beginArray();
fail();
} catch (JsonDataException expected) {
}
try {
reader.endArray();
fail();
} catch (JsonDataException expected) {
}
try {
reader.beginObject();
fail();
} catch (JsonDataException expected) {
}
try {
reader.endObject();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextBoolean()).isTrue();
try {
reader.nextString();
fail();
} catch (JsonDataException expected) {
}
try {
reader.nextName();
fail();
} catch (JsonDataException expected) {
}
try {
reader.beginArray();
fail();
} catch (JsonDataException expected) {
}
try {
reader.endArray();
fail();
} catch (JsonDataException expected) {
}
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
reader.close();
}
@Test public void integerMismatchWithDoubleDoesNotAdvance() throws IOException {
assumeTrue(factory != JsonReaderFactory.JSON_OBJECT); // TODO(jwilson): fix precision checks.
JsonReader reader = newReader("[1.5]");
reader.beginArray();
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextDouble()).isEqualTo(1.5d);
reader.endArray();
}
@Test public void integerMismatchWithLongDoesNotAdvance() throws IOException {
assumeTrue(factory != JsonReaderFactory.JSON_OBJECT); // TODO(jwilson): fix precision checks.
JsonReader reader = newReader("[9223372036854775807]");
reader.beginArray();
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextLong()).isEqualTo(9223372036854775807L);
reader.endArray();
}
@Test public void longMismatchWithDoubleDoesNotAdvance() throws IOException {
assumeTrue(factory != JsonReaderFactory.JSON_OBJECT); // TODO(jwilson): fix precision checks.
JsonReader reader = newReader("[1.5]");
reader.beginArray();
try {
reader.nextLong();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextDouble()).isEqualTo(1.5d);
reader.endArray();
}
@Test public void stringNullIsNotNull() throws IOException {
JsonReader reader = newReader("[\"null\"]");
reader.beginArray();
try {
reader.nextNull();
fail();
} catch (JsonDataException expected) {
}
}
@Test public void nullLiteralIsNotAString() throws IOException {
JsonReader reader = newReader("[null]");
reader.beginArray();
try {
reader.nextString();
fail();
} catch (JsonDataException expected) {
}
}
@Test public void topLevelValueTypes() throws IOException {
JsonReader reader1 = newReader("true");
assertThat(reader1.nextBoolean()).isTrue();
assertThat(reader1.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader2 = newReader("false");
assertThat(reader2.nextBoolean()).isFalse();
assertThat(reader2.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader3 = newReader("null");
assertThat(reader3.nextNull()).isNull();
assertThat(reader3.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader4 = newReader("123");
assertThat(reader4.nextInt()).isEqualTo(123);
assertThat(reader4.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader5 = newReader("123.4");
assertThat(reader5.nextDouble()).isEqualTo(123.4);
assertThat(reader5.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
JsonReader reader6 = newReader("\"a\"");
assertThat(reader6.nextString()).isEqualTo("a");
assertThat(reader6.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void topLevelValueTypeWithSkipValue() throws IOException {
JsonReader reader = newReader("true");
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void deeplyNestedArrays() throws IOException {
JsonReader reader = newReader("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]");
for (int i = 0; i < 31; i++) {
reader.beginArray();
}
assertThat(reader.getPath()).isEqualTo("$[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]"
+ "[0][0][0][0][0][0][0][0][0][0][0][0][0]");
for (int i = 0; i < 31; i++) {
reader.endArray();
}
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void deeplyNestedObjects() throws IOException {
// Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 31 levels deep.
String array = "{\"a\":%s}";
String json = "true";
for (int i = 0; i < 31; i++) {
json = String.format(array, json);
}
JsonReader reader = newReader(json);
for (int i = 0; i < 31; i++) {
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
}
assertThat(reader.getPath())
.isEqualTo("$.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a");
assertThat(reader.nextBoolean()).isTrue();
for (int i = 0; i < 31; i++) {
reader.endObject();
}
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipVeryLongUnquotedString() throws IOException {
JsonReader reader = newReader("[" + repeat('x', 8192) + "]");
reader.setLenient(true);
reader.beginArray();
reader.skipValue();
reader.endArray();
}
@Test public void skipTopLevelUnquotedString() throws IOException {
JsonReader reader = newReader(repeat('x', 8192));
reader.setLenient(true);
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipVeryLongQuotedString() throws IOException {
JsonReader reader = newReader("[\"" + repeat('x', 8192) + "\"]");
reader.beginArray();
reader.skipValue();
reader.endArray();
}
@Test public void skipTopLevelQuotedString() throws IOException {
JsonReader reader = newReader("\"" + repeat('x', 8192) + "\"");
reader.setLenient(true);
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void stringAsNumberWithTruncatedExponent() throws IOException {
JsonReader reader = newReader("[123e]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void stringAsNumberWithDigitAndNonDigitExponent() throws IOException {
JsonReader reader = newReader("[123e4b]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void stringAsNumberWithNonDigitExponent() throws IOException {
JsonReader reader = newReader("[123eb]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void emptyStringName() throws IOException {
JsonReader reader = newReader("{\"\":true}");
reader.setLenient(true);
assertThat(reader.peek()).isEqualTo(BEGIN_OBJECT);
reader.beginObject();
assertThat(reader.peek()).isEqualTo(NAME);
assertThat(reader.nextName()).isEqualTo("");
assertThat(reader.peek()).isEqualTo(JsonReader.Token.BOOLEAN);
assertThat(reader.nextBoolean()).isTrue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_OBJECT);
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void validEscapes() throws IOException {
JsonReader reader = newReader("[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]");
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("\"\\/\b\f\n\r\t");
}
@Test public void selectName() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("{\"a\": 5, \"b\": 5, \"c\": 5, \"d\": 5}");
reader.beginObject();
assertEquals("$.", reader.getPath());
assertEquals(0, reader.selectName(abc));
assertEquals("$.a", reader.getPath());
assertEquals(5, reader.nextInt());
assertEquals("$.a", reader.getPath());
assertEquals(1, reader.selectName(abc));
assertEquals("$.b", reader.getPath());
assertEquals(5, reader.nextInt());
assertEquals("$.b", reader.getPath());
assertEquals(2, reader.selectName(abc));
assertEquals("$.c", reader.getPath());
assertEquals(5, reader.nextInt());
assertEquals("$.c", reader.getPath());
// A missed selectName() doesn't advance anything, not even the path.
assertEquals(-1, reader.selectName(abc));
assertEquals("$.c", reader.getPath());
assertEquals(JsonReader.Token.NAME, reader.peek());
assertEquals("d", reader.nextName());
assertEquals("$.d", reader.getPath());
assertEquals(5, reader.nextInt());
assertEquals("$.d", reader.getPath());
reader.endObject();
}
@Test public void selectString() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("[\"a\", \"b\", \"c\", \"d\"]");
reader.beginArray();
assertEquals("$[0]", reader.getPath());
assertEquals(0, reader.selectString(abc));
assertEquals("$[1]", reader.getPath());
assertEquals(1, reader.selectString(abc));
assertEquals("$[2]", reader.getPath());
assertEquals(2, reader.selectString(abc));
assertEquals("$[3]", reader.getPath());
// A missed selectName() doesn't advance anything, not even the path.
assertEquals(-1, reader.selectString(abc));
assertEquals("$[3]", reader.getPath());
assertEquals(JsonReader.Token.STRING, reader.peek());
assertEquals("d", reader.nextString());
assertEquals("$[4]", reader.getPath());
reader.endArray();
}
/** Select does match necessarily escaping. The decoded value is used in the path. */
@Test public void selectNecessaryEscaping() throws IOException {
JsonReader.Options options = JsonReader.Options.of("\n", "\u0000", "\"");
JsonReader reader = newReader("{\"\\n\": 5,\"\\u0000\": 5, \"\\\"\": 5}");
reader.beginObject();
assertEquals(0, reader.selectName(options));
assertEquals(5, reader.nextInt());
assertEquals("$.\n", reader.getPath());
assertEquals(1, reader.selectName(options));
assertEquals(5, reader.nextInt());
assertEquals("$.\u0000", reader.getPath());
assertEquals(2, reader.selectName(options));
assertEquals(5, reader.nextInt());
assertEquals("$.\"", reader.getPath());
reader.endObject();
}
@Test public void stringToNumberCoersion() throws Exception {
JsonReader reader = newReader("[\"0\", \"9223372036854775807\", \"1.5\"]");
reader.beginArray();
assertThat(reader.nextInt()).isEqualTo(0);
assertThat(reader.nextLong()).isEqualTo(9223372036854775807L);
assertThat(reader.nextDouble()).isEqualTo(1.5d);
reader.endArray();
}
@Test public void unnecessaryPrecisionNumberCoersion() throws Exception {
JsonReader reader = newReader("[\"0.0\", \"9223372036854775807.0\"]");
reader.beginArray();
assertThat(reader.nextInt()).isEqualTo(0);
assertThat(reader.nextLong()).isEqualTo(9223372036854775807L);
reader.endArray();
}
@Test public void nanInfinityDoubleCoersion() throws Exception {
JsonReader reader = newReader("[\"NaN\", \"Infinity\", \"-Infinity\"]");
reader.beginArray();
reader.setLenient(true);
assertThat(reader.nextDouble()).isNaN();
assertThat(reader.nextDouble()).isEqualTo(Double.POSITIVE_INFINITY);
assertThat(reader.nextDouble()).isEqualTo(Double.NEGATIVE_INFINITY);
reader.endArray();
}
@Test public void intMismatchWithStringDoesNotAdvance() throws Exception {
JsonReader reader = newReader("[\"a\"]");
reader.beginArray();
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextString()).isEqualTo("a");
reader.endArray();
}
@Test public void longMismatchWithStringDoesNotAdvance() throws Exception {
JsonReader reader = newReader("[\"a\"]");
reader.beginArray();
try {
reader.nextLong();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextString()).isEqualTo("a");
reader.endArray();
}
@Test public void doubleMismatchWithStringDoesNotAdvance() throws Exception {
JsonReader reader = newReader("[\"a\"]");
reader.beginArray();
try {
reader.nextDouble();
fail();
} catch (JsonDataException expected) {
}
assertThat(reader.nextString()).isEqualTo("a");
reader.endArray();
}
}

View File

@@ -15,6 +15,7 @@
*/
package com.squareup.moshi;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -22,6 +23,8 @@ import java.util.List;
import java.util.Map;
import org.junit.Test;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
@@ -106,8 +109,7 @@ public final class ObjectJsonReaderTest {
@Test public void nesting() throws Exception {
List<Map<String, List<Map<String, Double>>>> root
= Collections.singletonList(Collections.singletonMap(
"a", Collections.singletonList(Collections.singletonMap("b", 1.5d))));
= singletonList(singletonMap("a", singletonList(singletonMap("b", 1.5d))));
JsonReader reader = new ObjectJsonReader(root);
assertThat(reader.hasNext()).isTrue();
@@ -158,7 +160,7 @@ public final class ObjectJsonReaderTest {
}
@Test public void promoteNameToValue() throws Exception {
Map<String, String> root = Collections.singletonMap("a", "b");
Map<String, String> root = singletonMap("a", "b");
JsonReader reader = new ObjectJsonReader(root);
reader.beginObject();
@@ -174,31 +176,32 @@ public final class ObjectJsonReaderTest {
}
@Test public void endArrayTooEarly() throws Exception {
JsonReader reader = new ObjectJsonReader(Collections.singletonList("s"));
JsonReader reader = new ObjectJsonReader(singletonList("s"));
reader.beginArray();
try {
reader.endArray();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Expected END_ARRAY but was STRING at path $[0]");
assertThat(expected).hasMessage(
"Expected END_ARRAY but was s, a java.lang.String, at path $[0]");
}
}
@Test public void endObjectTooEarly() throws Exception {
JsonReader reader = new ObjectJsonReader(Collections.singletonMap("a", "b"));
JsonReader reader = new ObjectJsonReader(singletonMap("a", "b"));
reader.beginObject();
try {
reader.endObject();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Expected END_OBJECT but was NAME at path $.");
assertThat(expected).hasMessageStartingWith("Expected END_OBJECT but was a=b");
}
}
@Test public void unsupportedType() throws Exception {
JsonReader reader = new ObjectJsonReader(Collections.singletonList(new StringBuilder("x")));
JsonReader reader = new ObjectJsonReader(singletonList(new StringBuilder("x")));
reader.beginArray();
try {
@@ -206,12 +209,109 @@ public final class ObjectJsonReaderTest {
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected a JSON value but was a java.lang.StringBuilder at path $[0]");
"Expected a JSON value but was x, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unsupportedKeyType() throws Exception {
JsonReader reader = new ObjectJsonReader(singletonMap(new StringBuilder("x"), "y"));
reader.beginObject();
try {
reader.nextName();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NAME but was x, a java.lang.StringBuilder, at path $.");
}
}
@Test public void nullKey() throws Exception {
JsonReader reader = new ObjectJsonReader(singletonMap(null, "y"));
reader.beginObject();
try {
reader.nextName();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Expected NAME but was null at path $.");
}
}
@Test public void unexpectedIntType() throws Exception {
JsonReader reader = new ObjectJsonReader(singletonList(new StringBuilder("1")));
reader.beginArray();
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedLongType() throws Exception {
JsonReader reader = new ObjectJsonReader(singletonList(new StringBuilder("1")));
reader.beginArray();
try {
reader.nextLong();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedDoubleType() throws Exception {
JsonReader reader = new ObjectJsonReader(singletonList(new StringBuilder("1")));
reader.beginArray();
try {
reader.nextDouble();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedStringType() throws Exception {
JsonReader reader = new ObjectJsonReader(singletonList(new StringBuilder("s")));
reader.beginArray();
try {
reader.nextString();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected STRING but was s, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedBooleanType() throws Exception {
JsonReader reader = new ObjectJsonReader(singletonList(new StringBuilder("true")));
reader.beginArray();
try {
reader.nextBoolean();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected BOOLEAN but was true, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedNullType() throws Exception {
JsonReader reader = new ObjectJsonReader(singletonList(new StringBuilder("null")));
reader.beginArray();
try {
reader.nextNull();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NULL but was null, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void skipRoot() throws Exception {
JsonReader reader = new ObjectJsonReader(Collections.singletonList(new StringBuilder("x")));
JsonReader reader = new ObjectJsonReader(singletonList(new StringBuilder("x")));
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@@ -292,7 +392,7 @@ public final class ObjectJsonReaderTest {
}
@Test public void failOnUnknown() throws Exception {
JsonReader reader = new ObjectJsonReader(Collections.singletonList("a"));
JsonReader reader = new ObjectJsonReader(singletonList("a"));
reader.setFailOnUnknown(true);
reader.beginArray();
@@ -306,7 +406,7 @@ public final class ObjectJsonReaderTest {
@Test public void close() throws Exception {
try {
JsonReader reader = new ObjectJsonReader(Collections.singletonList("a"));
JsonReader reader = new ObjectJsonReader(singletonList("a"));
reader.beginArray();
reader.close();
reader.nextString();
@@ -315,11 +415,48 @@ public final class ObjectJsonReaderTest {
}
try {
JsonReader reader = new ObjectJsonReader(Collections.singletonList("a"));
JsonReader reader = new ObjectJsonReader(singletonList("a"));
reader.close();
reader.beginArray();
fail();
} catch (IllegalStateException expected) {
}
}
@Test public void tooDeeplyNestedArrays() throws IOException {
Object root = Collections.emptyList();
for (int i = 0; i < 32; i++) {
root = singletonList(root);
}
JsonReader reader = new ObjectJsonReader(root);
for (int i = 0; i < 31; i++) {
reader.beginArray();
}
try {
reader.beginArray();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Nesting too deep at $[0][0][0][0][0][0][0][0][0][0][0][0][0]"
+ "[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]");
}
}
@Test public void tooDeeplyNestedObjects() throws IOException {
Object root = Boolean.TRUE;
for (int i = 0; i < 32; i++) {
root = singletonMap("a", root);
}
JsonReader reader = new ObjectJsonReader(root);
for (int i = 0; i < 31; i++) {
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
}
try {
reader.beginObject();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Nesting too deep at $.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.");
}
}
}

View File

@@ -15,6 +15,7 @@
*/
package com.squareup.moshi;
import java.util.Arrays;
import okio.Buffer;
final class TestUtil {
@@ -23,6 +24,12 @@ final class TestUtil {
return JsonReader.of(buffer);
}
static String repeat(char c, int count) {
char[] array = new char[count];
Arrays.fill(array, c);
return new String(array);
}
private TestUtil() {
throw new AssertionError("No instances.");
}