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