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