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 
  24 import java.util.AbstractMap;
  25 import java.util.Collection;
  26 import java.util.Comparator;
  27 import java.util.HashMap;
  28 import java.util.Iterator;
  29 import java.util.List;
  30 import java.util.ListIterator;
  31 import java.util.Map;
  32 import java.util.Objects;
  33 import java.util.Set;
  34 import java.util.function.Supplier;
  35 import java.util.regex.Pattern;
  36 
  37 import static java.util.Collections.emptyList;
  38 import static java.util.Collections.singleton;
  39 import static java.util.Objects.requireNonNull;
  40 
  41 //
  42 // A set of testing utility functions
  43 //
  44 public final class TestKit {
  45 
  46     private TestKit() { }
  47 
  48     public static void assertNotThrows(ThrowingProcedure code) {
  49         requireNonNull(code, "code");
  50         assertNotThrows(() -> {
  51             code.run();
  52             return null;
  53         });
  54     }
  55 
  56     public static <V> V assertNotThrows(ThrowingFunction<V> code) {
  57         requireNonNull(code, "code");
  58         try {
  59             return code.run();
  60         } catch (Throwable t) {
  61             throw new RuntimeException("Expected to run normally, but threw "
  62                     + t.getClass().getCanonicalName(), t);
  63         }
  64     }
  65 
  66     public static <T extends Throwable> T assertThrows(Class<T> clazz,
  67                                                        ThrowingProcedure code) {
  68         requireNonNull(clazz, "clazz");
  69         requireNonNull(code, "code");
  70         try {
  71             code.run();
  72         } catch (Throwable t) {
  73             if (clazz.isInstance(t)) {
  74                 return clazz.cast(t);
  75             }
  76             throw new RuntimeException("Expected to catch an exception of type "
  77                     + clazz.getCanonicalName() + ", but caught "
  78                     + t.getClass().getCanonicalName(), t);
  79 
  80         }
  81         throw new RuntimeException("Expected to catch an exception of type "
  82                 + clazz.getCanonicalName() + ", but caught nothing");
  83     }
  84 
  85     public interface ThrowingProcedure {
  86         void run() throws Throwable;
  87     }
  88 
  89     public interface ThrowingFunction<V> {
  90         V run() throws Throwable;
  91     }
  92 
  93     // The rationale behind asking for a regex is to not pollute variable names
  94     // space in the scope of assertion: if it's something as simple as checking
  95     // a message, we can do it inside
  96     public static <T extends Throwable> T assertThrows(Class<T> clazz,
  97                                                        String messageRegex,
  98                                                        ThrowingProcedure code) {
  99         requireNonNull(messageRegex, "messagePattern");
 100         T t = assertThrows(clazz, code);
 101         String m = t.getMessage();
 102         if (m == null) {
 103             throw new RuntimeException(String.format(
 104                     "Expected exception message to match the regex '%s', " +
 105                             "but the message was null", messageRegex), t);
 106         }
 107         if (!Pattern.matches(messageRegex, m)) {
 108             throw new RuntimeException(String.format(
 109                     "Expected exception message to match the regex '%s', " +
 110                             "actual message: %s", messageRegex, m), t);
 111         }
 112         return t;
 113     }
 114 
 115     /*
 116      * Asserts that the given Collection is unmodifiable: any mutator method
 117      * throw an UnsupportedOperationException unconditionally.
 118      */
 119     public static void assertUnmodifiableCollection(Collection<?> collection) {
 120         assertUnmodifiableCollection(collection, () -> null);
 121     }
 122 
 123     public static <E> void assertUnmodifiableCollection(Collection<E> collection,
 124                                                         Supplier<? extends E> elementsFactory) {
 125         requireNonNull(collection, "collection");
 126         requireNonNull(elementsFactory, "elementsFactory");
 127 
 128         E e = elementsFactory.get();
 129 
 130         assertUOE(() -> collection.add(e));
 131         assertUOE(() -> collection.addAll(singleton(e)));
 132         Iterator<?> i = collection.iterator();
 133         if (i.hasNext()) {
 134             i.next();
 135             assertUOE(i::remove);
 136         }
 137         assertUOE(collection::clear);
 138         assertUOE(() -> collection.remove(e));
 139         assertUOE(() -> collection.removeAll(singleton(e)));
 140         assertUOE(() -> collection.removeIf(x -> true));
 141         assertUOE(() -> collection.retainAll(emptyList()));
 142         // No need to check toArray methods, since API guarantees arrays
 143         // returned by them are "safe"
 144     }
 145 
 146     public static void assertUnmodifiableSet(Set<?> set) {
 147         assertUnmodifiableCollection(set, () -> null);
 148     }
 149 
 150     public static <E> void assertUnmodifiableSet(Set<E> set,
 151                                                  Supplier<? extends E> elementsFactory) {
 152         assertUnmodifiableCollection(set, elementsFactory);
 153     }
 154 
 155     public static void assertUnmodifiableList(List<?> list) {
 156         assertUnmodifiableList(list, () -> null);
 157     }
 158 
 159     public static <E> void assertUnmodifiableList(List<E> list,
 160                                                   Supplier<? extends E> elementsFactory) {
 161         assertUnmodifiableList(list, elementsFactory, 3); // This list, its sublist and its sublist's sublist
 162     }
 163 
 164     private static <E> void assertUnmodifiableList(List<E> list,
 165                                                    Supplier<? extends E> elementsFactory,
 166                                                    int depth) {
 167         requireNonNull(list, "list");
 168         requireNonNull(elementsFactory, "elementsFactory");
 169         if (depth < 0) {
 170             throw new IllegalArgumentException("depth: " + depth);
 171         }
 172         if (depth == 0) {
 173             return;
 174         }
 175 
 176         E e = elementsFactory.get();
 177 
 178         assertUnmodifiableCollection(list, elementsFactory);
 179         assertUOE(() -> list.add(0, e));
 180         assertUOE(() -> list.addAll(0, singleton(e)));
 181 
 182         ListIterator<E> i = list.listIterator();
 183         if (i.hasNext()) {
 184             i.next();
 185             assertUOE(i::remove);
 186             assertUOE(() -> i.set(e));
 187             assertUOE(() -> i.add(e));
 188         }
 189         assertUOE(() -> list.remove((int) 0));
 190         assertUOE(() -> list.replaceAll(x -> e));
 191         assertUOE(() -> list.set(0, e));
 192 
 193         // Any non-null general-purpose Comparator would do
 194         Comparator<Object> comparator = (a, b) -> Objects.hash(a, b);
 195         assertUOE(() -> list.sort(comparator));
 196 
 197         assertUnmodifiableList(list.subList(0, list.size()), elementsFactory, depth - 1);
 198     }
 199 
 200     public static void assertUnmodifiableMap(Map<?, ?> map) {
 201         assertUnmodifiableMap(map, () -> new AbstractMap.SimpleImmutableEntry<>(null, null));
 202     }
 203 
 204     public static <K, V> void assertUnmodifiableMap(Map<K, V> map,
 205                                                     Supplier<? extends Map.Entry<? extends K, ? extends V>> entriesFactory) {
 206         requireNonNull(map, "map");
 207         requireNonNull(entriesFactory, "entriesFactory");
 208         assertUOE(map::clear);
 209 
 210         Map.Entry<? extends K, ? extends V> e1 = entriesFactory.get();
 211         K k = e1.getKey();
 212         V v = e1.getValue();
 213 
 214         assertUOE(() -> map.compute(k, (k1, v1) -> v));
 215         assertUOE(() -> map.computeIfAbsent(k, (k1) -> v));
 216         assertUOE(() -> map.computeIfPresent(k, (k1, v1) -> v));
 217 
 218         Set<Map.Entry<K, V>> entrySet = map.entrySet();
 219         assertUnmodifiableSet(entrySet);
 220         for (Map.Entry<K, V> e : entrySet) {
 221             assertUOE(() -> e.setValue(null));
 222         }
 223 
 224         assertUnmodifiableSet(map.keySet());
 225         assertUOE(() -> map.merge(k, v, (k1, v1) -> v));
 226         assertUOE(() -> map.put(k, v));
 227         // Map.of(k, v) wouldn't do, as it doesn't permit nulls
 228         Map<K, V> m = new HashMap<>();
 229         m.put(k, v);
 230         assertUOE(() -> map.putAll(m));
 231         assertUOE(() -> map.putIfAbsent(k, v));
 232         assertUOE(() -> map.remove(k));
 233         assertUOE(() -> map.remove(k, v));
 234         assertUOE(() -> map.replace(k, v));
 235         assertUOE(() -> map.replace(k, v, v));
 236         assertUOE(() -> map.replaceAll((k1, v1) -> v));
 237         assertUnmodifiableCollection(map.values());
 238     }
 239 
 240     public static void assertUOE(ThrowingProcedure code) {
 241         assertThrows(UnsupportedOperationException.class, code);
 242     }
 243 }