/******************************************************************************* * 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 ); } } }