OSDN Git Service

New tests for JSONObject and for JSON's self-use.
authorJesse Wilson <jessewilson@google.com>
Wed, 10 Mar 2010 01:54:35 +0000 (17:54 -0800)
committerJesse Wilson <jessewilson@google.com>
Wed, 10 Mar 2010 18:00:53 +0000 (10:00 -0800)
Alongside development of these tests, I'm working on a new
cleanroom implementation. The self use test was written to
prevent me from self-using in a way that the original
implementation does not.

Change-Id: Ie617aca1978bd39d85b05e5c2c7bd657ed159dd6

libcore/json/src/test/java/org/json/AllTests.java
libcore/json/src/test/java/org/json/JSONArrayTest.java
libcore/json/src/test/java/org/json/JSONObjectTest.java [new file with mode: 0644]
libcore/json/src/test/java/org/json/JSONStringerTest.java
libcore/json/src/test/java/org/json/SelfUseTest.java [new file with mode: 0644]

index 8261a4d..ee6c90e 100644 (file)
@@ -21,10 +21,12 @@ import junit.framework.TestSuite;
 
 public class AllTests {
     public static Test suite() {
-        TestSuite suite = tests.TestSuiteFactory.createTestSuite();
+        TestSuite suite = new TestSuite();
         suite.addTestSuite(JSONArrayTest.class);
+        suite.addTestSuite(JSONObjectTest.class);
         suite.addTestSuite(JSONStringerTest.class);
-        suite.addTestSuite(JSONStringerTest.class);
+        suite.addTestSuite(JSONTokenerTest.class);
+        suite.addTestSuite(SelfUseTest.class);
         return suite;
     }
 }
