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 }