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