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