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