1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.org.json;
18
19import java.math.BigDecimal;
20import java.math.BigInteger;
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.HashMap;
26import java.util.HashSet;
27import java.util.Iterator;
28import java.util.Map;
29import java.util.NoSuchElementException;
30import java.util.Objects;
31import java.util.Set;
32import java.util.TreeMap;
33import junit.framework.TestCase;
34
35import org.json.JSONArray;
36import org.json.JSONException;
37import org.json.JSONObject;
38import org.json.JSONTokener;
39
40/**
41 * This black box test was written without inspecting the non-free org.json sourcecode.
42 */
43public class JSONObjectTest extends TestCase {
44
45    public void testEmptyObject() throws JSONException {
46        JSONObject object = new JSONObject();
47        assertEquals(0, object.length());
48
49        // bogus (but documented) behaviour: returns null rather than the empty object!
50        assertNull(object.names());
51
52        // returns null rather than an empty array!
53        assertNull(object.toJSONArray(new JSONArray()));
54        assertEquals("{}", object.toString());
55        assertEquals("{}", object.toString(5));
56        try {
57            object.get("foo");
58            fail();
59        } catch (JSONException e) {
60        }
61        try {
62            object.getBoolean("foo");
63            fail();
64        } catch (JSONException e) {
65        }
66        try {
67            object.getDouble("foo");
68            fail();
69        } catch (JSONException e) {
70        }
71        try {
72            object.getInt("foo");
73            fail();
74        } catch (JSONException e) {
75        }
76        try {
77            object.getJSONArray("foo");
78            fail();
79        } catch (JSONException e) {
80        }
81        try {
82            object.getJSONObject("foo");
83            fail();
84        } catch (JSONException e) {
85        }
86        try {
87            object.getLong("foo");
88            fail();
89        } catch (JSONException e) {
90        }
91        try {
92            object.getString("foo");
93            fail();
94        } catch (JSONException e) {
95        }
96        assertFalse(object.has("foo"));
97        assertTrue(object.isNull("foo")); // isNull also means "is not present"
98        assertNull(object.opt("foo"));
99        assertEquals(false, object.optBoolean("foo"));
100        assertEquals(true, object.optBoolean("foo", true));
101        assertEquals(Double.NaN, object.optDouble("foo"));
102        assertEquals(5.0, object.optDouble("foo", 5.0));
103        assertEquals(0, object.optInt("foo"));
104        assertEquals(5, object.optInt("foo", 5));
105        assertEquals(null, object.optJSONArray("foo"));
106        assertEquals(null, object.optJSONObject("foo"));
107        assertEquals(0, object.optLong("foo"));
108        assertEquals(Long.MAX_VALUE-1, object.optLong("foo", Long.MAX_VALUE-1));
109        assertEquals("", object.optString("foo")); // empty string is default!
110        assertEquals("bar", object.optString("foo", "bar"));
111        assertNull(object.remove("foo"));
112    }
113
114    public void testEqualsAndHashCode() throws JSONException {
115        JSONObject a = new JSONObject();
116        JSONObject b = new JSONObject();
117
118        // JSON object doesn't override either equals or hashCode (!)
119        assertFalse(a.equals(b));
120        assertEquals(a.hashCode(), System.identityHashCode(a));
121    }
122
123    public void testGet() throws JSONException {
124        JSONObject object = new JSONObject();
125        Object value = new Object();
126        object.put("foo", value);
127        object.put("bar", new Object());
128        object.put("baz", new Object());
129        assertSame(value, object.get("foo"));
130        try {
131            object.get("FOO");
132            fail();
133        } catch (JSONException e) {
134        }
135        try {
136            object.put(null, value);
137            fail();
138        } catch (JSONException e) {
139        }
140        try {
141            object.get(null);
142            fail();
143        } catch (JSONException e) {
144        }
145    }
146
147    public void testPut() throws JSONException {
148        JSONObject object = new JSONObject();
149        assertSame(object, object.put("foo", true));
150        object.put("foo", false);
151        assertEquals(false, object.get("foo"));
152
153        object.put("foo", 5.0d);
154        assertEquals(5.0d, object.get("foo"));
155        object.put("foo", 0);
156        assertEquals(0, object.get("foo"));
157        object.put("bar", Long.MAX_VALUE - 1);
158        assertEquals(Long.MAX_VALUE - 1, object.get("bar"));
159        object.put("baz", "x");
160        assertEquals("x", object.get("baz"));
161        object.put("bar", JSONObject.NULL);
162        assertSame(JSONObject.NULL, object.get("bar"));
163    }
164
165    public void testPutNullRemoves() throws JSONException {
166        JSONObject object = new JSONObject();
167        object.put("foo", "bar");
168        object.put("foo", (Collection) null);
169        assertEquals(0, object.length());
170        assertFalse(object.has("foo"));
171        try {
172            object.get("foo");
173            fail();
174        } catch (JSONException e) {
175        }
176    }
177
178    public void testPutOpt() throws JSONException {
179        JSONObject object = new JSONObject();
180        object.put("foo", "bar");
181        object.putOpt("foo", null);
182        assertEquals("bar", object.get("foo"));
183        object.putOpt(null, null);
184        assertEquals(1, object.length());
185        object.putOpt(null, "bar");
186        assertEquals(1, object.length());
187    }
188
189    public void testPutOptUnsupportedNumbers() throws JSONException {
190        JSONObject object = new JSONObject();
191        try {
192            object.putOpt("foo", Double.NaN);
193            fail();
194        } catch (JSONException e) {
195        }
196        try {
197            object.putOpt("foo", Double.NEGATIVE_INFINITY);
198            fail();
199        } catch (JSONException e) {
200        }
201        try {
202            object.putOpt("foo", Double.POSITIVE_INFINITY);
203            fail();
204        } catch (JSONException e) {
205        }
206    }
207
208    public void testRemove() throws JSONException {
209        JSONObject object = new JSONObject();
210        object.put("foo", "bar");
211        assertEquals(null, object.remove(null));
212        assertEquals(null, object.remove(""));
213        assertEquals(null, object.remove("bar"));
214        assertEquals("bar", object.remove("foo"));
215        assertEquals(null, object.remove("foo"));
216    }
217
218    public void testBooleans() throws JSONException {
219        JSONObject object = new JSONObject();
220        object.put("foo", true);
221        object.put("bar", false);
222        object.put("baz", "true");
223        object.put("quux", "false");
224        assertEquals(4, object.length());
225        assertEquals(true, object.getBoolean("foo"));
226        assertEquals(false, object.getBoolean("bar"));
227        assertEquals(true, object.getBoolean("baz"));
228        assertEquals(false, object.getBoolean("quux"));
229        assertFalse(object.isNull("foo"));
230        assertFalse(object.isNull("quux"));
231        assertTrue(object.has("foo"));
232        assertTrue(object.has("quux"));
233        assertFalse(object.has("missing"));
234        assertEquals(true, object.optBoolean("foo"));
235        assertEquals(false, object.optBoolean("bar"));
236        assertEquals(true, object.optBoolean("baz"));
237        assertEquals(false, object.optBoolean("quux"));
238        assertEquals(false, object.optBoolean("missing"));
239        assertEquals(true, object.optBoolean("foo", true));
240        assertEquals(false, object.optBoolean("bar", true));
241        assertEquals(true, object.optBoolean("baz", true));
242        assertEquals(false, object.optBoolean("quux", true));
243        assertEquals(true, object.optBoolean("missing", true));
244
245        object.put("foo", "truE");
246        object.put("bar", "FALSE");
247        assertEquals(true, object.getBoolean("foo"));
248        assertEquals(false, object.getBoolean("bar"));
249        assertEquals(true, object.optBoolean("foo"));
250        assertEquals(false, object.optBoolean("bar"));
251        assertEquals(true, object.optBoolean("foo", false));
252        assertEquals(false, object.optBoolean("bar", false));
253    }
254
255    // http://code.google.com/p/android/issues/detail?id=16411
256    public void testCoerceStringToBoolean() throws JSONException {
257        JSONObject object = new JSONObject();
258        object.put("foo", "maybe");
259        try {
260            object.getBoolean("foo");
261            fail();
262        } catch (JSONException expected) {
263        }
264        assertEquals(false, object.optBoolean("foo"));
265        assertEquals(true, object.optBoolean("foo", true));
266    }
267
268    public void testNumbers() throws JSONException {
269        JSONObject object = new JSONObject();
270        object.put("foo", Double.MIN_VALUE);
271        object.put("bar", 9223372036854775806L);
272        object.put("baz", Double.MAX_VALUE);
273        object.put("quux", -0d);
274        assertEquals(4, object.length());
275
276        String toString = object.toString();
277        assertTrue(toString, toString.contains("\"foo\":4.9E-324"));
278        assertTrue(toString, toString.contains("\"bar\":9223372036854775806"));
279        assertTrue(toString, toString.contains("\"baz\":1.7976931348623157E308"));
280
281        // toString() and getString() return different values for -0d!
282        assertTrue(toString, toString.contains("\"quux\":-0}") // no trailing decimal point
283                || toString.contains("\"quux\":-0,"));
284
285        assertEquals(Double.MIN_VALUE, object.get("foo"));
286        assertEquals(9223372036854775806L, object.get("bar"));
287        assertEquals(Double.MAX_VALUE, object.get("baz"));
288        assertEquals(-0d, object.get("quux"));
289        assertEquals(Double.MIN_VALUE, object.getDouble("foo"));
290        assertEquals(9.223372036854776E18, object.getDouble("bar"));
291        assertEquals(Double.MAX_VALUE, object.getDouble("baz"));
292        assertEquals(-0d, object.getDouble("quux"));
293        assertEquals(0, object.getLong("foo"));
294        assertEquals(9223372036854775806L, object.getLong("bar"));
295        assertEquals(Long.MAX_VALUE, object.getLong("baz"));
296        assertEquals(0, object.getLong("quux"));
297        assertEquals(0, object.getInt("foo"));
298        assertEquals(-2, object.getInt("bar"));
299        assertEquals(Integer.MAX_VALUE, object.getInt("baz"));
300        assertEquals(0, object.getInt("quux"));
301        assertEquals(Double.MIN_VALUE, object.opt("foo"));
302        assertEquals(9223372036854775806L, object.optLong("bar"));
303        assertEquals(Double.MAX_VALUE, object.optDouble("baz"));
304        assertEquals(0, object.optInt("quux"));
305        assertEquals(Double.MIN_VALUE, object.opt("foo"));
306        assertEquals(9223372036854775806L, object.optLong("bar"));
307        assertEquals(Double.MAX_VALUE, object.optDouble("baz"));
308        assertEquals(0, object.optInt("quux"));
309        assertEquals(Double.MIN_VALUE, object.optDouble("foo", 5.0d));
310        assertEquals(9223372036854775806L, object.optLong("bar", 1L));
311        assertEquals(Long.MAX_VALUE, object.optLong("baz", 1L));
312        assertEquals(0, object.optInt("quux", -1));
313        assertEquals("4.9E-324", object.getString("foo"));
314        assertEquals("9223372036854775806", object.getString("bar"));
315        assertEquals("1.7976931348623157E308", object.getString("baz"));
316        assertEquals("-0.0", object.getString("quux"));
317    }
318
319    public void testFloats() throws JSONException {
320        JSONObject object = new JSONObject();
321        try {
322            object.put("foo", (Float) Float.NaN);
323            fail();
324        } catch (JSONException e) {
325        }
326        try {
327            object.put("foo", (Float) Float.NEGATIVE_INFINITY);
328            fail();
329        } catch (JSONException e) {
330        }
331        try {
332            object.put("foo", (Float) Float.POSITIVE_INFINITY);
333            fail();
334        } catch (JSONException e) {
335        }
336    }
337
338    public void testOtherNumbers() throws JSONException {
339        Number nan = new Number() {
340            public int intValue() {
341                throw new UnsupportedOperationException();
342            }
343            public long longValue() {
344                throw new UnsupportedOperationException();
345            }
346            public float floatValue() {
347                throw new UnsupportedOperationException();
348            }
349            public double doubleValue() {
350                return Double.NaN;
351            }
352            @Override public String toString() {
353                return "x";
354            }
355        };
356
357        JSONObject object = new JSONObject();
358        try {
359            object.put("foo", nan);
360            fail("Object.put() accepted a NaN (via a custom Number class)");
361        } catch (JSONException e) {
362        }
363    }
364
365    public void testForeignObjects() throws JSONException {
366        Object foreign = new Object() {
367            @Override public String toString() {
368                return "x";
369            }
370        };
371
372        // foreign object types are accepted and treated as Strings!
373        JSONObject object = new JSONObject();
374        object.put("foo", foreign);
375        assertEquals("{\"foo\":\"x\"}", object.toString());
376    }
377
378    public void testNullKeys() {
379        try {
380            new JSONObject().put(null, false);
381            fail();
382        } catch (JSONException e) {
383        }
384        try {
385            new JSONObject().put(null, 0.0d);
386            fail();
387        } catch (JSONException e) {
388        }
389        try {
390            new JSONObject().put(null, 5);
391            fail();
392        } catch (JSONException e) {
393        }
394        try {
395            new JSONObject().put(null, 5L);
396            fail();
397        } catch (JSONException e) {
398        }
399        try {
400            new JSONObject().put(null, "foo");
401            fail();
402        } catch (JSONException e) {
403        }
404    }
405
406    public void testStrings() throws JSONException {
407        JSONObject object = new JSONObject();
408        object.put("foo", "true");
409        object.put("bar", "5.5");
410        object.put("baz", "9223372036854775806");
411        object.put("quux", "null");
412        object.put("height", "5\"8' tall");
413
414        assertTrue(object.toString().contains("\"foo\":\"true\""));
415        assertTrue(object.toString().contains("\"bar\":\"5.5\""));
416        assertTrue(object.toString().contains("\"baz\":\"9223372036854775806\""));
417        assertTrue(object.toString().contains("\"quux\":\"null\""));
418        assertTrue(object.toString().contains("\"height\":\"5\\\"8' tall\""));
419
420        assertEquals("true", object.get("foo"));
421        assertEquals("null", object.getString("quux"));
422        assertEquals("5\"8' tall", object.getString("height"));
423        assertEquals("true", object.opt("foo"));
424        assertEquals("5.5", object.optString("bar"));
425        assertEquals("true", object.optString("foo", "x"));
426        assertFalse(object.isNull("foo"));
427
428        assertEquals(true, object.getBoolean("foo"));
429        assertEquals(true, object.optBoolean("foo"));
430        assertEquals(true, object.optBoolean("foo", false));
431        assertEquals(0, object.optInt("foo"));
432        assertEquals(-2, object.optInt("foo", -2));
433
434        assertEquals(5.5d, object.getDouble("bar"));
435        assertEquals(5L, object.getLong("bar"));
436        assertEquals(5, object.getInt("bar"));
437        assertEquals(5, object.optInt("bar", 3));
438
439        // The last digit of the string is a 6 but getLong returns a 7. It's probably parsing as a
440        // double and then converting that to a long. This is consistent with JavaScript.
441        assertEquals(9223372036854775807L, object.getLong("baz"));
442        assertEquals(9.223372036854776E18, object.getDouble("baz"));
443        assertEquals(Integer.MAX_VALUE, object.getInt("baz"));
444
445        assertFalse(object.isNull("quux"));
446        try {
447            object.getDouble("quux");
448            fail();
449        } catch (JSONException e) {
450        }
451        assertEquals(Double.NaN, object.optDouble("quux"));
452        assertEquals(-1.0d, object.optDouble("quux", -1.0d));
453
454        object.put("foo", "TRUE");
455        assertEquals(true, object.getBoolean("foo"));
456    }
457
458    public void testJSONObjects() throws JSONException {
459        JSONObject object = new JSONObject();
460
461        JSONArray a = new JSONArray();
462        JSONObject b = new JSONObject();
463        object.put("foo", a);
464        object.put("bar", b);
465
466        assertSame(a, object.getJSONArray("foo"));
467        assertSame(b, object.getJSONObject("bar"));
468        try {
469            object.getJSONObject("foo");
470            fail();
471        } catch (JSONException e) {
472        }
473        try {
474            object.getJSONArray("bar");
475            fail();
476        } catch (JSONException e) {
477        }
478        assertEquals(a, object.optJSONArray("foo"));
479        assertEquals(b, object.optJSONObject("bar"));
480        assertEquals(null, object.optJSONArray("bar"));
481        assertEquals(null, object.optJSONObject("foo"));
482    }
483
484    public void testNullCoercionToString() throws JSONException {
485        JSONObject object = new JSONObject();
486        object.put("foo", JSONObject.NULL);
487        assertEquals("null", object.getString("foo"));
488    }
489
490    public void testArrayCoercion() throws JSONException {
491        JSONObject object = new JSONObject();
492        object.put("foo", "[true]");
493        try {
494            object.getJSONArray("foo");
495            fail();
496        } catch (JSONException e) {
497        }
498    }
499
500    public void testObjectCoercion() throws JSONException {
501        JSONObject object = new JSONObject();
502        object.put("foo", "{}");
503        try {
504            object.getJSONObject("foo");
505            fail();
506        } catch (JSONException e) {
507        }
508    }
509
510    public void testAccumulateValueChecking() throws JSONException {
511        JSONObject object = new JSONObject();
512        try {
513            object.accumulate("foo", Double.NaN);
514            fail();
515        } catch (JSONException e) {
516        }
517        object.accumulate("foo", 1);
518        try {
519            object.accumulate("foo", Double.NaN);
520            fail();
521        } catch (JSONException e) {
522        }
523        object.accumulate("foo", 2);
524        try {
525            object.accumulate("foo", Double.NaN);
526            fail();
527        } catch (JSONException e) {
528        }
529    }
530
531    public void testToJSONArray() throws JSONException {
532        JSONObject object = new JSONObject();
533        Object value = new Object();
534        object.put("foo", true);
535        object.put("bar", 5.0d);
536        object.put("baz", -0.0d);
537        object.put("quux", value);
538
539        JSONArray names = new JSONArray();
540        names.put("baz");
541        names.put("quux");
542        names.put("foo");
543
544        JSONArray array = object.toJSONArray(names);
545        assertEquals(-0.0d, array.get(0));
546        assertEquals(value, array.get(1));
547        assertEquals(true, array.get(2));
548
549        object.put("foo", false);
550        assertEquals(true, array.get(2));
551    }
552
553    public void testToJSONArrayMissingNames() throws JSONException {
554        JSONObject object = new JSONObject();
555        object.put("foo", true);
556        object.put("bar", 5.0d);
557        object.put("baz", JSONObject.NULL);
558
559        JSONArray names = new JSONArray();
560        names.put("bar");
561        names.put("foo");
562        names.put("quux");
563        names.put("baz");
564
565        JSONArray array = object.toJSONArray(names);
566        assertEquals(4, array.length());
567
568        assertEquals(5.0d, array.get(0));
569        assertEquals(true, array.get(1));
570        try {
571            array.get(2);
572            fail();
573        } catch (JSONException e) {
574        }
575        assertEquals(JSONObject.NULL, array.get(3));
576    }
577
578    public void testToJSONArrayNull() throws JSONException {
579        JSONObject object = new JSONObject();
580        assertEquals(null, object.toJSONArray(null));
581        object.put("foo", 5);
582        try {
583            object.toJSONArray(null);
584        } catch (JSONException e) {
585        }
586    }
587
588    public void testToJSONArrayEndsUpEmpty() throws JSONException {
589        JSONObject object = new JSONObject();
590        object.put("foo", 5);
591        JSONArray array = new JSONArray();
592        array.put("bar");
593        assertEquals(1, object.toJSONArray(array).length());
594    }
595
596    public void testToJSONArrayNonString() throws JSONException {
597        JSONObject object = new JSONObject();
598        object.put("foo", 5);
599        object.put("null", 10);
600        object.put("false", 15);
601
602        JSONArray names = new JSONArray();
603        names.put(JSONObject.NULL);
604        names.put(false);
605        names.put("foo");
606
607        // array elements are converted to strings to do name lookups on the map!
608        JSONArray array = object.toJSONArray(names);
609        assertEquals(3, array.length());
610        assertEquals(10, array.get(0));
611        assertEquals(15, array.get(1));
612        assertEquals(5, array.get(2));
613    }
614
615    public void testPutUnsupportedNumbers() throws JSONException {
616        JSONObject object = new JSONObject();
617        try {
618            object.put("foo", Double.NaN);
619            fail();
620        } catch (JSONException e) {
621        }
622        try {
623            object.put("foo", Double.NEGATIVE_INFINITY);
624            fail();
625        } catch (JSONException e) {
626        }
627        try {
628            object.put("foo", Double.POSITIVE_INFINITY);
629            fail();
630        } catch (JSONException e) {
631        }
632    }
633
634    public void testPutUnsupportedNumbersAsObjects() throws JSONException {
635        JSONObject object = new JSONObject();
636        try {
637            object.put("foo", (Double) Double.NaN);
638            fail();
639        } catch (JSONException e) {
640        }
641        try {
642            object.put("foo", (Double) Double.NEGATIVE_INFINITY);
643            fail();
644        } catch (JSONException e) {
645        }
646        try {
647            object.put("foo", (Double) Double.POSITIVE_INFINITY);
648            fail();
649        } catch (JSONException e) {
650        }
651    }
652
653    /**
654     * Although JSONObject is usually defensive about which numbers it accepts,
655     * it doesn't check inputs in its constructor.
656     */
657    public void testCreateWithUnsupportedNumbers() throws JSONException {
658        Map<String, Object> contents = new HashMap<String, Object>();
659        contents.put("foo", Double.NaN);
660        contents.put("bar", Double.NEGATIVE_INFINITY);
661        contents.put("baz", Double.POSITIVE_INFINITY);
662
663        JSONObject object = new JSONObject(contents);
664        assertEquals(Double.NaN, object.get("foo"));
665        assertEquals(Double.NEGATIVE_INFINITY, object.get("bar"));
666        assertEquals(Double.POSITIVE_INFINITY, object.get("baz"));
667    }
668
669    public void testToStringWithUnsupportedNumbers() {
670        // when the object contains an unsupported number, toString returns null!
671        JSONObject object = new JSONObject(Collections.singletonMap("foo", Double.NaN));
672        assertEquals(null, object.toString());
673    }
674
675    public void testMapConstructorCopiesContents() throws JSONException {
676        Map<String, Object> contents = new HashMap<String, Object>();
677        contents.put("foo", 5);
678        JSONObject object = new JSONObject(contents);
679        contents.put("foo", 10);
680        assertEquals(5, object.get("foo"));
681    }
682
683    public void testMapConstructorWithBogusEntries() {
684        Map<Object, Object> contents = new HashMap<Object, Object>();
685        contents.put(5, 5);
686
687        try {
688            new JSONObject(contents);
689            fail("JSONObject constructor doesn't validate its input!");
690        } catch (Exception e) {
691        }
692    }
693
694    public void testTokenerConstructor() throws JSONException {
695        JSONObject object = new JSONObject(new JSONTokener("{\"foo\": false}"));
696        assertEquals(1, object.length());
697        assertEquals(false, object.get("foo"));
698    }
699
700    public void testTokenerConstructorWrongType() throws JSONException {
701        try {
702            new JSONObject(new JSONTokener("[\"foo\", false]"));
703            fail();
704        } catch (JSONException e) {
705        }
706    }
707
708    public void testTokenerConstructorNull() throws JSONException {
709        try {
710            new JSONObject((JSONTokener) null);
711            fail();
712        } catch (NullPointerException e) {
713        }
714    }
715
716    public void testTokenerConstructorParseFail() {
717        try {
718            new JSONObject(new JSONTokener("{"));
719            fail();
720        } catch (JSONException e) {
721        }
722    }
723
724    public void testStringConstructor() throws JSONException {
725        JSONObject object = new JSONObject("{\"foo\": false}");
726        assertEquals(1, object.length());
727        assertEquals(false, object.get("foo"));
728    }
729
730    public void testStringConstructorWrongType() throws JSONException {
731        try {
732            new JSONObject("[\"foo\", false]");
733            fail();
734        } catch (JSONException e) {
735        }
736    }
737
738    public void testStringConstructorNull() throws JSONException {
739        try {
740            new JSONObject((String) null);
741            fail();
742        } catch (NullPointerException e) {
743        }
744    }
745
746    public void testStringConstructorParseFail() {
747        try {
748            new JSONObject("{");
749            fail();
750        } catch (JSONException e) {
751        }
752    }
753
754    public void testCopyConstructor() throws JSONException {
755        JSONObject source = new JSONObject();
756        source.put("a", JSONObject.NULL);
757        source.put("b", false);
758        source.put("c", 5);
759
760        JSONObject copy = new JSONObject(source, new String[] { "a", "c" });
761        assertEquals(2, copy.length());
762        assertEquals(JSONObject.NULL, copy.get("a"));
763        assertEquals(5, copy.get("c"));
764        assertEquals(null, copy.opt("b"));
765    }
766
767    public void testCopyConstructorMissingName() throws JSONException {
768        JSONObject source = new JSONObject();
769        source.put("a", JSONObject.NULL);
770        source.put("b", false);
771        source.put("c", 5);
772
773        JSONObject copy = new JSONObject(source, new String[]{ "a", "c", "d" });
774        assertEquals(2, copy.length());
775        assertEquals(JSONObject.NULL, copy.get("a"));
776        assertEquals(5, copy.get("c"));
777        assertEquals(0, copy.optInt("b"));
778    }
779
780    public void testAccumulateMutatesInPlace() throws JSONException {
781        JSONObject object = new JSONObject();
782        object.put("foo", 5);
783        object.accumulate("foo", 6);
784        JSONArray array = object.getJSONArray("foo");
785        assertEquals("[5,6]", array.toString());
786        object.accumulate("foo", 7);
787        assertEquals("[5,6,7]", array.toString());
788    }
789
790    public void testAccumulateExistingArray() throws JSONException {
791        JSONArray array = new JSONArray();
792        JSONObject object = new JSONObject();
793        object.put("foo", array);
794        object.accumulate("foo", 5);
795        assertEquals("[5]", array.toString());
796    }
797
798    public void testAccumulatePutArray() throws JSONException {
799        JSONObject object = new JSONObject();
800        object.accumulate("foo", 5);
801        assertEquals("{\"foo\":5}", object.toString());
802        object.accumulate("foo", new JSONArray());
803        assertEquals("{\"foo\":[5,[]]}", object.toString());
804    }
805
806    public void testAccumulateNull() {
807        JSONObject object = new JSONObject();
808        try {
809            object.accumulate(null, 5);
810            fail();
811        } catch (JSONException e) {
812        }
813    }
814
815    public void testEmptyStringKey() throws JSONException {
816        JSONObject object = new JSONObject();
817        object.put("", 5);
818        assertEquals(5, object.get(""));
819        assertEquals("{\"\":5}", object.toString());
820    }
821
822    public void testNullValue() throws JSONException {
823        JSONObject object = new JSONObject();
824        object.put("foo", JSONObject.NULL);
825        object.put("bar", (Collection) null);
826
827        // there are two ways to represent null; each behaves differently!
828        assertTrue(object.has("foo"));
829        assertFalse(object.has("bar"));
830        assertTrue(object.isNull("foo"));
831        assertTrue(object.isNull("bar"));
832    }
833
834    public void testNullValue_equalsAndHashCode() {
835        assertTrue(JSONObject.NULL.equals(null)); // guaranteed by javadoc
836        // not guaranteed by javadoc, but seems like a good idea
837        assertEquals(Objects.hashCode(null), JSONObject.NULL.hashCode());
838    }
839
840    public void testHas() throws JSONException {
841        JSONObject object = new JSONObject();
842        object.put("foo", 5);
843        assertTrue(object.has("foo"));
844        assertFalse(object.has("bar"));
845        assertFalse(object.has(null));
846    }
847
848    public void testOptNull() throws JSONException {
849        JSONObject object = new JSONObject();
850        object.put("foo", "bar");
851        assertEquals(null, object.opt(null));
852        assertEquals(false, object.optBoolean(null));
853        assertEquals(Double.NaN, object.optDouble(null));
854        assertEquals(0, object.optInt(null));
855        assertEquals(0L, object.optLong(null));
856        assertEquals(null, object.optJSONArray(null));
857        assertEquals(null, object.optJSONObject(null));
858        assertEquals("", object.optString(null));
859        assertEquals(true, object.optBoolean(null, true));
860        assertEquals(0.0d, object.optDouble(null, 0.0d));
861        assertEquals(1, object.optInt(null, 1));
862        assertEquals(1L, object.optLong(null, 1L));
863        assertEquals("baz", object.optString(null, "baz"));
864    }
865
866    public void testToStringWithIndentFactor() throws JSONException {
867        JSONObject object = new JSONObject();
868        object.put("foo", new JSONArray(Arrays.asList(5, 6)));
869        object.put("bar", new JSONObject());
870        String foobar = "{\n" +
871                "     \"foo\": [\n" +
872                "          5,\n" +
873                "          6\n" +
874                "     ],\n" +
875                "     \"bar\": {}\n" +
876                "}";
877        String barfoo = "{\n" +
878                "     \"bar\": {},\n" +
879                "     \"foo\": [\n" +
880                "          5,\n" +
881                "          6\n" +
882                "     ]\n" +
883                "}";
884        String string = object.toString(5);
885        assertTrue(string, foobar.equals(string) || barfoo.equals(string));
886    }
887
888    public void testNames() throws JSONException {
889        JSONObject object = new JSONObject();
890        object.put("foo", 5);
891        object.put("bar", 6);
892        object.put("baz", 7);
893        JSONArray array = object.names();
894        assertTrue(array.toString().contains("foo"));
895        assertTrue(array.toString().contains("bar"));
896        assertTrue(array.toString().contains("baz"));
897    }
898
899    public void testKeysEmptyObject() {
900        JSONObject object = new JSONObject();
901        assertFalse(object.keys().hasNext());
902        try {
903            object.keys().next();
904            fail();
905        } catch (NoSuchElementException e) {
906        }
907    }
908
909    public void testKeys() throws JSONException {
910        JSONObject object = new JSONObject();
911        object.put("foo", 5);
912        object.put("bar", 6);
913        object.put("foo", 7);
914
915        @SuppressWarnings("unchecked")
916        Iterator<String> keys = (Iterator<String>) object.keys();
917        Set<String> result = new HashSet<String>();
918        assertTrue(keys.hasNext());
919        result.add(keys.next());
920        assertTrue(keys.hasNext());
921        result.add(keys.next());
922        assertFalse(keys.hasNext());
923        assertEquals(new HashSet<String>(Arrays.asList("foo", "bar")), result);
924
925        try {
926            keys.next();
927            fail();
928        } catch (NoSuchElementException e) {
929        }
930    }
931
932    public void testMutatingKeysMutatesObject() throws JSONException {
933        JSONObject object = new JSONObject();
934        object.put("foo", 5);
935        Iterator keys = object.keys();
936        keys.next();
937        keys.remove();
938        assertEquals(0, object.length());
939    }
940
941    public void testQuote() {
942        // covered by JSONStringerTest.testEscaping
943    }
944
945    public void testQuoteNull() throws JSONException {
946        assertEquals("\"\"", JSONObject.quote(null));
947    }
948
949    public void testNumberToString() throws JSONException {
950        assertEquals("5", JSONObject.numberToString(5));
951        assertEquals("-0", JSONObject.numberToString(-0.0d));
952        assertEquals("9223372036854775806", JSONObject.numberToString(9223372036854775806L));
953        assertEquals("4.9E-324", JSONObject.numberToString(Double.MIN_VALUE));
954        assertEquals("1.7976931348623157E308", JSONObject.numberToString(Double.MAX_VALUE));
955        try {
956            JSONObject.numberToString(Double.NaN);
957            fail();
958        } catch (JSONException e) {
959        }
960        try {
961            JSONObject.numberToString(Double.NEGATIVE_INFINITY);
962            fail();
963        } catch (JSONException e) {
964        }
965        try {
966            JSONObject.numberToString(Double.POSITIVE_INFINITY);
967            fail();
968        } catch (JSONException e) {
969        }
970        assertEquals("0.001", JSONObject.numberToString(new BigDecimal("0.001")));
971        assertEquals("9223372036854775806",
972                JSONObject.numberToString(new BigInteger("9223372036854775806")));
973        try {
974            JSONObject.numberToString(null);
975            fail();
976        } catch (JSONException e) {
977        }
978    }
979
980    public void test_wrap() throws Exception {
981        assertEquals(JSONObject.NULL, JSONObject.wrap(null));
982
983        JSONArray a = new JSONArray();
984        assertEquals(a, JSONObject.wrap(a));
985
986        JSONObject o = new JSONObject();
987        assertEquals(o, JSONObject.wrap(o));
988
989        assertEquals(JSONObject.NULL, JSONObject.wrap(JSONObject.NULL));
990
991        assertTrue(JSONObject.wrap(new byte[0]) instanceof JSONArray);
992        assertTrue(JSONObject.wrap(new ArrayList<String>()) instanceof JSONArray);
993        assertTrue(JSONObject.wrap(new HashMap<String, String>()) instanceof JSONObject);
994        assertTrue(JSONObject.wrap(Double.valueOf(0)) instanceof Double);
995        assertTrue(JSONObject.wrap("hello") instanceof String);
996    }
997
998    // https://code.google.com/p/android/issues/detail?id=55114
999    public void test_toString_listAsMapValue() throws Exception {
1000        ArrayList<Object> list = new ArrayList<Object>();
1001        list.add("a");
1002        list.add(new ArrayList<String>());
1003        Map<String, Object> map = new TreeMap<String, Object>();
1004        map.put("x", "l");
1005        map.put("y", list);
1006        assertEquals("{\"x\":\"l\",\"y\":[\"a\",[]]}", new JSONObject(map).toString());
1007    }
1008
1009    public void testAppendExistingInvalidKey() throws JSONException {
1010        JSONObject object = new JSONObject();
1011        object.put("foo", 5);
1012        try {
1013            object.append("foo", 6);
1014            fail();
1015        } catch (JSONException expected) {
1016        }
1017    }
1018
1019    public void testAppendExistingArray() throws JSONException {
1020        JSONArray array = new JSONArray();
1021        JSONObject object = new JSONObject();
1022        object.put("foo", array);
1023        object.append("foo", 5);
1024        assertEquals("[5]", array.toString());
1025    }
1026
1027    public void testAppendPutArray() throws JSONException {
1028        JSONObject object = new JSONObject();
1029        object.append("foo", 5);
1030        assertEquals("{\"foo\":[5]}", object.toString());
1031        object.append("foo", new JSONArray());
1032        assertEquals("{\"foo\":[5,[]]}", object.toString());
1033    }
1034
1035    public void testAppendNull() {
1036        JSONObject object = new JSONObject();
1037        try {
1038            object.append(null, 5);
1039            fail();
1040        } catch (JSONException e) {
1041        }
1042    }
1043
1044    // https://code.google.com/p/android/issues/detail?id=103641
1045    public void testInvalidUnicodeEscape() {
1046        try {
1047            new JSONObject("{\"q\":\"\\u\", \"r\":[]}");
1048            fail();
1049        } catch (JSONException expected) {
1050        }
1051    }
1052}
1053