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