• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 android.util.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertSame;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import android.os.Bundle;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.util.ArrayMap;
29 import android.util.Log;
30 
31 import androidx.test.filters.SmallTest;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 import java.lang.reflect.InvocationTargetException;
38 import java.lang.reflect.Method;
39 import java.util.AbstractMap;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.Collections;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.Iterator;
46 import java.util.Map;
47 import java.util.NoSuchElementException;
48 import java.util.Set;
49 
50 @SmallTest
51 @RunWith(AndroidJUnit4.class)
52 public class ArrayMapTest {
53     static final boolean DEBUG = false;
54 
55     static final int OP_ADD = 1;
56     static final int OP_REM = 2;
57 
58     static int[] OPS = new int[] {
59             OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
60             OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
61             OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
62             OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
63 
64             OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
65             OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
66 
67             OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
68             OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
69 
70             OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
71             OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
72 
73             OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
74             OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
75             OP_ADD, OP_ADD, OP_ADD,
76             OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
77             OP_REM, OP_REM, OP_REM,
78             OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
79     };
80 
81     static int[] KEYS = new int[] {
82             // General adding and removing.
83               -1,   1900,    600,    200,   1200,   1500,   1800,    100,   1900,
84             2100,    300,    800,    600,   1100,   1300,   2000,   1000,   1400,
85              600,     -1,   1900,    600,    300,   2100,    200,    800,    800,
86             1800,   1500,   1300,   1100,   2000,   1400,   1000,   1200,   1900,
87 
88             // Shrink when removing item from end.
89              100,    200,    300,    400,    500,    600,    700,    800,    900,
90              900,    800,    700,    600,    500,    400,    300,    200,    100,
91 
92             // Shrink when removing item from middle.
93              100,    200,    300,    400,    500,    600,    700,    800,    900,
94              900,    800,    700,    600,    500,    400,    200,    300,    100,
95 
96             // Shrink when removing item from front.
97              100,    200,    300,    400,    500,    600,    700,    800,    900,
98              900,    800,    700,    600,    500,    400,    100,    200,    300,
99 
100             // Test hash collisions.
101              105,    106,    108,    104,    102,    102,    107,      5,    205,
102                4,    202,    203,      3,      5,    101,    109,    200,    201,
103                0,     -1,    100,
104              106,    108,    104,    102,    103,    105,    107,    101,    109,
105               -1,    100,      0,
106                4,      5,      3,      5,    200,    203,    202,    201,    205,
107     };
108 
109     public static class ControlledHash implements Parcelable {
110         final int mValue;
111 
ControlledHash(int value)112         ControlledHash(int value) {
113             mValue = value;
114         }
115 
116         @Override
equals(Object o)117         public final boolean equals(Object o) {
118             if (o == null) {
119                 return false;
120             }
121             return mValue == ((ControlledHash)o).mValue;
122         }
123 
124         @Override
hashCode()125         public final int hashCode() {
126             return mValue/100;
127         }
128 
129         @Override
toString()130         public final String toString() {
131             return Integer.toString(mValue);
132         }
133 
134         @Override
describeContents()135         public int describeContents() {
136             return 0;
137         }
138 
139         @Override
writeToParcel(Parcel dest, int flags)140         public void writeToParcel(Parcel dest, int flags) {
141             dest.writeInt(mValue);
142         }
143 
144         public static final Parcelable.Creator<ControlledHash> CREATOR
145                 = new Parcelable.Creator<ControlledHash>() {
146             public ControlledHash createFromParcel(Parcel in) {
147                 return new ControlledHash(in.readInt());
148             }
149 
150             public ControlledHash[] newArray(int size) {
151                 return new ControlledHash[size];
152             }
153         };
154     }
155 
compare(Object v1, Object v2)156     private static boolean compare(Object v1, Object v2) {
157         if (v1 == null) {
158             return v2 == null;
159         }
160         if (v2 == null) {
161             return false;
162         }
163         return v1.equals(v2);
164     }
165 
compareMaps(HashMap map, ArrayMap array)166     private static void compareMaps(HashMap map, ArrayMap array) {
167         if (map.size() != array.size()) {
168             fail("Bad size: expected " + map.size() + ", got " + array.size());
169         }
170 
171         Set<Map.Entry> mapSet = map.entrySet();
172         for (Map.Entry entry : mapSet) {
173             Object expValue = entry.getValue();
174             Object gotValue = array.get(entry.getKey());
175             if (!compare(expValue, gotValue)) {
176                 fail("Bad value: expected " + expValue + ", got " + gotValue
177                         + " at key " + entry.getKey());
178             }
179         }
180 
181         for (int i=0; i<array.size(); i++) {
182             Object gotValue = array.valueAt(i);
183             Object key = array.keyAt(i);
184             Object expValue = map.get(key);
185             if (!compare(expValue, gotValue)) {
186                 fail("Bad value: expected " + expValue + ", got " + gotValue
187                         + " at key " + key);
188             }
189         }
190 
191         if (map.entrySet().hashCode() != array.entrySet().hashCode()) {
192             fail("Entry set hash codes differ: map=0x"
193                     + Integer.toHexString(map.entrySet().hashCode()) + " array=0x"
194                     + Integer.toHexString(array.entrySet().hashCode()));
195         }
196 
197         if (!map.entrySet().equals(array.entrySet())) {
198             fail("Failed calling equals on map entry set against array set");
199         }
200 
201         if (!array.entrySet().equals(map.entrySet())) {
202             fail("Failed calling equals on array entry set against map set");
203         }
204 
205         if (map.keySet().hashCode() != array.keySet().hashCode()) {
206             fail("Key set hash codes differ: map=0x"
207                     + Integer.toHexString(map.keySet().hashCode()) + " array=0x"
208                     + Integer.toHexString(array.keySet().hashCode()));
209         }
210 
211         if (!map.keySet().equals(array.keySet())) {
212             fail("Failed calling equals on map key set against array set");
213         }
214 
215         if (!array.keySet().equals(map.keySet())) {
216             fail("Failed calling equals on array key set against map set");
217         }
218 
219         if (!map.keySet().containsAll(array.keySet())) {
220             fail("Failed map key set contains all of array key set");
221         }
222 
223         if (!array.keySet().containsAll(map.keySet())) {
224             fail("Failed array key set contains all of map key set");
225         }
226 
227         if (!array.containsAll(map.keySet())) {
228             fail("Failed array contains all of map key set");
229         }
230 
231         if (!map.entrySet().containsAll(array.entrySet())) {
232             fail("Failed map entry set contains all of array entry set");
233         }
234 
235         if (!array.entrySet().containsAll(map.entrySet())) {
236             fail("Failed array entry set contains all of map entry set");
237         }
238     }
239 
validateArrayMap(ArrayMap array)240     private static void validateArrayMap(ArrayMap array) {
241         Set<Map.Entry> entrySet = array.entrySet();
242         int index=0;
243         Iterator<Map.Entry> entryIt = entrySet.iterator();
244         while (entryIt.hasNext()) {
245             Map.Entry entry = entryIt.next();
246             Object value = entry.getKey();
247             Object realValue = array.keyAt(index);
248             if (!compare(realValue, value)) {
249                 fail("Bad array map entry set: expected key " + realValue
250                         + ", got " + value + " at index " + index);
251             }
252             value = entry.getValue();
253             realValue = array.valueAt(index);
254             if (!compare(realValue, value)) {
255                 fail("Bad array map entry set: expected value " + realValue
256                         + ", got " + value + " at index " + index);
257             }
258             index++;
259         }
260 
261         index = 0;
262         Set keySet = array.keySet();
263         Iterator keyIt = keySet.iterator();
264         while (keyIt.hasNext()) {
265             Object value = keyIt.next();
266             Object realValue = array.keyAt(index);
267             if (!compare(realValue, value)) {
268                 fail("Bad array map key set: expected key " + realValue
269                         + ", got " + value + " at index " + index);
270             }
271             index++;
272         }
273 
274         index = 0;
275         Collection valueCol = array.values();
276         Iterator valueIt = valueCol.iterator();
277         while (valueIt.hasNext()) {
278             Object value = valueIt.next();
279             Object realValue = array.valueAt(index);
280             if (!compare(realValue, value)) {
281                 fail("Bad array map value col: expected value " + realValue
282                         + ", got " + value + " at index " + index);
283             }
284             index++;
285         }
286     }
287 
compareBundles(Bundle bundle1, Bundle bundle2)288     private static void compareBundles(Bundle bundle1, Bundle bundle2) {
289         Set<String> keySet1 = bundle1.keySet();
290         Iterator<String> iterator1 = keySet1.iterator();
291         while (iterator1.hasNext()) {
292             String key = iterator1.next();
293             int value1 = bundle1.getInt(key);
294             if (bundle2.get(key) == null) {
295                 fail("Bad Bundle: bundle2 didn't have expected key " + key);
296             }
297             int value2 = bundle2.getInt(key);
298             if (value1 != value2) {
299                 fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2);
300             }
301         }
302         Set<String> keySet2 = bundle2.keySet();
303         Iterator<String> iterator2 = keySet2.iterator();
304         while (iterator2.hasNext()) {
305             String key = iterator2.next();
306             if (bundle1.get(key) == null) {
307                 fail("Bad Bundle: bundle1 didn't have expected key " + key);
308             }
309             int value1 = bundle1.getInt(key);
310             int value2 = bundle2.getInt(key);
311             if (value1 != value2) {
312                 fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2);
313             }
314         }
315     }
316 
dump(Map map, ArrayMap array)317     private static void dump(Map map, ArrayMap array) {
318         Log.e("test", "HashMap of " + map.size() + " entries:");
319         Set<Map.Entry> mapSet = map.entrySet();
320         for (Map.Entry entry : mapSet) {
321             Log.e("test", "    " + entry.getKey() + " -> " + entry.getValue());
322         }
323         Log.e("test", "ArrayMap of " + array.size() + " entries:");
324         for (int i=0; i<array.size(); i++) {
325             Log.e("test", "    " + array.keyAt(i) + " -> " + array.valueAt(i));
326         }
327     }
328 
dump(ArrayMap map1, ArrayMap map2)329     private static void dump(ArrayMap map1, ArrayMap map2) {
330         Log.e("test", "ArrayMap of " + map1.size() + " entries:");
331         for (int i=0; i<map1.size(); i++) {
332             Log.e("test", "    " + map1.keyAt(i) + " -> " + map1.valueAt(i));
333         }
334         Log.e("test", "ArrayMap of " + map2.size() + " entries:");
335         for (int i=0; i<map2.size(); i++) {
336             Log.e("test", "    " + map2.keyAt(i) + " -> " + map2.valueAt(i));
337         }
338     }
339 
dump(Bundle bundle1, Bundle bundle2)340     private static void dump(Bundle bundle1, Bundle bundle2) {
341         Log.e("test", "First Bundle of " + bundle1.size() + " entries:");
342         Set<String> keys1 = bundle1.keySet();
343         for (String key : keys1) {
344             Log.e("test", "    " + key + " -> " + bundle1.get(key));
345         }
346         Log.e("test", "Second Bundle of " + bundle2.size() + " entries:");
347         Set<String> keys2 = bundle2.keySet();
348         for (String key : keys2) {
349             Log.e("test", "    " + key + " -> " + bundle2.get(key));
350         }
351     }
352 
353     @Test
testBasicArrayMap()354     public void testBasicArrayMap() {
355         HashMap<ControlledHash, Integer> hashMap = new HashMap<>();
356         ArrayMap<ControlledHash, Integer> arrayMap = new ArrayMap<>();
357         Bundle bundle = new Bundle();
358 
359         for (int i=0; i<OPS.length; i++) {
360             Integer oldHash;
361             Integer oldArray;
362             ControlledHash key = KEYS[i] < 0 ? null : new ControlledHash(KEYS[i]);
363             String strKey = KEYS[i] < 0 ? null : Integer.toString(KEYS[i]);
364             switch (OPS[i]) {
365                 case OP_ADD:
366                     if (DEBUG) Log.i("test", "Adding key: " + key);
367                     oldHash = hashMap.put(key, i);
368                     oldArray = arrayMap.put(key, i);
369                     bundle.putInt(strKey, i);
370                     break;
371                 case OP_REM:
372                     if (DEBUG) Log.i("test", "Removing key: " + key);
373                     oldHash = hashMap.remove(key);
374                     oldArray = arrayMap.remove(key);
375                     bundle.remove(strKey);
376                     break;
377                 default:
378                     fail("Bad operation " + OPS[i] + " @ " + i);
379                     return;
380             }
381             if (!compare(oldHash, oldArray)) {
382                 String msg = "Bad result: expected " + oldHash + ", got " + oldArray;
383                 Log.e("test", msg);
384                 dump(hashMap, arrayMap);
385                 fail(msg);
386             }
387             try {
388                 validateArrayMap(arrayMap);
389             } catch (Throwable e) {
390                 Log.e("test", e.getMessage());
391                 dump(hashMap, arrayMap);
392                 throw e;
393             }
394             try {
395                 compareMaps(hashMap, arrayMap);
396             } catch (Throwable e) {
397                 Log.e("test", e.getMessage());
398                 dump(hashMap, arrayMap);
399                 throw e;
400             }
401             Parcel parcel = Parcel.obtain();
402             bundle.writeToParcel(parcel, 0);
403             parcel.setDataPosition(0);
404             Bundle bundle2 = parcel.readBundle();
405             try {
406                 compareBundles(bundle, bundle2);
407             } catch (Throwable e) {
408                 Log.e("test", e.getMessage());
409                 dump(bundle, bundle2);
410                 throw e;
411             }
412         }
413 
414         arrayMap.put(new ControlledHash(50000), 100);
415         ControlledHash lookup = new ControlledHash(50000);
416         Iterator<ControlledHash> it = arrayMap.keySet().iterator();
417         while (it.hasNext()) {
418             if (it.next().equals(lookup)) {
419                 it.remove();
420             }
421         }
422         if (arrayMap.containsKey(lookup)) {
423             String msg = "Bad map iterator: didn't remove test key";
424             Log.e("test", msg);
425             dump(hashMap, arrayMap);
426             fail(msg);
427         }
428     }
429 
430     @Test
431     public void testCopyArrayMap() {
432         // map copy constructor test
433         ArrayMap newMap = new ArrayMap<Integer, String>();
434         for (int i = 0; i < 10; ++i) {
435             newMap.put(i, String.valueOf(i));
436         }
437         ArrayMap mapCopy = new ArrayMap(newMap);
438         if (!compare(mapCopy, newMap)) {
439             String msg = "ArrayMap copy constructor failure: expected " +
440                     newMap + ", got " + mapCopy;
441             Log.e("test", msg);
442             dump(newMap, mapCopy);
443             fail(msg);
444             return;
445         }
446     }
447 
448     @Test
449     public void testEqualsArrayMap() {
450         ArrayMap<Integer, String> map1 = new ArrayMap<>();
451         ArrayMap<Integer, String> map2 = new ArrayMap<>();
452         HashMap<Integer, String> map3 = new HashMap<>();
453         if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
454             fail("ArrayMap equals failure for empty maps " + map1 + ", " +
455                     map2 + ", " + map3);
456         }
457 
458         for (int i = 0; i < 10; ++i) {
459             String value = String.valueOf(i);
460             map1.put(i, value);
461             map2.put(i, value);
462             map3.put(i, value);
463         }
464         if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
465             fail("ArrayMap equals failure for populated maps " + map1 + ", " +
466                     map2 + ", " + map3);
467         }
468 
469         map1.remove(0);
470         if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
471             fail("ArrayMap equals failure for map size " + map1 + ", " +
472                     map2 + ", " + map3);
473         }
474 
475         map1.put(0, "-1");
476         if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
477             fail("ArrayMap equals failure for map contents " + map1 + ", " +
478                     map2 + ", " + map3);
479         }
480     }
481 
482     /**
483      * Test creating a malformed array map with duplicated keys and that we will catch this
484      * when unparcelling.
485      */
486     @Test
487     public void testDuplicateKeys() throws NoSuchMethodException,
488             InvocationTargetException, IllegalAccessException, NoSuchFieldException {
489         ArrayMap<String, Object> map1 = new ArrayMap(2);
490 
491         Method appendMethod = ArrayMap.class.getMethod("append", Object.class, Object.class);
492         appendMethod.invoke(map1, Integer.toString(100000), "foo");
493         appendMethod.invoke(map1, Integer.toString(100000), "bar");
494 
495         // Now parcel/unparcel, and verify we get the expected error.
496         Parcel parcel = Parcel.obtain();
497         Method writeArrayMapMethod = Parcel.class.getMethod("writeArrayMap", ArrayMap.class);
498         writeArrayMapMethod.invoke(parcel, map1);
499         parcel.setDataPosition(0);
500         ArrayMap<String, Object> map2 = new ArrayMap(2);
501 
502         try {
503             Parcel.class.getMethod("readArrayMap", ArrayMap.class, ClassLoader.class).invoke(
504                     parcel, map2, null);
505         } catch (InvocationTargetException e) {
506             Throwable cause = e.getCause();
507             if (cause instanceof IllegalArgumentException) {
508                 // Good!
509                 return;
510             }
511             throw e;
512         }
513 
514         String msg = "Didn't throw expected IllegalArgumentException";
515         Log.e("test", msg);
516         dump(map1, map2);
517         fail(msg);
518     }
519 
520     private static void checkEntrySetToArray(ArrayMap<?, ?> testMap) {
521         try {
522             testMap.entrySet().toArray();
523             fail();
524         } catch (UnsupportedOperationException expected) {}
525 
526         try {
527             Map.Entry<?, ?>[] entries = new Map.Entry[20];
528             testMap.entrySet().toArray(entries);
529             fail();
530         } catch (UnsupportedOperationException expected) {}
531     }
532 
533     // http://b/32294038, Test ArrayMap.entrySet().toArray()
534     @Test
535     public void testEntrySetArray() {
536         // Create
537         ArrayMap<Integer, String> testMap = new ArrayMap<>();
538 
539         // Test empty
540         checkEntrySetToArray(testMap);
541 
542         // Test non-empty
543         for (int i = 0; i < 10; ++i) {
544             testMap.put(i, String.valueOf(i));
545         }
546         checkEntrySetToArray(testMap);
547     }
548 
549     @Test
550     public void testCanNotIteratePastEnd_entrySetIterator() {
551         Map<String, String> map = new ArrayMap<>();
552         map.put("key 1", "value 1");
553         map.put("key 2", "value 2");
554         Set<Map.Entry<String, String>> expectedEntriesToIterate = new HashSet<>(Arrays.asList(
555                 entryOf("key 1", "value 1"),
556                 entryOf("key 2", "value 2")
557         ));
558         Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
559 
560         // Assert iteration over the expected two entries in any order
561         assertTrue(iterator.hasNext());
562         Map.Entry<String, String> firstEntry = copyOf(iterator.next());
563         assertTrue(expectedEntriesToIterate.remove(firstEntry));
564 
565         assertTrue(iterator.hasNext());
566         Map.Entry<String, String> secondEntry = copyOf(iterator.next());
567         assertTrue(expectedEntriesToIterate.remove(secondEntry));
568 
569         assertFalse(iterator.hasNext());
570 
571         try {
572             iterator.next();
573             fail();
574         } catch (NoSuchElementException expected) {
575         }
576     }
577 
578     private static<K, V> Map.Entry<K, V> entryOf(K key, V value) {
579         return new AbstractMap.SimpleEntry<>(key, value);
580     }
581 
582     private static<K, V> Map.Entry<K, V> copyOf(Map.Entry<K, V> entry) {
583         return entryOf(entry.getKey(), entry.getValue());
584     }
585 
586     @Test
587     public void testCanNotIteratePastEnd_keySetIterator() {
588         Map<String, String> map = new ArrayMap<>();
589         map.put("key 1", "value 1");
590         map.put("key 2", "value 2");
591         Set<String> expectedKeysToIterate = new HashSet<>(Arrays.asList("key 1", "key 2"));
592         Iterator<String> iterator = map.keySet().iterator();
593 
594         // Assert iteration over the expected two keys in any order
595         assertTrue(iterator.hasNext());
596         String firstKey = iterator.next();
597         assertTrue(expectedKeysToIterate.remove(firstKey));
598 
599         assertTrue(iterator.hasNext());
600         String secondKey = iterator.next();
601         assertTrue(expectedKeysToIterate.remove(secondKey));
602 
603         assertFalse(iterator.hasNext());
604 
605         try {
606             iterator.next();
607             fail();
608         } catch (NoSuchElementException expected) {
609         }
610     }
611 
612     @Test
613     public void testCanNotIteratePastEnd_valuesIterator() {
614         Map<String, String> map = new ArrayMap<>();
615         map.put("key 1", "value 1");
616         map.put("key 2", "value 2");
617         Set<String> expectedValuesToIterate = new HashSet<>(Arrays.asList("value 1", "value 2"));
618         Iterator<String> iterator = map.values().iterator();
619 
620         // Assert iteration over the expected two values in any order
621         assertTrue(iterator.hasNext());
622         String firstValue = iterator.next();
623         assertTrue(expectedValuesToIterate.remove(firstValue));
624 
625         assertTrue(iterator.hasNext());
626         String secondValue = iterator.next();
627         assertTrue(expectedValuesToIterate.remove(secondValue));
628 
629         assertFalse(iterator.hasNext());
630 
631         try {
632             iterator.next();
633             fail();
634         } catch (NoSuchElementException expected) {
635         }
636     }
637 
638     /**
639      * The entrySet Iterator returns itself from each call to {@code next()}.
640      * This is unusual behavior for {@link Iterator#next()}; this test ensures that
641      * any future change to this behavior is deliberate.
642      */
643     @Test
644     public void testUnusualBehavior_eachEntryIsSameAsIterator_entrySetIterator() {
645         Map<String, String> map = new ArrayMap<>();
646         map.put("key 1", "value 1");
647         map.put("key 2", "value 2");
648         Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
649 
650         assertSame(iterator, iterator.next());
651         assertSame(iterator, iterator.next());
652     }
653 
654     @Test
655     public void testUnusualBehavior_equalsThrowsAfterRemove_entrySetIterator() {
656         Map<String, String> map = new ArrayMap<>();
657         map.put("key 1", "value 1");
658         map.put("key 2", "value 2");
659         Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
660         iterator.next();
661         iterator.remove();
662         try {
663             iterator.equals(iterator);
664             fail();
665         } catch (IllegalStateException expected) {
666         }
667     }
668 
669     private static<T> void assertEqualsBothWays(T a, T b) {
670         assertEquals(a, b);
671         assertEquals(b, a);
672         assertEquals(a.hashCode(), b.hashCode());
673     }
674 
675     @Test
676     public void testRemoveAll() {
677         final ArrayMap<Integer, String> map = new ArrayMap<>();
678         for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) {
679             map.put(i, i.toString());
680         }
681 
682         final ArrayMap<Integer, String> expectedMap = new ArrayMap<>();
683         for (Integer i :  Arrays.asList(2, 4)) {
684             expectedMap.put(i, String.valueOf(i));
685         }
686         map.removeAll(Arrays.asList(0, 1, 3, 5, 6));
687         if (!compare(map, expectedMap)) {
688             fail("ArrayMap removeAll failure, expect " + expectedMap + ", but " + map);
689         }
690 
691         map.removeAll(Collections.emptyList());
692         if (!compare(map, expectedMap)) {
693             fail("ArrayMap removeAll failure for empty maps, expect " + expectedMap + ", but " +
694                     map);
695         }
696 
697         map.removeAll(Arrays.asList(2, 4));
698         if (!map.isEmpty()) {
699             fail("ArrayMap removeAll failure, expect empty, but " + map);
700         }
701     }
702 }
703