/*******************************************************************************
* Copyright (c) 2013 EclipseSource.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ralf Sternberg - initial implementation and API
******************************************************************************/
package org.eclipse.rap.json;
import static org.eclipse.rap.json.TestUtil.assertException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.io.StringReader;
import org.hamcrest.core.StringStartsWith;
import org.junit.Test;
public class JsonParser_Test {
@Test
public void parse_rejectsEmptyString() {
assertParseException( 0, "Unexpected end of input", "" );
}
@Test
public void parse_rejectsEmptyReader() {
ParseException exception = assertException( ParseException.class, new Runnable() {
public void run() {
try {
new JsonParser( new StringReader( "" ) ).parse();
} catch( IOException exception ) {
throw new RuntimeException( exception );
}
}
} );
assertEquals( 0, exception.getOffset() );
assertThat( exception.getMessage(), StringStartsWith.startsWith( "Unexpected end of input at" ) );
}
@Test
public void parse_acceptsArrays() {
assertEquals( new JsonArray(), parse( "[]" ) );
}
@Test
public void parse_acceptsObjects() {
assertEquals( new JsonObject(), parse( "{}" ) );
}
@Test
public void parse_acceptsStrings() {
assertEquals( new JsonString( "" ), parse( "\"\"" ) );
}
@Test
public void parse_acceptsLiterals() {
assertSame( JsonValue.NULL, parse( "null" ) );
}
@Test
public void parse_stripsPadding() {
assertEquals( new JsonArray(), parse( " [ ] " ) );
}
@Test
public void parse_ignoresAllWhiteSpace() {
assertEquals( new JsonArray(), parse( "\t\r\n [\t\r\n ]\t\r\n " ) );
}
@Test
public void parse_failsWithUnterminatedString() {
assertParseException( 5, "Unexpected end of input", "[\"foo" );
}
@Test
public void parse_handlesLineBreaksAndColumnsCorrectly() {
assertParseException( 0, 1, 0, "!" );
assertParseException( 2, 2, 0, "[\n!" );
assertParseException( 3, 2, 0, "[\r\n!" );
assertParseException( 6, 3, 1, "[ \n \n !" );
assertParseException( 7, 2, 3, "[ \r\n \r !" );
}
@Test
public void parse_handlesInputsThatExceedBufferSize() throws IOException {
String input = "[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 ]";
JsonValue value = new JsonParser( new StringReader( input ), 3 ).parse();
assertEquals( "[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]", value.toString() );
}
@Test
public void parse_handlesStringsThatExceedBufferSize() throws IOException {
String input = "[ \"lorem ipsum dolor sit amet\" ]";
JsonValue value = new JsonParser( new StringReader( input ), 3 ).parse();
assertEquals( "[\"lorem ipsum dolor sit amet\"]", value.toString() );
}
@Test
public void parse_handlesNumbersThatExceedBufferSize() throws IOException {
String input = "[ 3.141592653589 ]";
JsonValue value = new JsonParser( new StringReader( input ), 3 ).parse();
assertEquals( "[3.141592653589]", value.toString() );
}
@Test
public void parse_handlesPositionsCorrectlyWhenInputExceedsBufferSize() {
final String input = "{\n \"a\": 23,\n \"b\": 42,\n}";
ParseException exception = assertException( ParseException.class, new Runnable() {
public void run() {
try {
new JsonParser( new StringReader( input ), 3 ).parse();
} catch( IOException e ) {
}
}
} );
assertEquals( 4, exception.getLine() );
assertEquals( 0, exception.getColumn() );
assertEquals( 24, exception.getOffset() );
}
@Test
public void arrays_empty() {
assertEquals( "[]", parse( "[]" ).toString() );
}
@Test
public void arrays_singleValue() {
assertEquals( "[23]", parse( "[23]" ).toString() );
}
@Test
public void arrays_multipleValues() {
assertEquals( "[23,42]", parse( "[23,42]" ).toString() );
}
@Test
public void arrays_withWhitespaces() {
assertEquals( "[23,42]", parse( "[ 23 , 42 ]" ).toString() );
}
@Test
public void arrays_nested() {
assertEquals( "[[23],42]", parse( "[[23],42]" ).toString() );
}
@Test
public void arrays_illegalSyntax() {
assertParseException( 1, "Expected value", "[,]" );
assertParseException( 4, "Expected ',' or ']'", "[23 42]" );
assertParseException( 4, "Expected value", "[23,]" );
}
@Test
public void arrays_incomplete() {
assertParseException( 1, "Unexpected end of input", "[" );
assertParseException( 2, "Unexpected end of input", "[ " );
assertParseException( 3, "Unexpected end of input", "[23" );
assertParseException( 4, "Unexpected end of input", "[23 " );
assertParseException( 4, "Unexpected end of input", "[23," );
assertParseException( 5, "Unexpected end of input", "[23, " );
}
@Test
public void objects_empty() {
assertEquals( "{}", parse( "{}" ).toString() );
}
@Test
public void objects_singleValue() {
assertEquals( "{\"foo\":23}", parse( "{\"foo\":23}" ).toString() );
}
@Test
public void objects_multipleValues() {
assertEquals( "{\"foo\":23,\"bar\":42}", parse( "{\"foo\":23,\"bar\":42}" ).toString() );
}
@Test
public void objects_whitespace() {
assertEquals( "{\"foo\":23,\"bar\":42}", parse( "{ \"foo\" : 23, \"bar\" : 42 }" ).toString() );
}
@Test
public void objects_nested() {
assertEquals( "{\"foo\":{\"bar\":42}}", parse( "{\"foo\":{\"bar\":42}}" ).toString() );
}
@Test
public void objects_illegalSyntax() {
assertParseException( 1, "Expected name", "{,}" );
assertParseException( 1, "Expected name", "{:}" );
assertParseException( 1, "Expected name", "{23}" );
assertParseException( 4, "Expected ':'", "{\"a\"}" );
assertParseException( 5, "Expected ':'", "{\"a\" \"b\"}" );
assertParseException( 5, "Expected value", "{\"a\":}" );
assertParseException( 8, "Expected name", "{\"a\":23,}" );
assertParseException( 8, "Expected name", "{\"a\":23,42" );
}
@Test
public void objects_incomplete() {
assertParseException( 1, "Unexpected end of input", "{" );
assertParseException( 2, "Unexpected end of input", "{ " );
assertParseException( 2, "Unexpected end of input", "{\"" );
assertParseException( 4, "Unexpected end of input", "{\"a\"" );
assertParseException( 5, "Unexpected end of input", "{\"a\" " );
assertParseException( 5, "Unexpected end of input", "{\"a\":" );
assertParseException( 6, "Unexpected end of input", "{\"a\": " );
assertParseException( 7, "Unexpected end of input", "{\"a\":23" );
assertParseException( 8, "Unexpected end of input", "{\"a\":23 " );
assertParseException( 8, "Unexpected end of input", "{\"a\":23," );
assertParseException( 9, "Unexpected end of input", "{\"a\":23, " );
}
@Test
public void strings_emptyString_isAccepted() {
assertEquals( "", parse( "\"\"" ).asString() );
}
@Test
public void strings_asciiCharacters_areAccepted() {
assertEquals( " ", parse( "\" \"" ).asString() );
assertEquals( "a", parse( "\"a\"" ).asString() );
assertEquals( "foo", parse( "\"foo\"" ).asString() );
assertEquals( "A2-D2", parse( "\"A2-D2\"" ).asString() );
assertEquals( "\u007f", parse( "\"\u007f\"" ).asString() );
}
@Test
public void strings_nonAsciiCharacters_areAccepted() {
assertEquals( "Русский", parse( "\"Русский\"" ).asString() );
assertEquals( "العربية", parse( "\"العربية\"" ).asString() );
assertEquals( "日本語", parse( "\"日本語\"" ).asString() );
}
@Test
public void strings_controlCharacters_areRejected() {
// JSON string must not contain characters < 0x20
assertParseException( 3, "Expected valid string character", "\"--\n--\"" );
assertParseException( 3, "Expected valid string character", "\"--\r\n--\"" );
assertParseException( 3, "Expected valid string character", "\"--\t--\"" );
assertParseException( 3, "Expected valid string character", "\"--\u0000--\"" );
assertParseException( 3, "Expected valid string character", "\"--\u001f--\"" );
}
@Test
public void strings_validEscapes_areAccepted() {
// valid escapes are \" \\ \/ \b \f \n \r \t and unicode escapes
assertEquals( " \" ", parse( "\" \\\" \"" ).asString() );
assertEquals( " \\ ", parse( "\" \\\\ \"" ).asString() );
assertEquals( " / ", parse( "\" \\/ \"" ).asString() );
assertEquals( " \u0008 ", parse( "\" \\b \"" ).asString() );
assertEquals( " \u000c ", parse( "\" \\f \"" ).asString() );
assertEquals( " \r ", parse( "\" \\r \"" ).asString() );
assertEquals( " \n ", parse( "\" \\n \"" ).asString() );
assertEquals( " \t ", parse( "\" \\t \"" ).asString() );
}
@Test
public void strings_escape_atStart() {
assertEquals( "\\x", parse( "\"\\\\x\"" ).asString() );
}
@Test
public void strings_escape_atEnd() {
assertEquals( "x\\", parse( "\"x\\\\\"" ).asString() );
}
@Test
public void strings_illegalEscapes_areRejected() {
assertParseException( 2, "Expected valid escape sequence", "\"\\a\"" );
assertParseException( 2, "Expected valid escape sequence", "\"\\x\"" );
assertParseException( 2, "Expected valid escape sequence", "\"\\000\"" );
}
@Test
public void strings_validUnicodeEscapes_areAccepted() {
assertEquals( "\u0021", parse( "\"\\u0021\"" ).asString() );
assertEquals( "\u4711", parse( "\"\\u4711\"" ).asString() );
assertEquals( "\uffff", parse( "\"\\uffff\"" ).asString() );
assertEquals( "\uabcdx", parse( "\"\\uabcdx\"" ).asString() );
}
@Test
public void strings_illegalUnicodeEscapes_areRejected() {
assertParseException( 3, "Expected hexadecimal digit", "\"\\u \"" );
assertParseException( 3, "Expected hexadecimal digit", "\"\\ux\"" );
assertParseException( 5, "Expected hexadecimal digit", "\"\\u20 \"" );
assertParseException( 6, "Expected hexadecimal digit", "\"\\u000x\"" );
}
@Test
public void strings_incompleteStrings_areRejected() {
assertParseException( 1, "Unexpected end of input", "\"" );
assertParseException( 4, "Unexpected end of input", "\"foo" );
assertParseException( 5, "Unexpected end of input", "\"foo\\" );
assertParseException( 6, "Unexpected end of input", "\"foo\\n" );
assertParseException( 6, "Unexpected end of input", "\"foo\\u" );
assertParseException( 7, "Unexpected end of input", "\"foo\\u0" );
assertParseException( 9, "Unexpected end of input", "\"foo\\u000" );
assertParseException( 10, "Unexpected end of input", "\"foo\\u0000" );
}
@Test
public void numbers_integer() {
assertEquals( new JsonNumber( "0" ), parse( "0" ) );
assertEquals( new JsonNumber( "-0" ), parse( "-0" ) );
assertEquals( new JsonNumber( "1" ), parse( "1" ) );
assertEquals( new JsonNumber( "-1" ), parse( "-1" ) );
assertEquals( new JsonNumber( "23" ), parse( "23" ) );
assertEquals( new JsonNumber( "-23" ), parse( "-23" ) );
assertEquals( new JsonNumber( "1234567890" ), parse( "1234567890" ) );
assertEquals( new JsonNumber( "123456789012345678901234567890" ),
parse( "123456789012345678901234567890" ) );
}
@Test
public void numbers_minusZero() {
// allowed by JSON, allowed by Java
JsonValue value = parse( "-0" );
assertEquals( 0, value.asInt() );
assertEquals( 0l, value.asLong() );
assertEquals( 0f, value.asFloat(), 0 );
assertEquals( 0d, value.asDouble(), 0 );
}
@Test
public void numbers_decimal() {
assertEquals( new JsonNumber( "0.23" ), parse( "0.23" ) );
assertEquals( new JsonNumber( "-0.23" ), parse( "-0.23" ) );
assertEquals( new JsonNumber( "1234567890.12345678901234567890" ),
parse( "1234567890.12345678901234567890" ) );
}
@Test
public void numbers_withExponent() {
assertEquals( new JsonNumber( "0.1e9" ), parse( "0.1e9" ) );
assertEquals( new JsonNumber( "0.1e9" ), parse( "0.1e9" ) );
assertEquals( new JsonNumber( "0.1E9" ), parse( "0.1E9" ) );
assertEquals( new JsonNumber( "-0.23e9" ), parse( "-0.23e9" ) );
assertEquals( new JsonNumber( "0.23e9" ), parse( "0.23e9" ) );
assertEquals( new JsonNumber( "0.23e+9" ), parse( "0.23e+9" ) );
assertEquals( new JsonNumber( "0.23e-9" ), parse( "0.23e-9" ) );
}
@Test
public void numbers_withInvalidFormat() {
assertParseException( 0, "Expected value", "+1" );
assertParseException( 0, "Expected value", ".1" );
assertParseException( 1, "Unexpected character", "02" );
assertParseException( 2, "Unexpected character", "-02" );
assertParseException( 1, "Expected digit", "-x" );
assertParseException( 2, "Expected digit", "1.x" );
assertParseException( 2, "Expected digit", "1ex" );
assertParseException( 3, "Unexpected character", "1e1x" );
}
@Test
public void numbers_incomplete() {
assertParseException( 1, "Unexpected end of input", "-" );
assertParseException( 2, "Unexpected end of input", "1." );
assertParseException( 4, "Unexpected end of input", "1.0e" );
assertParseException( 5, "Unexpected end of input", "1.0e-" );
}
@Test
public void null_complete() {
assertEquals( JsonValue.NULL, parse( "null" ) );
}
@Test
public void null_incomplete() {
assertParseException( 1, "Unexpected end of input", "n" );
assertParseException( 2, "Unexpected end of input", "nu" );
assertParseException( 3, "Unexpected end of input", "nul" );
}
@Test
public void null_withIllegalCharacter() {
assertParseException( 1, "Expected 'u'", "nx" );
assertParseException( 2, "Expected 'l'", "nux" );
assertParseException( 3, "Expected 'l'", "nulx" );
assertParseException( 4, "Unexpected character", "nullx" );
}
@Test
public void true_complete() {
assertSame( JsonValue.TRUE, parse( "true" ) );
}
@Test
public void true_incomplete() {
assertParseException( 1, "Unexpected end of input", "t" );
assertParseException( 2, "Unexpected end of input", "tr" );
assertParseException( 3, "Unexpected end of input", "tru" );
}
@Test
public void true_withIllegalCharacter() {
assertParseException( 1, "Expected 'r'", "tx" );
assertParseException( 2, "Expected 'u'", "trx" );
assertParseException( 3, "Expected 'e'", "trux" );
assertParseException( 4, "Unexpected character", "truex" );
}
@Test
public void false_complete() {
assertSame( JsonValue.FALSE, parse( "false" ) );
}
@Test
public void false_incomplete() {
assertParseException( 1, "Unexpected end of input", "f" );
assertParseException( 2, "Unexpected end of input", "fa" );
assertParseException( 3, "Unexpected end of input", "fal" );
assertParseException( 4, "Unexpected end of input", "fals" );
}
@Test
public void false_withIllegalCharacter() {
assertParseException( 1, "Expected 'a'", "fx" );
assertParseException( 2, "Expected 'l'", "fax" );
assertParseException( 3, "Expected 's'", "falx" );
assertParseException( 4, "Expected 'e'", "falsx" );
assertParseException( 5, "Unexpected character", "falsex" );
}
private static void assertParseException( int offset, String message, final String json ) {
ParseException exception = assertException( ParseException.class, new Runnable() {
public void run() {
parse( json );
}
} );
assertEquals( offset, exception.getOffset() );
assertThat( exception.getMessage(), StringStartsWith.startsWith( message + " at" ) );
}
private static void assertParseException( int offset, int line, int column, final String json ) {
ParseException exception = assertException( ParseException.class, new Runnable() {
public void run() {
parse( json );
}
} );
assertEquals( "offset", offset, exception.getOffset() );
assertEquals( "line", line, exception.getLine() );
assertEquals( "column", column, exception.getColumn() );
}
private static JsonValue parse( String json ) {
try {
return new JsonParser( json ).parse();
} catch( IOException exception ) {
throw new RuntimeException( exception );
}
}
}