index 34e5ff6..d6013a8 100644 (file)
@@ -19,6 +19,7 @@ package org.json;
 import junit.framework.TestCase;
 
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * This black box test was written without inspecting the non-free org.json sourcecode.
@@ -131,17 +132,17 @@ public class JSONArrayTest extends TestCase {
         // bogus behaviour: there's 2 ways to represent null; each behaves differently!
         assertEquals(JSONObject.NULL, array.get(0));
         try {
-            assertEquals(null, array.get(1));
+            array.get(1);
             fail();
         } catch (JSONException e) {
         }
         try {
-            assertEquals(null, array.get(2));
+            array.get(2);
             fail();
         } catch (JSONException e) {
         }
         try {
-            assertEquals(null, array.get(3));
+            array.get(3);
             fail();
         } catch (JSONException e) {
         }
@@ -238,7 +239,7 @@ public class JSONArrayTest extends TestCase {
         assertEquals(-2, array.optInt(0, -2));
 
         assertEquals(5.5d, array.getDouble(1));
-        assertEquals(5, array.getLong(1));
+        assertEquals(5L, array.getLong(1));
         assertEquals(5, array.getInt(1));
         assertEquals(5, array.optInt(1, 3));
 
@@ -285,13 +286,45 @@ public class JSONArrayTest extends TestCase {
         values.put(5.5d);
         values.put(null);
 
-        // bogus behaviour: null values are stripped 
+        // bogus behaviour: null values are stripped
         JSONObject object = values.toJSONObject(keys);
         assertEquals(1, object.length());
         assertFalse(object.has("b"));
         assertEquals("{\"a\":5.5}", object.toString());
     }
 
+    public void testToJSONObjectMoreNamesThanValues() throws JSONException {
+        JSONArray keys = new JSONArray();
+        keys.put("a");
+        keys.put("b");
+        JSONArray values = new JSONArray();
+        values.put(5.5d);
+        JSONObject object = values.toJSONObject(keys);
+        assertEquals(1, object.length());
+        assertEquals(5.5d, object.get("a"));
+    }
+
+    public void testToJSONObjectMoreValuesThanNames() throws JSONException {
+        JSONArray keys = new JSONArray();
+        keys.put("a");
+        JSONArray values = new JSONArray();
+        values.put(5.5d);
+        values.put(11.0d);
+        JSONObject object = values.toJSONObject(keys);
+        assertEquals(1, object.length());
+        assertEquals(5.5d, object.get("a"));
+    }
+
+    public void testToJSONObjectNullKey() throws JSONException {
+        JSONArray keys = new JSONArray();
+        keys.put(JSONObject.NULL);
+        JSONArray values = new JSONArray();
+        values.put(5.5d);
+        JSONObject object = values.toJSONObject(keys);
+        assertEquals(1, object.length());
+        assertEquals(5.5d, object.get("null"));
+    }
+
     public void testPutUnsupportedNumbers() throws JSONException {
         JSONArray array = new JSONArray();
 
@@ -312,6 +345,10 @@ public class JSONArrayTest extends TestCase {
         }
     }
 
+    /**
+     * Although JSONArray is usually defensive about which numbers it accepts,
+     * it doesn't check inputs in its constructor.
+     */
     public void testCreateWithUnsupportedNumbers() throws JSONException {
         JSONArray array = new JSONArray(Arrays.asList(5.5, Double.NaN));
         assertEquals(2, array.length());
@@ -324,6 +361,13 @@ public class JSONArrayTest extends TestCase {
         JSONArray array = new JSONArray(Arrays.asList(5.5, Double.NaN));
         assertNull(array.toString());
     }
+    
+    public void testListConstructorCopiesContents() throws JSONException {
+        List<Object> contents = Arrays.<Object>asList(5);
+        JSONArray array = new JSONArray(contents);
+        contents.set(0, 10);
+        assertEquals(5, array.get(0));
+    }
 
     public void testCreate() throws JSONException {
         JSONArray array = new JSONArray(Arrays.asList(5.5, true));
@@ -333,6 +377,35 @@ public class JSONArrayTest extends TestCase {
         assertEquals("[5.5,true]", array.toString());
     }
 
+    public void testAccessOutOfBounds() throws JSONException {
+        JSONArray array = new JSONArray();
+        array.put("foo");
+        assertEquals(null, array.opt(3));
+        assertEquals(null, array.opt(-3));
+        assertEquals("", array.optString(3));
+        assertEquals("", array.optString(-3));
+        try {
+            array.get(3);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            array.get(-3);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            array.getString(3);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            array.getString(-3);
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
     public void testParsingConstructor() {
         fail("TODO");
     }
diff --git a/libcore/json/src/test/java/org/json/JSONObjectTest.java b/libcore/json/src/test/java/org/json/JSONObjectTest.java
new file mode 100644 (file)
index 0000000..83beea8
--- /dev/null
@@ -0,0 +1,803 @@
+/**
+ * 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 org.json;
+
+import junit.framework.TestCase;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+/**
+ * This black box test was written without inspecting the non-free org.json sourcecode.
+ */
+public class JSONObjectTest extends TestCase {
+
+    public void testEmptyObject() throws JSONException {
+        JSONObject object = new JSONObject();
+        assertEquals(0, object.length());
+
+        // bogus (but documented) behaviour: returns null rather than the empty object
+        assertNull(object.names());
+
+        // bogus behaviour: returns null rather than an empty array
+        assertNull(object.toJSONArray(new JSONArray()));
+        assertEquals("{}", object.toString());
+        assertEquals("{}", object.toString(5));
+        try {
+            object.get("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.getBoolean("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.getDouble("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.getInt("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.getJSONArray("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.getJSONObject("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.getLong("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.getString("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        assertFalse(object.has("foo"));
+        assertTrue(object.isNull("foo")); // isNull also means "is not present"
+        assertNull(object.opt("foo"));
+        assertEquals(false, object.optBoolean("foo"));
+        assertEquals(true, object.optBoolean("foo", true));
+        assertEquals(Double.NaN, object.optDouble("foo"));
+        assertEquals(5.0, object.optDouble("foo", 5.0));
+        assertEquals(0, object.optInt("foo"));
+        assertEquals(5, object.optInt("foo", 5));
+        assertEquals(null, object.optJSONArray("foo"));
+        assertEquals(null, object.optJSONObject("foo"));
+        assertEquals(0, object.optLong("foo"));
+        assertEquals(Long.MAX_VALUE-1, object.optLong("foo", Long.MAX_VALUE-1));
+        assertEquals("", object.optString("foo")); // empty string is default!
+        assertEquals("bar", object.optString("foo", "bar"));
+        assertNull(object.remove("foo"));
+    }
+    
+    public void testEqualsAndHashCode() throws JSONException {
+        JSONObject a = new JSONObject();
+        JSONObject b = new JSONObject();
+
+        // JSON object doesn't override either equals or hashCode (!)
+        assertFalse(a.equals(b));
+        assertEquals(a.hashCode(), System.identityHashCode(a));
+    }
+
+    public void testGet() throws JSONException {
+        JSONObject object = new JSONObject();
+        Object value = new Object();
+        object.put("foo", value);
+        object.put("bar", new Object());
+        object.put("baz", new Object());
+        assertSame(value, object.get("foo"));
+        try {
+            object.get("FOO");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.put(null, value);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.get(null);
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
+    public void testPut() throws JSONException {
+        JSONObject object = new JSONObject();
+        assertSame(object, object.put("foo", true));
+        object.put("foo", false);
+        assertEquals(false, object.get("foo"));
+
+        object.put("foo", 5.0d);
+        assertEquals(5.0d, object.get("foo"));
+        object.put("foo", 0);
+        assertEquals(0, object.get("foo"));
+        object.put("bar", Long.MAX_VALUE - 1);
+        assertEquals(Long.MAX_VALUE - 1, object.get("bar"));
+        object.put("baz", "x");
+        assertEquals("x", object.get("baz"));
+        object.put("bar", JSONObject.NULL);
+        assertSame(JSONObject.NULL, object.get("bar"));
+    }
+
+    public void testPutNullRemoves() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", "bar");
+        object.put("foo", null);
+        assertEquals(0, object.length());
+        assertFalse(object.has("foo"));
+        try {
+            object.get("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
+    public void testPutOpt() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", "bar");
+        object.putOpt("foo", null);
+        assertEquals("bar", object.get("foo"));
+        object.putOpt(null, null);
+        assertEquals(1, object.length());
+        object.putOpt(null, "bar");
+        assertEquals(1, object.length());
+    }
+
+    public void testPutOptUnsupportedNumbers() throws JSONException {
+        JSONObject object = new JSONObject();
+        try {
+            object.putOpt("foo", Double.NaN);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.putOpt("foo", Double.NEGATIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.putOpt("foo", Double.POSITIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
+    public void testRemove() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", "bar");
+        assertEquals(null, object.remove(null));
+        assertEquals(null, object.remove(""));
+        assertEquals(null, object.remove("bar"));
+        assertEquals("bar", object.remove("foo"));
+        assertEquals(null, object.remove("foo"));
+    }
+
+    public void testBooleans() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", true);
+        object.put("bar", false);
+        object.put("baz", "true");
+        object.put("quux", "false");
+        assertEquals(4, object.length());
+        assertEquals(true, object.getBoolean("foo"));
+        assertEquals(false, object.getBoolean("bar"));
+        assertEquals(true, object.getBoolean("baz"));
+        assertEquals(false, object.getBoolean("quux"));
+        assertFalse(object.isNull("foo"));
+        assertFalse(object.isNull("quux"));
+        assertTrue(object.has("foo"));
+        assertTrue(object.has("quux"));
+        assertFalse(object.has("missing"));
+        assertEquals(true, object.optBoolean("foo"));
+        assertEquals(false, object.optBoolean("bar"));
+        assertEquals(true, object.optBoolean("baz"));
+        assertEquals(false, object.optBoolean("quux"));
+        assertEquals(false, object.optBoolean("missing"));
+        assertEquals(true, object.optBoolean("foo", true));
+        assertEquals(false, object.optBoolean("bar", true));
+        assertEquals(true, object.optBoolean("baz", true));
+        assertEquals(false, object.optBoolean("quux", true));
+        assertEquals(true, object.optBoolean("missing", true));
+
+        object.put("foo", "truE");
+        object.put("bar", "FALSE");
+        assertEquals(true, object.getBoolean("foo"));
+        assertEquals(false, object.getBoolean("bar"));
+        assertEquals(true, object.optBoolean("foo"));
+        assertEquals(false, object.optBoolean("bar"));
+        assertEquals(true, object.optBoolean("foo", false));
+        assertEquals(false, object.optBoolean("bar", false));
+    }
+
+    public void testNumbers() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", Double.MIN_VALUE);
+        object.put("bar", 9223372036854775806L);
+        object.put("baz", Double.MAX_VALUE);
+        object.put("quux", -0d);
+        assertEquals(4, object.length());
+
+        assertTrue(object.toString().contains("\"foo\":4.9E-324"));
+        assertTrue(object.toString().contains("\"bar\":9223372036854775806"));
+        assertTrue(object.toString().contains("\"baz\":1.7976931348623157E308"));
+
+        // bogus behaviour: toString() and getString() return different values for -0d
+        assertTrue(object.toString().contains("\"quux\":-0}") // no trailing decimal point
+                || object.toString().contains("\"quux\":-0,"));
+
+        assertEquals(Double.MIN_VALUE, object.get("foo"));
+        assertEquals(9223372036854775806L, object.get("bar"));
+        assertEquals(Double.MAX_VALUE, object.get("baz"));
+        assertEquals(-0d, object.get("quux"));
+        assertEquals(Double.MIN_VALUE, object.getDouble("foo"));
+        assertEquals(9.223372036854776E18, object.getDouble("bar"));
+        assertEquals(Double.MAX_VALUE, object.getDouble("baz"));
+        assertEquals(-0d, object.getDouble("quux"));
+        assertEquals(0, object.getLong("foo"));
+        assertEquals(9223372036854775806L, object.getLong("bar"));
+        assertEquals(Long.MAX_VALUE, object.getLong("baz"));
+        assertEquals(0, object.getLong("quux"));
+        assertEquals(0, object.getInt("foo"));
+        assertEquals(-2, object.getInt("bar"));
+        assertEquals(Integer.MAX_VALUE, object.getInt("baz"));
+        assertEquals(0, object.getInt("quux"));
+        assertEquals(Double.MIN_VALUE, object.opt("foo"));
+        assertEquals(9223372036854775806L, object.optLong("bar"));
+        assertEquals(Double.MAX_VALUE, object.optDouble("baz"));
+        assertEquals(0, object.optInt("quux"));
+        assertEquals(Double.MIN_VALUE, object.opt("foo"));
+        assertEquals(9223372036854775806L, object.optLong("bar"));
+        assertEquals(Double.MAX_VALUE, object.optDouble("baz"));
+        assertEquals(0, object.optInt("quux"));
+        assertEquals(Double.MIN_VALUE, object.optDouble("foo", 5.0d));
+        assertEquals(9223372036854775806L, object.optLong("bar", 1L));
+        assertEquals(Long.MAX_VALUE, object.optLong("baz", 1L));
+        assertEquals(0, object.optInt("quux", -1));
+        assertEquals("4.9E-324", object.getString("foo"));
+        assertEquals("9223372036854775806", object.getString("bar"));
+        assertEquals("1.7976931348623157E308", object.getString("baz"));
+        assertEquals("-0.0", object.getString("quux"));
+    }
+
+    public void testFloats() throws JSONException {
+        JSONObject object = new JSONObject();
+        try {
+            object.put("foo", (Float) Float.NaN);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.put("foo", (Float) Float.NEGATIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.put("foo", (Float) Float.POSITIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
+    public void testOtherNumbers() throws JSONException {
+        Number nan = new Number() {
+            public int intValue() {
+                return 0;
+            }
+            public long longValue() {
+                return 1L;
+            }
+            public float floatValue() {
+                return 2;
+            }
+            public double doubleValue() {
+                return Double.NaN;
+            }
+            @Override public String toString() {
+                return "x";
+            }
+        };
+
+        // bogus behaviour: foreign object types should be rejected!
+        JSONObject object = new JSONObject();
+        object.put("foo", nan);
+        assertEquals("{\"foo\":x}", object.toString());
+    }
+
+    public void testForeignObjects() throws JSONException {
+        Object foreign = new Object() {
+            @Override public String toString() {
+                return "x";
+            }
+        };
+
+        // bogus behaviour: foreign object types should be rejected and not treated as Strings
+        JSONObject object = new JSONObject();
+        object.put("foo", foreign);
+        assertEquals("{\"foo\":\"x\"}", object.toString());
+    }
+
+    public void testNullKeys() {
+        try {
+            new JSONObject().put(null, false);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            new JSONObject().put(null, 0.0d);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            new JSONObject().put(null, 5);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            new JSONObject().put(null, 5L);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            new JSONObject().put(null, "foo");
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
+    public void testStrings() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", "true");
+        object.put("bar", "5.5");
+        object.put("baz", "9223372036854775806");
+        object.put("quux", "null");
+        object.put("height", "5\"8' tall");
+
+        assertTrue(object.toString().contains("\"foo\":\"true\""));
+        assertTrue(object.toString().contains("\"bar\":\"5.5\""));
+        assertTrue(object.toString().contains("\"baz\":\"9223372036854775806\""));
+        assertTrue(object.toString().contains("\"quux\":\"null\""));
+        assertTrue(object.toString().contains("\"height\":\"5\\\"8' tall\""));
+
+        assertEquals("true", object.get("foo"));
+        assertEquals("null", object.getString("quux"));
+        assertEquals("5\"8' tall", object.getString("height"));
+        assertEquals("true", object.opt("foo"));
+        assertEquals("5.5", object.optString("bar"));
+        assertEquals("true", object.optString("foo", "x"));
+        assertFalse(object.isNull("foo"));
+
+        assertEquals(true, object.getBoolean("foo"));
+        assertEquals(true, object.optBoolean("foo"));
+        assertEquals(true, object.optBoolean("foo", false));
+        assertEquals(0, object.optInt("foo"));
+        assertEquals(-2, object.optInt("foo", -2));
+
+        assertEquals(5.5d, object.getDouble("bar"));
+        assertEquals(5L, object.getLong("bar"));
+        assertEquals(5, object.getInt("bar"));
+        assertEquals(5, object.optInt("bar", 3));
+
+        // The last digit of the string is a 6 but getLong returns a 7. It's probably parsing as a
+        // double and then converting that to a long. This is consistent with JavaScript.
+        assertEquals(9223372036854775807L, object.getLong("baz"));
+        assertEquals(9.223372036854776E18, object.getDouble("baz"));
+        assertEquals(Integer.MAX_VALUE, object.getInt("baz"));
+
+        assertFalse(object.isNull("quux"));
+        try {
+            object.getDouble("quux");
+            fail();
+        } catch (JSONException e) {
+        }
+        assertEquals(Double.NaN, object.optDouble("quux"));
+        assertEquals(-1.0d, object.optDouble("quux", -1.0d));
+
+        object.put("foo", "TRUE");
+        assertEquals(true, object.getBoolean("foo"));
+    }
+
+    public void testJSONObjects() throws JSONException {
+        JSONObject object = new JSONObject();
+
+        JSONArray a = new JSONArray();
+        JSONObject b = new JSONObject();
+        object.put("foo", a);
+        object.put("bar", b);
+
+        assertSame(a, object.getJSONArray("foo"));
+        assertSame(b, object.getJSONObject("bar"));
+        try {
+            object.getJSONObject("foo");
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.getJSONArray("bar");
+            fail();
+        } catch (JSONException e) {
+        }
+        assertEquals(a, object.optJSONArray("foo"));
+        assertEquals(b, object.optJSONObject("bar"));
+        assertEquals(null, object.optJSONArray("bar"));
+        assertEquals(null, object.optJSONObject("foo"));
+    }
+
+    public void testToJSONArray() throws JSONException {
+        JSONObject object = new JSONObject();
+        Object value = new Object();
+        object.put("foo", true);
+        object.put("bar", 5.0d);
+        object.put("baz", -0.0d);
+        object.put("quux", value);
+
+        JSONArray names = new JSONArray();
+        names.put("baz");
+        names.put("quux");
+        names.put("foo");
+
+        JSONArray array = object.toJSONArray(names);
+        assertEquals(-0.0d, array.get(0));
+        assertEquals(value, array.get(1));
+        assertEquals(true, array.get(2));
+
+        object.put("foo", false);
+        assertEquals(true, array.get(2));
+    }
+
+    public void testToJSONArrayMissingNames() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", true);
+        object.put("bar", 5.0d);
+        object.put("baz", JSONObject.NULL);
+
+        JSONArray names = new JSONArray();
+        names.put("bar");
+        names.put("foo");
+        names.put("quux");
+        names.put("baz");
+
+        JSONArray array = object.toJSONArray(names);
+        assertEquals(4, array.length());
+
+        assertEquals(5.0d, array.get(0));
+        assertEquals(true, array.get(1));
+        try {
+            array.get(2);
+            fail();
+        } catch (JSONException e) {
+        }
+        assertEquals(JSONObject.NULL, array.get(3));
+    }
+
+    public void testToJSONArrayNull() throws JSONException {
+        JSONObject object = new JSONObject();
+        assertEquals(null, object.toJSONArray(null));
+        object.put("foo", 5);
+        try {
+            object.toJSONArray(null);
+        } catch (JSONException e) {
+        }
+    }
+
+    public void testToJSONArrayEndsUpEmpty() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", 5);
+        JSONArray array = new JSONArray();
+        array.put("bar");
+        assertEquals(1, object.toJSONArray(array).length());
+    }
+
+    public void testToJSONArrayNonString() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", 5);
+        object.put("null", 10);
+        object.put("false", 15);
+
+        JSONArray names = new JSONArray();
+        names.put(JSONObject.NULL);
+        names.put(false);
+        names.put("foo");
+
+        // bogus behaviour: array elements are converted to Strings
+        JSONArray array = object.toJSONArray(names);
+        assertEquals(3, array.length());
+        assertEquals(10, array.get(0));
+        assertEquals(15, array.get(1));
+        assertEquals(5, array.get(2));
+    }
+
+    public void testPutUnsupportedNumbers() throws JSONException {
+        JSONObject object = new JSONObject();
+        try {
+            object.put("foo", Double.NaN);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.put("foo", Double.NEGATIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.put("foo", Double.POSITIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
+    public void testPutUnsupportedNumbersAsObjects() throws JSONException {
+        JSONObject object = new JSONObject();
+        try {
+            object.put("foo", (Double) Double.NaN);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.put("foo", (Double) Double.NEGATIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            object.put("foo", (Double) Double.POSITIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
+    /**
+     * Although JSONObject is usually defensive about which numbers it accepts,
+     * it doesn't check inputs in its constructor.
+     */
+    public void testCreateWithUnsupportedNumbers() throws JSONException {
+        Map<String, Object> contents = new HashMap<String, Object>();
+        contents.put("foo", Double.NaN);
+        contents.put("bar", Double.NEGATIVE_INFINITY);
+        contents.put("baz", Double.POSITIVE_INFINITY);
+
+        JSONObject object = new JSONObject(contents);
+        assertEquals(Double.NaN, object.get("foo"));
+        assertEquals(Double.NEGATIVE_INFINITY, object.get("bar"));
+        assertEquals(Double.POSITIVE_INFINITY, object.get("baz"));
+    }
+
+    public void testToStringWithUnsupportedNumbers() {
+        // bogus behaviour: when the object contains an unsupported number, toString returns null
+        JSONObject object = new JSONObject(Collections.singletonMap("foo", Double.NaN));
+        assertEquals(null, object.toString());
+    }
+
+    public void testMapConstructorCopiesContents() throws JSONException {
+        Map<String, Object> contents = new HashMap<String, Object>();
+        contents.put("foo", 5);
+        JSONObject object = new JSONObject(contents);
+        contents.put("foo", 10);
+        assertEquals(5, object.get("foo"));
+    }
+
+    public void testMapConstructorWithBogusEntries() {
+        Map<Object, Object> contents = new HashMap<Object, Object>();
+        contents.put(5, 5);
+
+        // bogus behaviour: the constructor doesn't validate its input
+        new JSONObject(contents);
+    }
+
+    public void testAccumulateMutatesInPlace() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", 5);
+        object.accumulate("foo", 6);
+        JSONArray array = object.getJSONArray("foo");
+        assertEquals("[5,6]", array.toString());
+        object.accumulate("foo", 7);
+        assertEquals("[5,6,7]", array.toString());
+    }
+
+    public void testAccumulateExistingArray() throws JSONException {
+        JSONArray array = new JSONArray();
+        JSONObject object = new JSONObject();
+        object.put("foo", array);
+        object.accumulate("foo", 5);
+        assertEquals("[5]", array.toString());
+    }
+
+    public void testAccumulatePutArray() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.accumulate("foo", 5);
+        assertEquals("{\"foo\":5}", object.toString());
+        object.accumulate("foo", new JSONArray());
+        assertEquals("{\"foo\":[5,[]]}", object.toString());
+    }
+
+    public void testAccumulateNull() {
+        JSONObject object = new JSONObject();
+        try {
+            object.accumulate(null, 5);
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+
+    public void testEmptyStringKey() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("", 5);
+        assertEquals(5, object.get(""));
+        assertEquals("{\"\":5}", object.toString());
+    }
+
+    public void testNullValue() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", JSONObject.NULL);
+        object.put("bar", null);
+
+        // bogus behaviour: there's 2 ways to represent null; each behaves differently!
+        assertTrue(object.has("foo"));
+        assertFalse(object.has("bar"));
+        assertTrue(object.isNull("foo"));
+        assertTrue(object.isNull("bar"));
+    }
+
+    public void testHas() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", 5);
+        assertTrue(object.has("foo"));
+        assertFalse(object.has("bar"));
+        assertFalse(object.has(null));
+    }
+    
+    public void testOptNull() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", "bar");
+        assertEquals(null, object.opt(null));
+        assertEquals(false, object.optBoolean(null));
+        assertEquals(Double.NaN, object.optDouble(null));
+        assertEquals(0, object.optInt(null));
+        assertEquals(0L, object.optLong(null));
+        assertEquals(null, object.optJSONArray(null));
+        assertEquals(null, object.optJSONObject(null));
+        assertEquals("", object.optString(null));
+        assertEquals(true, object.optBoolean(null, true));
+        assertEquals(0.0d, object.optDouble(null, 0.0d));
+        assertEquals(1, object.optInt(null, 1));
+        assertEquals(1L, object.optLong(null, 1L));
+        assertEquals("baz", object.optString(null, "baz"));
+    }
+
+    public void testToStringWithIndentFactor() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", new JSONArray(Arrays.asList(5, 6)));
+        object.put("bar", new JSONObject());
+        String foobar = "{\n" +
+                "     \"foo\": [\n" +
+                "          5,\n" +
+                "          6\n" +
+                "     ],\n" +
+                "     \"bar\": {}\n" +
+                "}";
+        String barfoo = "{\n" +
+                "     \"bar\": {},\n" +
+                "     \"foo\": [\n" +
+                "          5,\n" +
+                "          6\n" +
+                "     ]\n" +
+                "}";
+        String string = object.toString(5);
+        assertTrue(string, foobar.equals(string) || barfoo.equals(string));
+    }
+
+    public void testNames() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", 5);
+        object.put("bar", 6);
+        object.put("baz", 7);
+        JSONArray array = object.names();
+        assertTrue(array.toString().contains("foo"));
+        assertTrue(array.toString().contains("bar"));
+        assertTrue(array.toString().contains("baz"));
+    }
+
+    public void testKeysEmptyObject() {
+        JSONObject object = new JSONObject();
+        assertFalse(object.keys().hasNext());
+        try {
+            object.keys().next();
+            fail();
+        } catch (NoSuchElementException e) {
+        }
+    }
+
+    public void testKeys() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", 5);
+        object.put("bar", 6);
+        object.put("foo", 7);
+
+        @SuppressWarnings("unchecked")
+        Iterator<String> keys = (Iterator<String>) object.keys();
+        Set<String> result = new HashSet<String>();
+        assertTrue(keys.hasNext());
+        result.add(keys.next());
+        assertTrue(keys.hasNext());
+        result.add(keys.next());
+        assertFalse(keys.hasNext());
+        assertEquals(new HashSet<String>(Arrays.asList("foo", "bar")), result);
+
+        try {
+            keys.next();
+            fail();
+        } catch (NoSuchElementException e) {
+        }
+    }
+
+    public void testMutatingKeysMutatesObject() throws JSONException {
+        JSONObject object = new JSONObject();
+        object.put("foo", 5);
+        Iterator keys = object.keys();
+        keys.next();
+        keys.remove();
+        assertEquals(0, object.length());
+    }
+
+    public void testQuote() {
+        // covered by JSONStringerTest.testEscaping()
+    }
+
+    public void testNumberToString() throws JSONException {
+        assertEquals("5", JSONObject.numberToString(5));
+        assertEquals("-0", JSONObject.numberToString(-0.0d));
+        assertEquals("9223372036854775806", JSONObject.numberToString(9223372036854775806L));
+        assertEquals("4.9E-324", JSONObject.numberToString(Double.MIN_VALUE));
+        assertEquals("1.7976931348623157E308", JSONObject.numberToString(Double.MAX_VALUE));
+        try {
+            JSONObject.numberToString(Double.NaN);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            JSONObject.numberToString(Double.NEGATIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+        try {
+            JSONObject.numberToString(Double.POSITIVE_INFINITY);
+            fail();
+        } catch (JSONException e) {
+        }
+        assertEquals("0.001", JSONObject.numberToString(new BigDecimal("0.001")));
+        assertEquals("9223372036854775806",
+                JSONObject.numberToString(new BigInteger("9223372036854775806")));
+        try {
+            JSONObject.numberToString(null);
+            fail();
+        } catch (JSONException e) {
+        }
+    }
+}
index a30df9e..03a2903 100644 (file)
@@ -213,6 +213,7 @@ public class JSONStringerTest extends TestCase {
                 new JSONStringer().object().key("a").value(original).endObject().toString());
         assertEquals("[\"" + escaped + "\"]",
                 new JSONStringer().array().value(original).endArray().toString());
+        assertEquals("\"" + escaped + "\"", JSONObject.quote(original));
     }
 
     public void testJSONArrayAsValue() throws JSONException {
diff --git a/libcore/json/src/test/java/org/json/SelfUseTest.java b/libcore/json/src/test/java/org/json/SelfUseTest.java
new file mode 100644 (file)
index 0000000..e3d428b
--- /dev/null
@@ -0,0 +1,204 @@
+/**
+ * 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;
+
+/**
+ * These tests checks self use; ie. when methods delegate to non-final methods.
+ * For the most part we doesn't attempt to cover self-use, except in those cases
+ * where our clean room implementation does it.
+ *
+ * <p>This black box test was written without inspecting the non-free org.json
+ * sourcecode.
+ */
+public class SelfUseTest extends TestCase {
+
+    private int objectPutCalls = 0;
+    private int objectGetCalls = 0;
+    private int objectOptCalls = 0;
+    private int objectOptTypeCalls = 0;
+    private int arrayPutCalls = 0;
+    private int arrayGetCalls = 0;
+    private int arrayOptCalls = 0;
+    private int arrayOptTypeCalls = 0;
+
+    private final JSONObject object = new JSONObject() {
+        @Override public JSONObject put(String name, Object value) throws JSONException {
+            objectPutCalls++;
+            return super.put(name, value);
+        }
+        @Override public Object get(String name) throws JSONException {
+            objectGetCalls++;
+            return super.get(name);
+        }
+        @Override public Object opt(String name) {
+            objectOptCalls++;
+            return super.opt(name);
+        }
+        @Override public boolean optBoolean(String key, boolean defaultValue) {
+            objectOptTypeCalls++;
+            return super.optBoolean(key, defaultValue);
+        }
+        @Override public double optDouble(String key, double defaultValue) {
+            objectOptTypeCalls++;
+            return super.optDouble(key, defaultValue);
+        }
+        @Override public int optInt(String key, int defaultValue) {
+            objectOptTypeCalls++;
+            return super.optInt(key, defaultValue);
+        }
+        @Override public long optLong(String key, long defaultValue) {
+            objectOptTypeCalls++;
+            return super.optLong(key, defaultValue);
+        }
+        @Override public String optString(String key, String defaultValue) {
+            objectOptTypeCalls++;
+            return super.optString(key, defaultValue);
+        }
+    };
+
+    private final JSONArray array = new JSONArray() {
+        @Override public JSONArray put(int index, Object value) throws JSONException {
+            arrayPutCalls++;
+            return super.put(index, value);
+        }
+        @Override public Object get(int index) throws JSONException {
+            arrayGetCalls++;
+            return super.get(index);
+        }
+        @Override public Object opt(int index) {
+            arrayOptCalls++;
+            return super.opt(index);
+        }
+        @Override public boolean optBoolean(int index, boolean fallback) {
+            arrayOptTypeCalls++;
+            return super.optBoolean(index, fallback);
+        }
+        @Override public double optDouble(int index, double fallback) {
+            arrayOptTypeCalls++;
+            return super.optDouble(index, fallback);
+        }
+        @Override public long optLong(int index, long fallback) {
+            arrayOptTypeCalls++;
+            return super.optLong(index, fallback);
+        }
+        @Override public String optString(int index, String fallback) {
+            arrayOptTypeCalls++;
+            return super.optString(index, fallback);
+        }
+        @Override public int optInt(int index, int fallback) {
+            arrayOptTypeCalls++;
+            return super.optInt(index, fallback);
+        }
+    };
+
+    public void testObjectPut() throws JSONException {
+        object.putOpt("foo", "bar");
+        assertEquals(1, objectPutCalls);
+    }
+
+    public void testObjectAccumulate() throws JSONException {
+        object.accumulate("foo", "bar");
+        assertEquals(1, objectPutCalls);
+    }
+
+    public void testObjectGetBoolean() throws JSONException {
+        object.put("foo", "true");
+        object.getBoolean("foo");
+        assertEquals(1, objectGetCalls);
+    }
+
+    public void testObjectOptType() throws JSONException {
+        object.optBoolean("foo");
+        assertEquals(1, objectOptCalls);
+        assertEquals(1, objectOptTypeCalls);
+        object.optDouble("foo");
+        assertEquals(2, objectOptCalls);
+        assertEquals(2, objectOptTypeCalls);
+        object.optInt("foo");
+        assertEquals(3, objectOptCalls);
+        assertEquals(3, objectOptTypeCalls);
+        object.optLong("foo");
+        assertEquals(4, objectOptCalls);
+        assertEquals(4, objectOptTypeCalls);
+        object.optString("foo");
+        assertEquals(5, objectOptCalls);
+        assertEquals(5, objectOptTypeCalls);
+    }
+
+    public void testToJSONArray() throws JSONException {
+        object.put("foo", 5);
+        object.put("bar", 10);
+        array.put("foo");
+        array.put("baz");
+        array.put("bar");
+        object.toJSONArray(array);
+        assertEquals(3, arrayOptCalls);
+        assertEquals(0, arrayOptTypeCalls);
+        assertEquals(3, objectOptCalls);
+        assertEquals(0, objectOptTypeCalls);
+    }
+
+    public void testPutAtIndex() throws JSONException {
+        array.put(10, false);
+        assertEquals(1, arrayPutCalls);
+    }
+
+    public void testIsNull() {
+        array.isNull(5);
+        assertEquals(1, arrayOptCalls);
+    }
+
+    public void testArrayGetType() throws JSONException {
+        array.put(true);
+        array.getBoolean(0);
+        assertEquals(1, arrayGetCalls);
+    }
+
+    public void testArrayOptType() throws JSONException {
+        array.optBoolean(3);
+        assertEquals(1, arrayOptCalls);
+        assertEquals(1, arrayOptTypeCalls);
+        array.optDouble(3);
+        assertEquals(2, arrayOptCalls);
+        assertEquals(2, arrayOptTypeCalls);
+        array.optInt(3);
+        assertEquals(3, arrayOptCalls);
+        assertEquals(3, arrayOptTypeCalls);
+        array.optLong(3);
+        assertEquals(4, arrayOptCalls);
+        assertEquals(4, arrayOptTypeCalls);
+        array.optString(3);
+        assertEquals(5, arrayOptCalls);
+        assertEquals(5, arrayOptTypeCalls);
+    }
+
+    public void testToJSONObject() throws JSONException {
+        array.put("foo");
+        array.put("baz");
+        array.put("bar");
+        JSONArray values = new JSONArray();
+        values.put(5.5d);
+        values.put(11d);
+        values.put(30);
+        values.toJSONObject(array);
+        assertEquals(3, arrayOptCalls);
+        assertEquals(0, arrayOptTypeCalls);
+    }
+
+}