blob: 1409a3be7e4cf6b7899ab7db0010b3212c807c60 [file] [log] [blame]
/**
* Copyright (C) 2010 The Android Open Source Project
*
* 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 org.json;
import junit.framework.TestCase;
/**
* This black box test was written without inspecting the non-free org.json sourcecode.
*/
public class JSONTokenerTest extends TestCase {
public void testNulls() throws JSONException {
// bogus behaviour: JSONTokener accepts null, only to fail later on almost all APIs.
new JSONTokener(null).back();
try {
new JSONTokener(null).more();
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).next();
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).next(3);
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).next('A');
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).nextClean();
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).nextString('"');
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).nextTo('A');
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).nextTo("ABC");
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).nextValue();
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).skipPast("ABC");
fail();
} catch (NullPointerException e) {
}
try {
new JSONTokener(null).skipTo('A');
fail();
} catch (NullPointerException e) {
}
assertEquals("foo! at character 0 of null",
new JSONTokener(null).syntaxError("foo!").getMessage());
assertEquals(" at character 0 of null", new JSONTokener(null).toString());
}
public void testEmptyString() throws JSONException {
JSONTokener backTokener = new JSONTokener("");
backTokener.back();
assertEquals(" at character 0 of ", backTokener.toString());
assertFalse(new JSONTokener("").more());
assertEquals('\0', new JSONTokener("").next());
try {
new JSONTokener("").next(3);
fail();
} catch (JSONException expected) {
}
try {
new JSONTokener("").next('A');
fail();
} catch (JSONException e) {
}
assertEquals('\0', new JSONTokener("").nextClean());
try {
new JSONTokener("").nextString('"');
fail();
} catch (JSONException e) {
}
assertEquals("", new JSONTokener("").nextTo('A'));
assertEquals("", new JSONTokener("").nextTo("ABC"));
try {
new JSONTokener("").nextValue();
fail();
} catch (JSONException e) {
}
new JSONTokener("").skipPast("ABC");
assertEquals('\0', new JSONTokener("").skipTo('A'));
assertEquals("foo! at character 0 of ",
new JSONTokener("").syntaxError("foo!").getMessage());
assertEquals(" at character 0 of ", new JSONTokener("").toString());
}
public void testCharacterNavigation() throws JSONException {
JSONTokener abcdeTokener = new JSONTokener("ABCDE");
assertEquals('A', abcdeTokener.next());
assertEquals('B', abcdeTokener.next('B'));
assertEquals("CD", abcdeTokener.next(2));
try {
abcdeTokener.next(2);
fail();
} catch (JSONException e) {
}
assertEquals('E', abcdeTokener.nextClean());
assertEquals('\0', abcdeTokener.next());
try {
// bogus behaviour: returning an empty string should be valid
abcdeTokener.next(0);
fail();
} catch (JSONException e) {
}
assertFalse(abcdeTokener.more());
abcdeTokener.back();
assertTrue(abcdeTokener.more());
assertEquals('E', abcdeTokener.next());
}
public void testBackNextAndMore() throws JSONException {
JSONTokener abcTokener = new JSONTokener("ABC");
assertTrue(abcTokener.more());
abcTokener.next();
abcTokener.next();
assertTrue(abcTokener.more());
abcTokener.next();
assertFalse(abcTokener.more());
abcTokener.back();
assertTrue(abcTokener.more());
abcTokener.next();
assertFalse(abcTokener.more());
abcTokener.back();
abcTokener.back();
abcTokener.back();
abcTokener.back(); // bogus behaviour: you can back up before the beginning of a String
assertEquals('A', abcTokener.next());
}
public void testNextMatching() throws JSONException {
JSONTokener abcdTokener = new JSONTokener("ABCD");
assertEquals('A', abcdTokener.next('A'));
try {
abcdTokener.next('C'); // although it failed, this op consumes a character of input
fail();
} catch (JSONException e) {
}
assertEquals('C', abcdTokener.next('C'));
assertEquals('D', abcdTokener.next('D'));
try {
abcdTokener.next('E');
fail();
} catch (JSONException e) {
}
}
public void testNextN() throws JSONException {
JSONTokener abcdeTokener = new JSONTokener("ABCDEF");
assertEquals("", abcdeTokener.next(0));
try {
abcdeTokener.next(7);
fail();
} catch (JSONException e) {
}
assertEquals("ABC", abcdeTokener.next(3));
try {
abcdeTokener.next(4);
fail();
} catch (JSONException e) {
}
try {
// bogus behaviour: there should be 3 characters left, but there must be an off-by-one
// error in the implementation.
assertEquals("DEF", abcdeTokener.next(3));
fail();
} catch (JSONException e) {
}
assertEquals("DE", abcdeTokener.next(2));
assertEquals('F', abcdeTokener.next());
try {
// bogus behaviour: returning an empty string should be valid
abcdeTokener.next(0);
fail();
} catch (JSONException e) {
}
abcdeTokener.back();
abcdeTokener.back();
abcdeTokener.back();
assertEquals("DE", abcdeTokener.next(2));
assertEquals('F', abcdeTokener.next());
}
public void testNextCleanComments() throws JSONException {
JSONTokener tokener = new JSONTokener(
" A /*XX*/B/*XX//XX\n//XX\nXX*/C//X//X//X\nD/*X*///X\n");
assertEquals('A', tokener.nextClean());
assertEquals('B', tokener.nextClean());
assertEquals('C', tokener.nextClean());
assertEquals('D', tokener.nextClean());
assertEquals('\0', tokener.nextClean());
}
public void testNextCleanTrailingOpenComment() throws JSONException {
try {
new JSONTokener(" /* ").nextClean();
fail();
} catch (JSONException e) {
}
assertEquals('\0', new JSONTokener(" // ").nextClean());
}
public void testNextCleanNewlineDelimiters() throws JSONException {
assertEquals('B', new JSONTokener(" // \r\n B ").nextClean());
assertEquals('B', new JSONTokener(" // \n B ").nextClean());
assertEquals('B', new JSONTokener(" // \r B ").nextClean());
}
/**
* Tests which characters tokener treats as ignorable whitespace. See Kevin Bourrillion's
* <a href="https://spreadsheets.google.com/pub?key=pd8dAQyHbdewRsnE5x5GzKQ">list
* of whitespace characters</a>.
*/
public void testNextCleanWhitespace() throws JSONException {
// This behaviour contradicts the JSON spec. It claims the only space
// characters are space, tab, newline and carriage return. But it treats
// many characters like whitespace! These are the same whitespace
// characters used by String.trim(), with the exception of '\0'.
assertEquals("character tabulation", 'A', new JSONTokener("\u0009A").nextClean());
assertEquals("line feed", 'A', new JSONTokener("\nA").nextClean());
assertEquals("line tabulation", 'A', new JSONTokener("\u000bA").nextClean());
assertEquals("form feed", 'A', new JSONTokener("\u000cA").nextClean());
assertEquals("carriage return", 'A', new JSONTokener("\rA").nextClean());
assertEquals("information separator 4", 'A', new JSONTokener("\u001cA").nextClean());
assertEquals("information separator 3", 'A', new JSONTokener("\u001dA").nextClean());
assertEquals("information separator 2", 'A', new JSONTokener("\u001eA").nextClean());
assertEquals("information separator 1", 'A', new JSONTokener("\u001fA").nextClean());
assertEquals("space", 'A', new JSONTokener("\u0020A").nextClean());
for (char c = '\u0002'; c < ' '; c++) {
assertEquals('A', new JSONTokener(new String(new char[] { ' ', c, 'A' })).nextClean());
}
// These characters are neither whitespace in the JSON spec nor the implementation
assertEquals("null", '\u0000', new JSONTokener("\u0000A").nextClean());
assertEquals("next line", '\u0085', new JSONTokener("\u0085A").nextClean());
assertEquals("non-breaking space", '\u00a0', new JSONTokener("\u00a0A").nextClean());
assertEquals("ogham space mark", '\u1680', new JSONTokener("\u1680A").nextClean());
assertEquals("mongolian vowel separator", '\u180e', new JSONTokener("\u180eA").nextClean());
assertEquals("en quad", '\u2000', new JSONTokener("\u2000A").nextClean());
assertEquals("em quad", '\u2001', new JSONTokener("\u2001A").nextClean());
assertEquals("en space", '\u2002', new JSONTokener("\u2002A").nextClean());
assertEquals("em space", '\u2003', new JSONTokener("\u2003A").nextClean());
assertEquals("three-per-em space", '\u2004', new JSONTokener("\u2004A").nextClean());
assertEquals("four-per-em space", '\u2005', new JSONTokener("\u2005A").nextClean());
assertEquals("six-per-em space", '\u2006', new JSONTokener("\u2006A").nextClean());
assertEquals("figure space", '\u2007', new JSONTokener("\u2007A").nextClean());
assertEquals("punctuation space", '\u2008', new JSONTokener("\u2008A").nextClean());
assertEquals("thin space", '\u2009', new JSONTokener("\u2009A").nextClean());
assertEquals("hair space", '\u200a', new JSONTokener("\u200aA").nextClean());
assertEquals("zero-width space", '\u200b', new JSONTokener("\u200bA").nextClean());
assertEquals("left-to-right mark", '\u200e', new JSONTokener("\u200eA").nextClean());
assertEquals("right-to-left mark", '\u200f', new JSONTokener("\u200fA").nextClean());
assertEquals("line separator", '\u2028', new JSONTokener("\u2028A").nextClean());
assertEquals("paragraph separator", '\u2029', new JSONTokener("\u2029A").nextClean());
assertEquals("narrow non-breaking space", '\u202f', new JSONTokener("\u202fA").nextClean());
assertEquals("medium mathematical space", '\u205f', new JSONTokener("\u205fA").nextClean());
assertEquals("ideographic space", '\u3000', new JSONTokener("\u3000A").nextClean());
}
public void testNextString() throws JSONException {
assertEquals("", new JSONTokener("'").nextString('\''));
assertEquals("", new JSONTokener("\"").nextString('\"'));
assertEquals("ABC", new JSONTokener("ABC'DEF").nextString('\''));
assertEquals("ABC", new JSONTokener("ABC'''DEF").nextString('\''));
// nextString permits slash-escaping of arbitrary characters!
assertEquals("ABC", new JSONTokener("A\\B\\C'DEF").nextString('\''));
JSONTokener tokener = new JSONTokener(" 'abc' 'def' \"ghi\"");
tokener.next();
assertEquals('\'', tokener.next());
assertEquals("abc", tokener.nextString('\''));
tokener.next();
assertEquals('\'', tokener.next());
assertEquals("def", tokener.nextString('\''));
tokener.next();
assertEquals('"', tokener.next());
assertEquals("ghi", tokener.nextString('\"'));
assertFalse(tokener.more());
}
public void testNextStringNoDelimiter() throws JSONException {
try {
new JSONTokener("").nextString('\'');
fail();
} catch (JSONException e) {
}
JSONTokener tokener = new JSONTokener(" 'abc");
tokener.next();
tokener.next();
try {
tokener.next('\'');
fail();
} catch (JSONException e) {
}
}
public void testNextStringEscapedQuote() throws JSONException {
try {
new JSONTokener("abc\\").nextString('"');
fail();
} catch (JSONException e) {
}
// we're mixing Java escaping like \" and JavaScript escaping like \\\"
// which makes these tests extra tricky to read!
assertEquals("abc\"def", new JSONTokener("abc\\\"def\"ghi").nextString('"'));
assertEquals("abc\\def", new JSONTokener("abc\\\\def\"ghi").nextString('"'));
assertEquals("abc/def", new JSONTokener("abc\\/def\"ghi").nextString('"'));
assertEquals("abc\bdef", new JSONTokener("abc\\bdef\"ghi").nextString('"'));
assertEquals("abc\fdef", new JSONTokener("abc\\fdef\"ghi").nextString('"'));
assertEquals("abc\ndef", new JSONTokener("abc\\ndef\"ghi").nextString('"'));
assertEquals("abc\rdef", new JSONTokener("abc\\rdef\"ghi").nextString('"'));
assertEquals("abc\tdef", new JSONTokener("abc\\tdef\"ghi").nextString('"'));
}
public void testNextStringUnicodeEscaped() throws JSONException {
// we're mixing Java escaping like \\ and JavaScript escaping like \\u
assertEquals("abc def", new JSONTokener("abc\\u0020def\"ghi").nextString('"'));
assertEquals("abcU0020def", new JSONTokener("abc\\U0020def\"ghi").nextString('"'));
// JSON requires 4 hex characters after a unicode escape
try {
new JSONTokener("abc\\u002\"").nextString('"');
fail();
} catch (JSONException e) {
}
try {
new JSONTokener("abc\\u").nextString('"');
fail();
} catch (JSONException e) {
}
try {
new JSONTokener("abc\\u \"").nextString('"');
fail();
} catch (NumberFormatException e) {
}
assertEquals("abc\"def", new JSONTokener("abc\\u0022def\"ghi").nextString('"'));
try {
new JSONTokener("abc\\u000G\"").nextString('"');
fail();
} catch (NumberFormatException e) {
}
}
public void testNextStringNonQuote() throws JSONException {
assertEquals("AB", new JSONTokener("ABC").nextString('C'));
assertEquals("ABCD", new JSONTokener("AB\\CDC").nextString('C'));
assertEquals("AB\nC", new JSONTokener("AB\\nCn").nextString('n'));
}
public void testNextTo() throws JSONException {
assertEquals("ABC", new JSONTokener("ABCDEFG").nextTo("DHI"));
assertEquals("ABCDEF", new JSONTokener("ABCDEF").nextTo(""));
JSONTokener tokener = new JSONTokener("ABC\rDEF\nGHI\r\nJKL");
assertEquals("ABC", tokener.nextTo("M"));
assertEquals('\r', tokener.next());
assertEquals("DEF", tokener.nextTo("M"));
assertEquals('\n', tokener.next());
assertEquals("GHI", tokener.nextTo("M"));
assertEquals('\r', tokener.next());
assertEquals('\n', tokener.next());
assertEquals("JKL", tokener.nextTo("M"));
tokener = new JSONTokener("ABCDEFGHI");
assertEquals("ABC", tokener.nextTo("DEF"));
assertEquals("", tokener.nextTo("DEF"));
assertEquals('D', tokener.next());
assertEquals("", tokener.nextTo("DEF"));
assertEquals('E', tokener.next());
assertEquals("", tokener.nextTo("DEF"));
assertEquals('F', tokener.next());
assertEquals("GHI", tokener.nextTo("DEF"));
assertEquals("", tokener.nextTo("DEF"));
tokener = new JSONTokener(" \t \fABC \t DEF");
assertEquals("ABC", tokener.nextTo("DEF"));
assertEquals('D', tokener.next());
tokener = new JSONTokener(" \t \fABC \n DEF");
assertEquals("ABC", tokener.nextTo("\n"));
assertEquals("", tokener.nextTo("\n"));
// Bogus behaviour: the tokener stops after \0 always
tokener = new JSONTokener(" \0\t \fABC \n DEF");
assertEquals("", tokener.nextTo("D"));
assertEquals('\t', tokener.next());
assertEquals("ABC", tokener.nextTo("D"));
tokener = new JSONTokener("ABC\0DEF");
assertEquals("ABC", tokener.nextTo("\0"));
assertEquals("DEF", tokener.nextTo("\0"));
tokener = new JSONTokener("");
try {
tokener.nextTo(null);
fail();
} catch (NullPointerException e) {
}
}
public void testSkipPast() {
JSONTokener tokener = new JSONTokener("ABCDEF");
tokener.skipPast("ABC");
assertEquals('D', tokener.next());
tokener.skipPast("EF");
assertEquals('\0', tokener.next());
tokener = new JSONTokener("ABCDEF");
tokener.skipPast("ABCDEF");
assertEquals('\0', tokener.next());
tokener = new JSONTokener("ABCDEF");
tokener.skipPast("G");
assertEquals('\0', tokener.next());
tokener = new JSONTokener("ABC\0ABC");
tokener.skipPast("ABC");
assertEquals('\0', tokener.next());
assertEquals('A', tokener.next());
tokener = new JSONTokener("\0ABC");
tokener.skipPast("ABC");
assertEquals('\0', tokener.next());
tokener = new JSONTokener("ABC\nDEF");
tokener.skipPast("DEF");
assertEquals('\0', tokener.next());
tokener = new JSONTokener("ABC");
tokener.skipPast("ABCDEF");
assertEquals('\0', tokener.next());
tokener = new JSONTokener("ABCDABCDABCD");
tokener.skipPast("ABC");
assertEquals('D', tokener.next());
tokener.skipPast("ABC");
assertEquals('D', tokener.next());
tokener.skipPast("ABC");
assertEquals('D', tokener.next());
tokener = new JSONTokener("");
try {
tokener.skipPast(null);
fail();
} catch (NullPointerException e) {
}
}
public void testSkipTo() {
JSONTokener tokener = new JSONTokener("ABCDEF");
tokener.skipTo('A');
assertEquals('A', tokener.next());
tokener.skipTo('D');
assertEquals('D', tokener.next());
tokener.skipTo('G');
assertEquals('E', tokener.next());
tokener.skipTo('A');
assertEquals('F', tokener.next());
tokener = new JSONTokener("ABC\0DEF");
tokener.skipTo('F');
// bogus behaviour: skipTo gives up when it sees '\0'
assertEquals('A', tokener.next());
tokener = new JSONTokener("ABC\nDEF");
tokener.skipTo('F');
assertEquals('F', tokener.next());
tokener = new JSONTokener("ABCfDEF");
tokener.skipTo('F');
assertEquals('F', tokener.next());
tokener = new JSONTokener("ABC/* DEF */");
tokener.skipTo('D');
assertEquals('D', tokener.next());
}
public void testDehexchar() {
assertEquals( 0, JSONTokener.dehexchar('0'));
assertEquals( 1, JSONTokener.dehexchar('1'));
assertEquals( 2, JSONTokener.dehexchar('2'));
assertEquals( 3, JSONTokener.dehexchar('3'));
assertEquals( 4, JSONTokener.dehexchar('4'));
assertEquals( 5, JSONTokener.dehexchar('5'));
assertEquals( 6, JSONTokener.dehexchar('6'));
assertEquals( 7, JSONTokener.dehexchar('7'));
assertEquals( 8, JSONTokener.dehexchar('8'));
assertEquals( 9, JSONTokener.dehexchar('9'));
assertEquals(10, JSONTokener.dehexchar('A'));
assertEquals(11, JSONTokener.dehexchar('B'));
assertEquals(12, JSONTokener.dehexchar('C'));
assertEquals(13, JSONTokener.dehexchar('D'));
assertEquals(14, JSONTokener.dehexchar('E'));
assertEquals(15, JSONTokener.dehexchar('F'));
assertEquals(10, JSONTokener.dehexchar('a'));
assertEquals(11, JSONTokener.dehexchar('b'));
assertEquals(12, JSONTokener.dehexchar('c'));
assertEquals(13, JSONTokener.dehexchar('d'));
assertEquals(14, JSONTokener.dehexchar('e'));
assertEquals(15, JSONTokener.dehexchar('f'));
for (int c = 0; c <= 0xFFFF; c++) {
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
continue;
}
assertEquals("dehexchar " + c, -1, JSONTokener.dehexchar((char) c));
}
}
public void testNextValue() {
fail("TODO");
}
}