1 /* 2 * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 6612102 8336669 27 * @summary Test Map implementations for mutual compatibility 28 * @key randomness 29 */ 30 31 import java.util.Collections; 32 import java.util.HashMap; 33 import java.util.Hashtable; 34 import java.util.IdentityHashMap; 35 import java.util.Iterator; 36 import java.util.LinkedHashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Random; 40 import java.util.TreeMap; 41 import java.util.WeakHashMap; 42 import java.util.concurrent.ConcurrentHashMap; 43 import java.util.concurrent.ConcurrentSkipListMap; 44 45 /** 46 * Based on the strange scenario required to reproduce 47 * (coll) IdentityHashMap.iterator().remove() might decrement size twice 48 * 49 * It would be good to add more "Lockstep-style" tests to this file. 50 */ 51 public class LockStep { 52 // An interned identity class holding an int (like non-Preview Integer) 53 // interned for IdentityHashMap 54 // identity for WeakHashMap 55 private record Int(int intValue) implements Comparable<Int> { 56 private static final Map<Integer, Int> interned = new HashMap<>(100); 57 58 // Return a unique Ini for each int. 59 static Int intern(int intValue) { 60 return interned.computeIfAbsent(intValue, (i) -> new Int(i)); 61 } 62 63 @Override 64 public int compareTo(Int o) { 65 return Integer.compare(intValue, o.intValue); 66 } 67 } 68 69 void mapsEqual(Map m1, Map m2) { 70 equal(m1, m2); 71 equal(m2, m1); 72 equal(m1.size(), m2.size()); 73 equal(m1.isEmpty(), m2.isEmpty()); 74 equal(m1.keySet(), m2.keySet()); 75 equal(m2.keySet(), m1.keySet()); 76 } 77 78 void mapsEqual(List<Map> maps) { 79 Map first = maps.get(0); 80 for (Map map : maps) 81 mapsEqual(first, map); 82 } 83 84 void put(List<Map> maps, Object key, Object val) { 85 for (Map map : maps) 86 map.put(key, val); 87 mapsEqual(maps); 88 } 89 90 void removeLastTwo(List<Map> maps) { 91 Map first = maps.get(0); 92 int size = first.size(); 93 Iterator fit = first.keySet().iterator(); 94 for (int j = 0; j < size - 2; j++) 95 fit.next(); 96 Object x1 = fit.next(); 97 Object x2 = fit.next(); 98 99 for (Map map : maps) { 100 Iterator it = map.keySet().iterator(); 101 while (it.hasNext()) { 102 Object x = it.next(); 103 if (x == x1 || x == x2) 104 it.remove(); 105 } 106 } 107 mapsEqual(maps); 108 } 109 110 void remove(Map m, Iterator it) { 111 int size = m.size(); 112 it.remove(); 113 if (m.size() != size-1) 114 throw new Error(String.format("Incorrect size!%nmap=%s, size=%d%n", 115 m.toString(), m.size())); 116 } 117 118 void test(String[] args) throws Throwable { 119 final int iterations = 100; 120 final Random r = new Random(); 121 122 for (int i = 0; i < iterations; i++) { 123 List<Map> maps = List.of( 124 new IdentityHashMap(11), 125 new HashMap(16), 126 new LinkedHashMap(16), 127 new WeakHashMap(16), 128 new Hashtable(16), 129 new TreeMap(), 130 new ConcurrentHashMap(16), 131 new ConcurrentSkipListMap(), 132 Collections.checkedMap(new HashMap(16), Int.class, Integer.class), 133 Collections.checkedSortedMap(new TreeMap(), Int.class, Integer.class), 134 Collections.checkedNavigableMap(new TreeMap(), Int.class, Integer.class), 135 Collections.synchronizedMap(new HashMap(16)), 136 Collections.synchronizedSortedMap(new TreeMap()), 137 Collections.synchronizedNavigableMap(new TreeMap())); 138 139 for (int j = 0; j < 10; j++) 140 put(maps, Int.intern(r.nextInt(100)), r.nextInt(100)); 141 removeLastTwo(maps); 142 } 143 } 144 145 //--------------------- Infrastructure --------------------------- 146 volatile int passed = 0, failed = 0; 147 void pass() {passed++;} 148 void fail() {failed++; Thread.dumpStack();} 149 void fail(String msg) {System.err.println(msg); fail();} 150 void unexpected(Throwable t) {failed++; t.printStackTrace();} 151 void check(boolean cond) {if (cond) pass(); else fail();} 152 void equal(Object x, Object y) { 153 if (x == null ? y == null : x.equals(y)) pass(); 154 else fail(x + " not equal to " + y);} 155 public static void main(String[] args) throws Throwable { 156 new LockStep().instanceMain(args);} 157 void instanceMain(String[] args) throws Throwable { 158 try {test(args);} catch (Throwable t) {unexpected(t);} 159 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); 160 if (failed > 0) throw new AssertionError("Some tests failed");} 161 }