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.util.Arrays;
20import java.util.Collection;
21import java.util.Collections;
22import java.util.List;
23import junit.framework.TestCase;
24
25/**
26 * This black box test was written without inspecting the non-free org.json sourcecode.
27 */
28public class JSONArrayTest extends TestCase {
29
30    public void testEmptyArray() throws JSONException {
31        JSONArray array = new JSONArray();
32        assertEquals(0, array.length());
33        assertEquals("", array.join(" AND "));
34        try {
35            array.get(0);
36            fail();
37        } catch (JSONException e) {
38        }
39        try {
40            array.getBoolean(0);
41            fail();
42        } catch (JSONException e) {
43        }
44
45        assertEquals("[]", array.toString());
46        assertEquals("[]", array.toString(4));
47
48        // out of bounds is co-opted with defaulting
49        assertTrue(array.isNull(0));
50        assertNull(array.opt(0));
51        assertFalse(array.optBoolean(0));
52        assertTrue(array.optBoolean(0, true));
53
54        // bogus (but documented) behaviour: returns null rather than an empty object!
55        assertNull(array.toJSONObject(new JSONArray()));
56    }
57
58    public void testEqualsAndHashCode() throws JSONException {
59        JSONArray a = new JSONArray();
60        JSONArray b = new JSONArray();
61        assertTrue(a.equals(b));
62        assertEquals("equals() not consistent with hashCode()", a.hashCode(), b.hashCode());
63
64        a.put(true);
65        a.put(false);
66        b.put(true);
67        b.put(false);
68        assertTrue(a.equals(b));
69        assertEquals(a.hashCode(), b.hashCode());
70
71        b.put(true);
72        assertFalse(a.equals(b));
73        assertTrue(a.hashCode() != b.hashCode());
74    }
75
76    public void testBooleans() throws JSONException {
77        JSONArray array = new JSONArray();
78        array.put(true);
79        array.put(false);
80        array.put(2, false);
81        array.put(3, false);
82        array.put(2, true);
83        assertEquals("[true,false,true,false]", array.toString());
84        assertEquals(4, array.length());
85        assertEquals(Boolean.TRUE, array.get(0));
86        assertEquals(Boolean.FALSE, array.get(1));
87        assertEquals(Boolean.TRUE, array.get(2));
88        assertEquals(Boolean.FALSE, array.get(3));
89        assertFalse(array.isNull(0));
90        assertFalse(array.isNull(1));
91        assertFalse(array.isNull(2));
92        assertFalse(array.isNull(3));
93        assertEquals(true, array.optBoolean(0));
94        assertEquals(false, array.optBoolean(1, true));
95        assertEquals(true, array.optBoolean(2, false));
96        assertEquals(false, array.optBoolean(3));
97        assertEquals("true", array.getString(0));
98        assertEquals("false", array.getString(1));
99        assertEquals("true", array.optString(2));
100        assertEquals("false", array.optString(3, "x"));
101        assertEquals("[\n     true,\n     false,\n     true,\n     false\n]", array.toString(5));
102
103        JSONArray other = new JSONArray();
104        other.put(true);
105        other.put(false);
106        other.put(true);
107        other.put(false);
108        assertTrue(array.equals(other));
109        other.put(true);
110        assertFalse(array.equals(other));
111
112        other = new JSONArray();
113        other.put("true");
114        other.put("false");
115        other.put("truE");
116        other.put("FALSE");
117        assertFalse(array.equals(other));
118        assertFalse(other.equals(array));
119        assertEquals(true, other.getBoolean(0));
120        assertEquals(false, other.optBoolean(1, true));
121        assertEquals(true, other.optBoolean(2));
122        assertEquals(false, other.getBoolean(3));
123    }
124
125    // http://code.google.com/p/android/issues/detail?id=16411
126    public void testCoerceStringToBoolean() throws JSONException {
127        JSONArray array = new JSONArray();
128        array.put("maybe");
129        try {
130            array.getBoolean(0);
131            fail();
132        } catch (JSONException expected) {
133        }
134        assertEquals(false, array.optBoolean(0));
135        assertEquals(true, array.optBoolean(0, true));
136    }
137
138    public void testNulls() throws JSONException {
139        JSONArray array = new JSONArray();
140        array.put(3, (Collection) null);
141        array.put(0, JSONObject.NULL);
142        assertEquals(4, array.length());
143        assertEquals("[null,null,null,null]", array.toString());
144
145        // there's 2 ways to represent null; each behaves differently!
146        assertEquals(JSONObject.NULL, array.get(0));
147        try {
148            array.get(1);
149            fail();
150        } catch (JSONException e) {
151        }
152        try {
153            array.get(2);
154            fail();
155        } catch (JSONException e) {
156        }
157        try {
158            array.get(3);
159            fail();
160        } catch (JSONException e) {
161        }
162        assertEquals(JSONObject.NULL, array.opt(0));
163        assertEquals(null, array.opt(1));
164        assertEquals(null, array.opt(2));
165        assertEquals(null, array.opt(3));
166        assertTrue(array.isNull(0));
167        assertTrue(array.isNull(1));
168        assertTrue(array.isNull(2));
169        assertTrue(array.isNull(3));
170        assertEquals("null", array.optString(0));
171        assertEquals("", array.optString(1));
172        assertEquals("", array.optString(2));
173        assertEquals("", array.optString(3));
174    }
175
176    /**
177     * Our behaviour is questioned by this bug:
178     * http://code.google.com/p/android/issues/detail?id=7257
179     */
180    public void testParseNullYieldsJSONObjectNull() throws JSONException {
181        JSONArray array = new JSONArray("[\"null\",null]");
182        array.put((Collection) null);
183        assertEquals("null", array.get(0));
184        assertEquals(JSONObject.NULL, array.get(1));
185        try {
186            array.get(2);
187            fail();
188        } catch (JSONException e) {
189        }
190        assertEquals("null", array.getString(0));
191        assertEquals("null", array.getString(1));
192        try {
193            array.getString(2);
194            fail();
195        } catch (JSONException e) {
196        }
197    }
198
199    public void testNumbers() throws JSONException {
200        JSONArray array = new JSONArray();
201        array.put(Double.MIN_VALUE);
202        array.put(9223372036854775806L);
203        array.put(Double.MAX_VALUE);
204        array.put(-0d);
205        assertEquals(4, array.length());
206
207        // toString() and getString(int) return different values for -0d
208        assertEquals("[4.9E-324,9223372036854775806,1.7976931348623157E308,-0]", array.toString());
209
210        assertEquals(Double.MIN_VALUE, array.get(0));
211        assertEquals(9223372036854775806L, array.get(1));
212        assertEquals(Double.MAX_VALUE, array.get(2));
213        assertEquals(-0d, array.get(3));
214        assertEquals(Double.MIN_VALUE, array.getDouble(0));
215        assertEquals(9.223372036854776E18, array.getDouble(1));
216        assertEquals(Double.MAX_VALUE, array.getDouble(2));
217        assertEquals(-0d, array.getDouble(3));
218        assertEquals(0, array.getLong(0));
219        assertEquals(9223372036854775806L, array.getLong(1));
220        assertEquals(Long.MAX_VALUE, array.getLong(2));
221        assertEquals(0, array.getLong(3));
222        assertEquals(0, array.getInt(0));
223        assertEquals(-2, array.getInt(1));
224        assertEquals(Integer.MAX_VALUE, array.getInt(2));
225        assertEquals(0, array.getInt(3));
226        assertEquals(Double.MIN_VALUE, array.opt(0));
227        assertEquals(Double.MIN_VALUE, array.optDouble(0));
228        assertEquals(0, array.optLong(0, 1L));
229        assertEquals(0, array.optInt(0, 1));
230        assertEquals("4.9E-324", array.getString(0));
231        assertEquals("9223372036854775806", array.getString(1));
232        assertEquals("1.7976931348623157E308", array.getString(2));
233        assertEquals("-0.0", array.getString(3));
234
235        JSONArray other = new JSONArray();
236        other.put(Double.MIN_VALUE);
237        other.put(9223372036854775806L);
238        other.put(Double.MAX_VALUE);
239        other.put(-0d);
240        assertTrue(array.equals(other));
241        other.put(0, 0L);
242        assertFalse(array.equals(other));
243    }
244
245    public void testStrings() throws JSONException {
246        JSONArray array = new JSONArray();
247        array.put("true");
248        array.put("5.5");
249        array.put("9223372036854775806");
250        array.put("null");
251        array.put("5\"8' tall");
252        assertEquals(5, array.length());
253        assertEquals("[\"true\",\"5.5\",\"9223372036854775806\",\"null\",\"5\\\"8' tall\"]",
254                array.toString());
255
256        // although the documentation doesn't mention it, join() escapes text and wraps
257        // strings in quotes
258        assertEquals("\"true\" \"5.5\" \"9223372036854775806\" \"null\" \"5\\\"8' tall\"",
259                array.join(" "));
260
261        assertEquals("true", array.get(0));
262        assertEquals("null", array.getString(3));
263        assertEquals("5\"8' tall", array.getString(4));
264        assertEquals("true", array.opt(0));
265        assertEquals("5.5", array.optString(1));
266        assertEquals("9223372036854775806", array.optString(2, null));
267        assertEquals("null", array.optString(3, "-1"));
268        assertFalse(array.isNull(0));
269        assertFalse(array.isNull(3));
270
271        assertEquals(true, array.getBoolean(0));
272        assertEquals(true, array.optBoolean(0));
273        assertEquals(true, array.optBoolean(0, false));
274        assertEquals(0, array.optInt(0));
275        assertEquals(-2, array.optInt(0, -2));
276
277        assertEquals(5.5d, array.getDouble(1));
278        assertEquals(5L, array.getLong(1));
279        assertEquals(5, array.getInt(1));
280        assertEquals(5, array.optInt(1, 3));
281
282        // The last digit of the string is a 6 but getLong returns a 7. It's probably parsing as a
283        // double and then converting that to a long. This is consistent with JavaScript.
284        assertEquals(9223372036854775807L, array.getLong(2));
285        assertEquals(9.223372036854776E18, array.getDouble(2));
286        assertEquals(Integer.MAX_VALUE, array.getInt(2));
287
288        assertFalse(array.isNull(3));
289        try {
290            array.getDouble(3);
291            fail();
292        } catch (JSONException e) {
293        }
294        assertEquals(Double.NaN, array.optDouble(3));
295        assertEquals(-1.0d, array.optDouble(3, -1.0d));
296    }
297
298    public void testJoin() throws JSONException {
299        JSONArray array = new JSONArray();
300        array.put((Collection) null);
301        assertEquals("null", array.join(" & "));
302        array.put("\"");
303        assertEquals("null & \"\\\"\"", array.join(" & "));
304        array.put(5);
305        assertEquals("null & \"\\\"\" & 5", array.join(" & "));
306        array.put(true);
307        assertEquals("null & \"\\\"\" & 5 & true", array.join(" & "));
308        array.put(new JSONArray(Arrays.asList(true, false)));
309        assertEquals("null & \"\\\"\" & 5 & true & [true,false]", array.join(" & "));
310        array.put(new JSONObject(Collections.singletonMap("x", 6)));
311        assertEquals("null & \"\\\"\" & 5 & true & [true,false] & {\"x\":6}", array.join(" & "));
312    }
313
314    public void testJoinWithNull() throws JSONException {
315        JSONArray array = new JSONArray(Arrays.asList(5, 6));
316        assertEquals("5null6", array.join(null));
317    }
318
319    public void testJoinWithSpecialCharacters() throws JSONException {
320        JSONArray array = new JSONArray(Arrays.asList(5, 6));
321        assertEquals("5\"6", array.join("\""));
322    }
323
324    public void testToJSONObject() throws JSONException {
325        JSONArray keys = new JSONArray();
326        keys.put("a");
327        keys.put("b");
328
329        JSONArray values = new JSONArray();
330        values.put(5.5d);
331        values.put(false);
332
333        JSONObject object = values.toJSONObject(keys);
334        assertEquals(5.5d, object.get("a"));
335        assertEquals(false, object.get("b"));
336
337        keys.put(0, "a");
338        values.put(0, 11.0d);
339        assertEquals(5.5d, object.get("a"));
340    }
341
342    public void testToJSONObjectWithNulls() throws JSONException {
343        JSONArray keys = new JSONArray();
344        keys.put("a");
345        keys.put("b");
346
347        JSONArray values = new JSONArray();
348        values.put(5.5d);
349        values.put((Collection) null);
350
351        // null values are stripped!
352        JSONObject object = values.toJSONObject(keys);
353        assertEquals(1, object.length());
354        assertFalse(object.has("b"));
355        assertEquals("{\"a\":5.5}", object.toString());
356    }
357
358    public void testToJSONObjectMoreNamesThanValues() throws JSONException {
359        JSONArray keys = new JSONArray();
360        keys.put("a");
361        keys.put("b");
362        JSONArray values = new JSONArray();
363        values.put(5.5d);
364        JSONObject object = values.toJSONObject(keys);
365        assertEquals(1, object.length());
366        assertEquals(5.5d, object.get("a"));
367    }
368
369    public void testToJSONObjectMoreValuesThanNames() throws JSONException {
370        JSONArray keys = new JSONArray();
371        keys.put("a");
372        JSONArray values = new JSONArray();
373        values.put(5.5d);
374        values.put(11.0d);
375        JSONObject object = values.toJSONObject(keys);
376        assertEquals(1, object.length());
377        assertEquals(5.5d, object.get("a"));
378    }
379
380    public void testToJSONObjectNullKey() throws JSONException {
381        JSONArray keys = new JSONArray();
382        keys.put(JSONObject.NULL);
383        JSONArray values = new JSONArray();
384        values.put(5.5d);
385        JSONObject object = values.toJSONObject(keys);
386        assertEquals(1, object.length());
387        assertEquals(5.5d, object.get("null"));
388    }
389
390    public void testPutUnsupportedNumbers() throws JSONException {
391        JSONArray array = new JSONArray();
392
393        try {
394            array.put(Double.NaN);
395            fail();
396        } catch (JSONException e) {
397        }
398        try {
399            array.put(0, Double.NEGATIVE_INFINITY);
400            fail();
401        } catch (JSONException e) {
402        }
403        try {
404            array.put(0, Double.POSITIVE_INFINITY);
405            fail();
406        } catch (JSONException e) {
407        }
408    }
409
410    public void testPutUnsupportedNumbersAsObject() throws JSONException {
411        JSONArray array = new JSONArray();
412        array.put(Double.valueOf(Double.NaN));
413        array.put(Double.valueOf(Double.NEGATIVE_INFINITY));
414        array.put(Double.valueOf(Double.POSITIVE_INFINITY));
415        assertEquals(null, array.toString());
416    }
417
418    /**
419     * Although JSONArray is usually defensive about which numbers it accepts,
420     * it doesn't check inputs in its constructor.
421     */
422    public void testCreateWithUnsupportedNumbers() throws JSONException {
423        JSONArray array = new JSONArray(Arrays.asList(5.5, Double.NaN));
424        assertEquals(2, array.length());
425        assertEquals(5.5, array.getDouble(0));
426        assertEquals(Double.NaN, array.getDouble(1));
427    }
428
429    public void testToStringWithUnsupportedNumbers() throws JSONException {
430        // when the array contains an unsupported number, toString returns null!
431        JSONArray array = new JSONArray(Arrays.asList(5.5, Double.NaN));
432        assertNull(array.toString());
433    }
434
435    public void testListConstructorCopiesContents() throws JSONException {
436        List<Object> contents = Arrays.<Object>asList(5);
437        JSONArray array = new JSONArray(contents);
438        contents.set(0, 10);
439        assertEquals(5, array.get(0));
440    }
441
442    public void testTokenerConstructor() throws JSONException {
443        JSONArray object = new JSONArray(new JSONTokener("[false]"));
444        assertEquals(1, object.length());
445        assertEquals(false, object.get(0));
446    }
447
448    public void testTokenerConstructorWrongType() throws JSONException {
449        try {
450            new JSONArray(new JSONTokener("{\"foo\": false}"));
451            fail();
452        } catch (JSONException e) {
453        }
454    }
455
456    public void testTokenerConstructorNull() throws JSONException {
457        try {
458            new JSONArray((JSONTokener) null);
459            fail();
460        } catch (NullPointerException e) {
461        }
462    }
463
464    public void testTokenerConstructorParseFail() {
465        try {
466            new JSONArray(new JSONTokener("["));
467            fail();
468        } catch (JSONException e) {
469        } catch (StackOverflowError e) {
470            fail("Stack overflowed on input: \"[\"");
471        }
472    }
473
474    public void testStringConstructor() throws JSONException {
475        JSONArray object = new JSONArray("[false]");
476        assertEquals(1, object.length());
477        assertEquals(false, object.get(0));
478    }
479
480    public void testStringConstructorWrongType() throws JSONException {
481        try {
482            new JSONArray("{\"foo\": false}");
483            fail();
484        } catch (JSONException e) {
485        }
486    }
487
488    public void testStringConstructorNull() throws JSONException {
489        try {
490            new JSONArray((String) null);
491            fail();
492        } catch (NullPointerException e) {
493        }
494    }
495
496    public void testStringConstructorParseFail() {
497        try {
498            new JSONArray("[");
499            fail();
500        } catch (JSONException e) {
501        } catch (StackOverflowError e) {
502            fail("Stack overflowed on input: \"[\"");
503        }
504    }
505
506    public void testCreate() throws JSONException {
507        JSONArray array = new JSONArray(Arrays.asList(5.5, true));
508        assertEquals(2, array.length());
509        assertEquals(5.5, array.getDouble(0));
510        assertEquals(true, array.get(1));
511        assertEquals("[5.5,true]", array.toString());
512    }
513
514    public void testAccessOutOfBounds() throws JSONException {
515        JSONArray array = new JSONArray();
516        array.put("foo");
517        assertEquals(null, array.opt(3));
518        assertEquals(null, array.opt(-3));
519        assertEquals("", array.optString(3));
520        assertEquals("", array.optString(-3));
521        try {
522            array.get(3);
523            fail();
524        } catch (JSONException e) {
525        }
526        try {
527            array.get(-3);
528            fail();
529        } catch (JSONException e) {
530        }
531        try {
532            array.getString(3);
533            fail();
534        } catch (JSONException e) {
535        }
536        try {
537            array.getString(-3);
538            fail();
539        } catch (JSONException e) {
540        }
541    }
542
543    public void test_remove() throws Exception {
544        JSONArray a = new JSONArray();
545        assertEquals(null, a.remove(-1));
546        assertEquals(null, a.remove(0));
547
548        a.put("hello");
549        assertEquals(null, a.remove(-1));
550        assertEquals(null, a.remove(1));
551        assertEquals("hello", a.remove(0));
552        assertEquals(null, a.remove(0));
553    }
554
555    enum MyEnum { A, B, C; }
556
557    // https://code.google.com/p/android/issues/detail?id=62539
558    public void testEnums() throws Exception {
559        // This works because it's in java.* and any class in there falls back to toString.
560        JSONArray a1 = new JSONArray(java.lang.annotation.RetentionPolicy.values());
561        assertEquals("[\"SOURCE\",\"CLASS\",\"RUNTIME\"]", a1.toString());
562
563        // This doesn't because it's not.
564        JSONArray a2 = new JSONArray(MyEnum.values());
565        assertEquals("[null,null,null]", a2.toString());
566    }
567}
568