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