1 /* 2 * Copyright (c) 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 package jdk.incubator.http.internal.websocket; 24 25 import java.nio.ByteBuffer; 26 import java.util.*; 27 import java.util.concurrent.*; 28 import java.util.function.Consumer; 29 import java.util.function.Predicate; 30 import java.util.regex.Pattern; 31 import java.util.stream.Collectors; 32 import java.util.stream.Stream; 33 34 import static java.util.List.of; 35 import static java.util.Objects.requireNonNull; 36 37 /* 38 * Auxiliary test infrastructure 39 */ 40 final class TestSupport { 41 42 private TestSupport() { } 43 44 static <A, B, R> Iterator<R> cartesianIterator(List<A> a, 45 List<B> b, 46 F2<A, B, R> f2) { 47 @SuppressWarnings("unchecked") 48 F<R> t = p -> f2.apply((A) p[0], (B) p[1]); 49 return cartesianIterator(of(a, b), t); 50 } 51 52 static <A, B, C, R> Iterator<R> cartesianIterator(List<A> a, 53 List<B> b, 54 List<C> c, 55 F3<A, B, C, R> f3) { 56 @SuppressWarnings("unchecked") 57 F<R> t = p -> f3.apply((A) p[0], (B) p[1], (C) p[2]); 58 return cartesianIterator(of(a, b, c), t); 59 } 60 61 static <A, B, C, D, R> Iterator<R> cartesianIterator(List<A> a, 62 List<B> b, 63 List<C> c, 64 List<D> d, 65 F4<A, B, C, D, R> f4) { 66 @SuppressWarnings("unchecked") 67 F<R> t = p -> f4.apply((A) p[0], (B) p[1], (C) p[2], (D) p[3]); 68 return cartesianIterator(of(a, b, c, d), t); 69 } 70 71 static <A, B, C, D, E, R> Iterator<R> cartesianIterator(List<A> a, 72 List<B> b, 73 List<C> c, 74 List<D> d, 75 List<E> e, 76 F5<A, B, C, D, E, R> f5) { 77 @SuppressWarnings("unchecked") 78 F<R> t = p -> f5.apply((A) p[0], (B) p[1], (C) p[2], (D) p[3], (E) p[4]); 79 return cartesianIterator(of(a, b, c, d, e), t); 80 } 81 82 static <R> Iterator<R> cartesianIterator(List<? extends List<?>> params, 83 F<R> function) { 84 if (params.isEmpty()) { 85 return Collections.emptyIterator(); 86 } 87 for (List<?> l : params) { 88 if (l.isEmpty()) { 89 return Collections.emptyIterator(); 90 } 91 } 92 // Assertion: if we are still here, there is at least a single element 93 // in the product 94 return new Iterator<>() { 95 96 private final int arity = params.size(); 97 private final int[] coordinates = new int[arity]; 98 private boolean hasNext = true; 99 100 @Override 101 public boolean hasNext() { 102 return hasNext; 103 } 104 105 @Override 106 public R next() { 107 if (!hasNext) { 108 throw new NoSuchElementException(); 109 } 110 Object[] array = new Object[arity]; 111 for (int i = 0; i < arity; i++) { 112 array[i] = params.get(i).get(coordinates[i]); 113 } 114 int p = arity - 1; 115 while (p >= 0 && coordinates[p] == params.get(p).size() - 1) { 116 p--; 117 } 118 if (p < 0) { 119 hasNext = false; 120 } else { 121 coordinates[p]++; 122 for (int i = p + 1; i < arity; i++) { 123 coordinates[i] = 0; 124 } 125 } 126 return function.apply(array); 127 } 128 }; 129 } 130 131 @FunctionalInterface 132 public interface F1<A, R> { 133 R apply(A a); 134 } 135 136 @FunctionalInterface 137 public interface F2<A, B, R> { 138 R apply(A a, B b); 139 } 140 141 @FunctionalInterface 142 public interface F3<A, B, C, R> { 143 R apply(A a, B b, C c); 144 } 145 146 @FunctionalInterface 147 public interface F4<A, B, C, D, R> { 148 R apply(A a, B b, C c, D d); 149 } 150 151 @FunctionalInterface 152 public interface F5<A, B, C, D, E, R> { 153 R apply(A a, B b, C c, D d, E e); 154 } 155 156 @FunctionalInterface 157 public interface F<R> { 158 R apply(Object[] args); 159 } 160 161 static <T> Iterator<T> iteratorOf1(T element) { 162 return List.of(element).iterator(); 163 } 164 165 @SafeVarargs 166 static <T> Iterator<T> iteratorOf(T... elements) { 167 return List.of(elements).iterator(); 168 } 169 170 static <T> Iterator<T> limit(int maxElements, Iterator<? extends T> elements) { 171 return new Iterator<>() { 172 173 int count = maxElements; 174 175 @Override 176 public boolean hasNext() { 177 return count > 0 && elements.hasNext(); 178 } 179 180 @Override 181 public T next() { 182 if (!hasNext()) { 183 throw new NoSuchElementException(); 184 } 185 count--; 186 return elements.next(); 187 } 188 }; 189 } 190 191 // static <T> Iterator<T> filter(Iterator<? extends T> source, 192 // Predicate<? super T> predicate) { 193 // return new Iterator<>() { 194 // 195 // { findNext(); } 196 // 197 // T next; 198 // boolean hasNext; 199 // 200 // @Override 201 // public boolean hasNext() { 202 // return hasNext; 203 // } 204 // 205 // @Override 206 // public T next() { 207 // if (!hasNext) { 208 // throw new NoSuchElementException(); 209 // } 210 // T n = this.next; 211 // findNext(); 212 // return n; 213 // } 214 // 215 // void findNext() { 216 // while (source.hasNext()) { 217 // T n = source.next(); 218 // if (predicate.test(n)) { 219 // hasNext = true; 220 // next = n; 221 // break; 222 // } 223 // } 224 // } 225 // }; 226 // } 227 228 static ByteBuffer fullCopy(ByteBuffer src) { 229 ByteBuffer copy = ByteBuffer.allocate(src.capacity()); 230 int p = src.position(); 231 int l = src.limit(); 232 src.clear(); 233 copy.put(src).position(p).limit(l); 234 src.position(p).limit(l); 235 return copy; 236 } 237 238 static void forEachBufferPartition(ByteBuffer src, 239 Consumer<? super Iterable<? extends ByteBuffer>> action) { 240 forEachPartition(src.remaining(), 241 (lengths) -> { 242 int end = src.position(); 243 List<ByteBuffer> buffers = new LinkedList<>(); 244 for (int len : lengths) { 245 ByteBuffer d = src.duplicate(); 246 d.position(end); 247 d.limit(end + len); 248 end += len; 249 buffers.add(d); 250 } 251 action.accept(buffers); 252 }); 253 } 254 255 private static void forEachPartition(int n, 256 Consumer<? super Iterable<Integer>> action) { 257 forEachPartition(n, new Stack<>(), action); 258 } 259 260 private static void forEachPartition(int n, 261 Stack<Integer> path, 262 Consumer<? super Iterable<Integer>> action) { 263 if (n == 0) { 264 action.accept(path); 265 } else { 266 for (int i = 1; i <= n; i++) { 267 path.push(i); 268 forEachPartition(n - i, path, action); 269 path.pop(); 270 } 271 } 272 } 273 274 static void forEachPermutation(int n, Consumer<? super int[]> c) { 275 int[] a = new int[n]; 276 for (int i = 0; i < n; i++) { 277 a[i] = i; 278 } 279 permutations(0, a, c); 280 } 281 282 private static void permutations(int i, int[] a, Consumer<? super int[]> c) { 283 if (i == a.length) { 284 c.accept(Arrays.copyOf(a, a.length)); 285 return; 286 } 287 for (int j = i; j < a.length; j++) { 288 swap(a, i, j); 289 permutations(i + 1, a, c); 290 swap(a, i, j); 291 } 292 } 293 294 private static void swap(int[] a, int i, int j) { 295 int x = a[i]; 296 a[i] = a[j]; 297 a[j] = x; 298 } 299 300 static <T> Iterator<T> concat(Iterator<? extends Iterator<? extends T>> iterators) { 301 requireNonNull(iterators); 302 return new Iterator<>() { 303 304 private Iterator<? extends T> current = Collections.emptyIterator(); 305 306 @Override 307 public boolean hasNext() { 308 while (!current.hasNext()) { 309 if (!iterators.hasNext()) { 310 return false; 311 } else { 312 current = iterators.next(); 313 } 314 } 315 return true; 316 } 317 318 @Override 319 public T next() { 320 if (!hasNext()) { 321 throw new NoSuchElementException(); 322 } 323 return current.next(); 324 } 325 }; 326 } 327 328 interface Mock { 329 330 /* 331 * Completes exceptionally if there are any expectations that haven't 332 * been met within the given time period, otherwise completes normally 333 */ 334 CompletableFuture<Void> expectations(long timeout, TimeUnit unit); 335 } 336 337 static final class InvocationChecker { 338 339 private final Object lock = new Object(); 340 private final Iterator<InvocationExpectation> expectations; 341 private final CompletableFuture<Void> expectationsViolation 342 = new CompletableFuture<>(); 343 344 InvocationChecker(Iterable<InvocationExpectation> expectations) { 345 this.expectations = requireNonNull(expectations).iterator(); 346 } 347 348 /* 349 * Completes exceptionally if there are any expectations that haven't 350 * been met within the given time period, otherwise completes normally 351 */ 352 CompletableFuture<Void> expectations(long timeout, TimeUnit unit) { 353 return expectationsViolation 354 .orTimeout(timeout, unit) 355 .handle((v, t) -> { 356 if (t == null) { 357 throw new InternalError( 358 "Unexpected normal completion: " + v); 359 } else if (t instanceof TimeoutException) { 360 synchronized (lock) { 361 if (!expectations.hasNext()) { 362 return null; 363 } else { 364 throw new AssertionFailedException( 365 "More invocations were expected"); 366 } 367 } 368 } else if (t instanceof AssertionFailedException) { 369 throw (AssertionFailedException) t; 370 } else { 371 throw new RuntimeException(t); 372 } 373 }); 374 } 375 376 void checkInvocation(String name, Object... args) { 377 synchronized (lock) { 378 if (!expectations.hasNext()) { 379 expectationsViolation.completeExceptionally( 380 new AssertionFailedException( 381 "Less invocations were expected: " + name)); 382 return; 383 } 384 InvocationExpectation next = expectations.next(); 385 if (!next.name.equals(name)) { 386 expectationsViolation.completeExceptionally( 387 new AssertionFailedException( 388 "A different invocation was expected: " + name) 389 ); 390 return; 391 } 392 if (!next.predicate.apply(args)) { 393 expectationsViolation.completeExceptionally( 394 new AssertionFailedException( 395 "Invocation doesn't match the predicate: " 396 + name + ", " + Arrays.toString(args)) 397 ); 398 } 399 } 400 } 401 } 402 403 static final class InvocationExpectation { 404 405 final String name; 406 final F<Boolean> predicate; 407 408 InvocationExpectation(String name, F<Boolean> predicate) { 409 this.name = requireNonNull(name); 410 this.predicate = requireNonNull(predicate); 411 } 412 } 413 414 static void checkExpectations(Mock... mocks) { 415 checkExpectations(0, TimeUnit.SECONDS, mocks); 416 } 417 418 static void checkExpectations(long timeout, TimeUnit unit, Mock... mocks) { 419 CompletableFuture<?>[] completableFutures = Stream.of(mocks) 420 .map(m -> m.expectations(timeout, unit)) 421 .collect(Collectors.toList()).toArray(new CompletableFuture<?>[0]); 422 CompletableFuture<Void> cf = CompletableFuture.allOf(completableFutures); 423 try { 424 cf.join(); 425 } catch (CompletionException e) { 426 Throwable cause = e.getCause(); 427 if (cause instanceof AssertionFailedException) { 428 throw (AssertionFailedException) cause; 429 } else { 430 throw e; 431 } 432 } 433 } 434 435 public static <T extends Throwable> T assertThrows(Class<? extends T> clazz, 436 ThrowingProcedure code) { 437 @SuppressWarnings("unchecked") 438 T t = (T) assertThrows(clazz::isInstance, code); 439 return t; 440 } 441 442 /* 443 * The rationale behind asking for a regex is to not pollute variable names 444 * space in the scope of assertion: if it's something as simple as checking 445 * a message, we can do it inside 446 */ 447 @SuppressWarnings("unchecked") 448 static <T extends Throwable> T assertThrows(Class<? extends T> clazz, 449 String messageRegex, 450 ThrowingProcedure code) { 451 requireNonNull(messageRegex, "messagePattern"); 452 Predicate<Throwable> p = e -> clazz.isInstance(e) 453 && Pattern.matches(messageRegex, e.getMessage()); 454 return (T) assertThrows(p, code); 455 } 456 457 static Throwable assertThrows(Predicate<? super Throwable> predicate, 458 ThrowingProcedure code) { 459 requireNonNull(predicate, "predicate"); 460 requireNonNull(code, "code"); 461 Throwable caught = null; 462 try { 463 code.run(); 464 } catch (Throwable t) { 465 caught = t; 466 } 467 if (predicate.test(caught)) { 468 return caught; 469 } 470 if (caught == null) { 471 throw new AssertionFailedException("No exception was thrown"); 472 } 473 throw new AssertionFailedException("Caught exception didn't match the predicate", caught); 474 } 475 476 /* 477 * Blocking assertion, waits for completion 478 */ 479 static Throwable assertCompletesExceptionally(Class<? extends Throwable> clazz, 480 CompletionStage<?> stage) { 481 CompletableFuture<?> cf = 482 CompletableFuture.completedFuture(null).thenCompose(x -> stage); 483 return assertThrows(t -> clazz.isInstance(t.getCause()), cf::get); 484 } 485 486 interface ThrowingProcedure { 487 void run() throws Throwable; 488 } 489 490 static final class Expectation { 491 492 private final List<Predicate<? super Throwable>> list = new LinkedList<>(); 493 494 static Expectation ifExpect(boolean condition, 495 Predicate<? super Throwable> predicate) { 496 return addPredicate(new Expectation(), condition, predicate); 497 } 498 499 Expectation orExpect(boolean condition, 500 Predicate<? super Throwable> predicate) { 501 return addPredicate(this, condition, predicate); 502 } 503 504 static Expectation addPredicate(Expectation e, boolean condition, 505 Predicate<? super Throwable> predicate) { 506 if (condition) { 507 e.list.add(requireNonNull(predicate)); 508 } 509 return e; 510 } 511 512 public Throwable assertThrows(ThrowingProcedure code) { 513 Predicate<Throwable> p; 514 if (list.isEmpty()) { 515 p = Objects::isNull; 516 } else { 517 p = e -> list.stream().anyMatch(x -> x.test(e)); 518 } 519 return TestSupport.assertThrows(p, code); 520 } 521 } 522 523 static final class AssertionFailedException extends RuntimeException { 524 525 private static final long serialVersionUID = 1L; 526 527 AssertionFailedException(String message) { 528 super(message); 529 } 530 531 AssertionFailedException(String message, Throwable cause) { 532 super(message, cause); 533 } 534 } 535 }