1 /* 2 * Copyright (c) 2013, 2016, 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 import org.testng.annotations.DataProvider; 25 import org.testng.annotations.Test; 26 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.ConcurrentModificationException; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.LinkedHashMap; 35 import java.util.LinkedHashSet; 36 import java.util.LinkedList; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.PriorityQueue; 40 import java.util.Set; 41 import java.util.Spliterator; 42 import java.util.Stack; 43 import java.util.TreeMap; 44 import java.util.TreeSet; 45 import java.util.Vector; 46 import java.util.WeakHashMap; 47 import java.util.function.Consumer; 48 import java.util.function.Function; 49 import java.util.function.Supplier; 50 51 import static org.testng.Assert.*; 52 53 /** 54 * @test 55 * @bug 8148748 56 * @summary Spliterator last-binding and fail-fast tests 57 * @run testng SpliteratorLateBindingFailFastTest 58 */ 59 60 @Test 61 public class SpliteratorLateBindingFailFastTest { 62 63 private interface Source<T> { 64 Collection<T> asCollection(); 65 void update(); 66 } 67 68 private static class SpliteratorDataBuilder<T> { 69 final List<Object[]> data; 70 71 final T newValue; 72 73 final List<T> exp; 74 75 final Map<T, T> mExp; 76 77 SpliteratorDataBuilder(List<Object[]> data, T newValue, List<T> exp) { 78 this.data = data; 79 this.newValue = newValue; 80 this.exp = exp; 81 this.mExp = createMap(exp); 82 } 83 84 Map<T, T> createMap(List<T> l) { 85 Map<T, T> m = new LinkedHashMap<>(); 86 for (T t : l) { 87 m.put(t, t); 88 } 89 return m; 90 } 91 92 void add(String description, Supplier<Source<?>> s) { 93 description = joiner(description).toString(); 94 data.add(new Object[]{description, s}); 95 } 96 97 void addCollection(Function<Collection<T>, ? extends Collection<T>> f) { 98 class CollectionSource implements Source<T> { 99 final Collection<T> c = f.apply(exp); 100 101 final Consumer<Collection<T>> updater; 102 103 CollectionSource(Consumer<Collection<T>> updater) { 104 this.updater = updater; 105 } 106 107 @Override 108 public Collection<T> asCollection() { 109 return c; 110 } 111 112 @Override 113 public void update() { 114 updater.accept(c); 115 } 116 } 117 118 String description = "new " + f.apply(Collections.<T>emptyList()).getClass().getName() + ".spliterator() "; 119 add(description + "ADD", () -> new CollectionSource(c -> c.add(newValue))); 120 add(description + "REMOVE", () -> new CollectionSource(c -> c.remove(c.iterator().next()))); 121 } 122 123 void addList(Function<Collection<T>, ? extends List<T>> l) { 124 addCollection(l); 125 addCollection(l.andThen(list -> list.subList(0, list.size()))); 126 } 127 128 void addMap(Function<Map<T, T>, ? extends Map<T, T>> mapConstructor) { 129 class MapSource<U> implements Source<U> { 130 final Map<T, T> m = mapConstructor.apply(mExp); 131 132 final Collection<U> c; 133 134 final Consumer<Map<T, T>> updater; 135 136 MapSource(Function<Map<T, T>, Collection<U>> f, Consumer<Map<T, T>> updater) { 137 this.c = f.apply(m); 138 this.updater = updater; 139 } 140 141 @Override 142 public Collection<U> asCollection() { 143 return c; 144 } 145 146 @Override 147 public void update() { 148 updater.accept(m); 149 } 150 } 151 152 Map<String, Consumer<Map<T, T>>> actions = new HashMap<>(); 153 actions.put("ADD", m -> m.put(newValue, newValue)); 154 actions.put("REMOVE", m -> m.remove(m.keySet().iterator().next())); 155 156 String description = "new " + mapConstructor.apply(Collections.<T, T>emptyMap()).getClass().getName(); 157 for (Map.Entry<String, Consumer<Map<T, T>>> e : actions.entrySet()) { 158 add(description + ".keySet().spliterator() " + e.getKey(), 159 () -> new MapSource<T>(m -> m.keySet(), e.getValue())); 160 add(description + ".values().spliterator() " + e.getKey(), 161 () -> new MapSource<T>(m -> m.values(), e.getValue())); 162 add(description + ".entrySet().spliterator() " + e.getKey(), 163 () -> new MapSource<Map.Entry<T, T>>(m -> m.entrySet(), e.getValue())); 164 } 165 } 166 167 StringBuilder joiner(String description) { 168 return new StringBuilder(description). 169 append(" {"). 170 append("size=").append(exp.size()). 171 append("}"); 172 } 173 } 174 175 static Object[][] spliteratorDataProvider; 176 177 @DataProvider(name = "Source") 178 public static Object[][] spliteratorDataProvider() { 179 if (spliteratorDataProvider != null) { 180 return spliteratorDataProvider; 181 } 182 183 List<Object[]> data = new ArrayList<>(); 184 SpliteratorDataBuilder<Integer> db = new SpliteratorDataBuilder<>(data, 5, Arrays.asList(1, 2, 3, 4)); 185 186 // Collections 187 188 db.addList(ArrayList::new); 189 190 db.addList(LinkedList::new); 191 192 db.addList(Vector::new); 193 194 195 db.addCollection(HashSet::new); 196 197 db.addCollection(LinkedHashSet::new); 198 199 db.addCollection(TreeSet::new); 200 201 202 db.addCollection(c -> { Stack<Integer> s = new Stack<>(); s.addAll(c); return s;}); 203 204 db.addCollection(PriorityQueue::new); 205 206 // ArrayDeque fails some tests since its fail-fast support is weaker 207 // than other collections and limited to detecting most, but not all, 208 // removals. It probably requires its own test since it is difficult 209 // to abstract out the conditions under which it fails-fast. 210 // db.addCollection(ArrayDeque::new); 211 212 // Maps 213 214 db.addMap(HashMap::new); 215 216 db.addMap(LinkedHashMap::new); 217 218 // This fails when run through jtreg but passes when run through 219 // ant 220 // db.addMap(IdentityHashMap::new); 221 222 db.addMap(WeakHashMap::new); 223 224 // @@@ Descending maps etc 225 db.addMap(TreeMap::new); 226 227 return spliteratorDataProvider = data.toArray(new Object[0][]); 228 } 229 230 @Test(dataProvider = "Source") 231 public <T> void lateBindingTestWithForEach(String description, Supplier<Source<T>> ss) { 232 Source<T> source = ss.get(); 233 Collection<T> c = source.asCollection(); 234 Spliterator<T> s = c.spliterator(); 235 236 source.update(); 237 238 Set<T> r = new HashSet<>(); 239 s.forEachRemaining(r::add); 240 241 assertEquals(r, new HashSet<>(c)); 242 } 243 244 @Test(dataProvider = "Source") 245 public <T> void lateBindingTestWithTryAdvance(String description, Supplier<Source<T>> ss) { 246 Source<T> source = ss.get(); 247 Collection<T> c = source.asCollection(); 248 Spliterator<T> s = c.spliterator(); 249 250 source.update(); 251 252 Set<T> r = new HashSet<>(); 253 while (s.tryAdvance(r::add)) { } 254 255 assertEquals(r, new HashSet<>(c)); 256 } 257 258 @Test(dataProvider = "Source") 259 public <T> void lateBindingTestWithCharacteritics(String description, Supplier<Source<T>> ss) { 260 Source<T> source = ss.get(); 261 Collection<T> c = source.asCollection(); 262 Spliterator<T> s = c.spliterator(); 263 s.characteristics(); 264 265 Set<T> r = new HashSet<>(); 266 s.forEachRemaining(r::add); 267 268 assertEquals(r, new HashSet<>(c)); 269 } 270 271 272 @Test(dataProvider = "Source") 273 public <T> void testFailFastTestWithTryAdvance(String description, Supplier<Source<T>> ss) { 274 { 275 Source<T> source = ss.get(); 276 Collection<T> c = source.asCollection(); 277 Spliterator<T> s = c.spliterator(); 278 279 s.tryAdvance(e -> { 280 }); 281 source.update(); 282 283 executeAndCatch(() -> s.tryAdvance(e -> { })); 284 } 285 286 { 287 Source<T> source = ss.get(); 288 Collection<T> c = source.asCollection(); 289 Spliterator<T> s = c.spliterator(); 290 291 s.tryAdvance(e -> { 292 }); 293 source.update(); 294 295 executeAndCatch(() -> s.forEachRemaining(e -> { 296 })); 297 } 298 } 299 300 @Test(dataProvider = "Source") 301 public <T> void testFailFastTestWithForEach(String description, Supplier<Source<T>> ss) { 302 Source<T> source = ss.get(); 303 Collection<T> c = source.asCollection(); 304 Spliterator<T> s = c.spliterator(); 305 306 executeAndCatch(() -> s.forEachRemaining(e -> { 307 source.update(); 308 })); 309 } 310 311 @Test(dataProvider = "Source") 312 public <T> void testFailFastTestWithEstimateSize(String description, Supplier<Source<T>> ss) { 313 { 314 Source<T> source = ss.get(); 315 Collection<T> c = source.asCollection(); 316 Spliterator<T> s = c.spliterator(); 317 318 s.estimateSize(); 319 source.update(); 320 321 executeAndCatch(() -> s.tryAdvance(e -> { })); 322 } 323 324 { 325 Source<T> source = ss.get(); 326 Collection<T> c = source.asCollection(); 327 Spliterator<T> s = c.spliterator(); 328 329 s.estimateSize(); 330 source.update(); 331 332 executeAndCatch(() -> s.forEachRemaining(e -> { 333 })); 334 } 335 } 336 337 private void executeAndCatch(Runnable r) { 338 executeAndCatch(ConcurrentModificationException.class, r); 339 } 340 341 private void executeAndCatch(Class<? extends Exception> expected, Runnable r) { 342 Exception caught = null; 343 try { 344 r.run(); 345 } 346 catch (Exception e) { 347 caught = e; 348 } 349 350 assertNotNull(caught, 351 String.format("No Exception was thrown, expected an Exception of %s to be thrown", 352 expected.getName())); 353 assertTrue(expected.isInstance(caught), 354 String.format("Exception thrown %s not an instance of %s", 355 caught.getClass().getName(), expected.getName())); 356 } 357 358 }