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