• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.base;
16 
17 import com.google.common.annotations.GwtCompatible;
18 import com.google.common.annotations.GwtIncompatible;
19 import com.google.common.annotations.J2ktIncompatible;
20 import com.google.common.base.TestExceptions.SomeCheckedException;
21 import com.google.common.base.TestExceptions.SomeError;
22 import com.google.common.base.TestExceptions.SomeOtherCheckedException;
23 import com.google.common.base.TestExceptions.SomeUncheckedException;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.util.concurrent.ExecutionError;
26 import com.google.common.util.concurrent.UncheckedExecutionException;
27 import com.google.errorprone.annotations.CanIgnoreReturnValue;
28 import java.lang.reflect.InvocationTargetException;
29 import java.nio.charset.UnsupportedCharsetException;
30 import java.util.ConcurrentModificationException;
31 import java.util.NoSuchElementException;
32 import java.util.concurrent.CancellationException;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.TimeoutException;
35 import junit.framework.AssertionFailedError;
36 import org.checkerframework.checker.nullness.qual.Nullable;
37 
38 /** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */
39 @GwtCompatible(emulated = true)
40 @ElementTypesAreNonnullByDefault
41 final class ReflectionFreeAssertThrows {
42   interface ThrowingRunnable {
run()43     void run() throws Throwable;
44   }
45 
46   interface ThrowingSupplier {
get()47     @Nullable Object get() throws Throwable;
48   }
49 
50   @CanIgnoreReturnValue
assertThrows( Class<T> expectedThrowable, ThrowingSupplier supplier)51   static <T extends Throwable> T assertThrows(
52       Class<T> expectedThrowable, ThrowingSupplier supplier) {
53     return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true);
54   }
55 
56   @CanIgnoreReturnValue
assertThrows( Class<T> expectedThrowable, ThrowingRunnable runnable)57   static <T extends Throwable> T assertThrows(
58       Class<T> expectedThrowable, ThrowingRunnable runnable) {
59     return doAssertThrows(
60         expectedThrowable,
61         () -> {
62           runnable.run();
63           return null;
64         },
65         /* userPassedSupplier= */ false);
66   }
67 
doAssertThrows( Class<T> expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier)68   private static <T extends Throwable> T doAssertThrows(
69       Class<T> expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) {
70     Predicate<Throwable> predicate = INSTANCE_OF.get(expectedThrowable);
71     if (predicate == null) {
72       throw new IllegalArgumentException(
73           expectedThrowable
74               + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the"
75               + " map in that class.");
76     }
77     Object result;
78     try {
79       result = supplier.get();
80     } catch (Throwable t) {
81       if (predicate.apply(t)) {
82         // We are careful to set up INSTANCE_OF to match each Predicate to its target Class.
83         @SuppressWarnings("unchecked")
84         T caught = (T) t;
85         return caught;
86       }
87       throw new AssertionError(
88           "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t);
89     }
90     if (userPassedSupplier) {
91       throw new AssertionError(
92           "expected to throw "
93               + expectedThrowable.getSimpleName()
94               + " but returned result: "
95               + result);
96     } else {
97       throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName());
98     }
99   }
100 
101   private enum PlatformSpecificExceptionBatch {
102     PLATFORM {
103       @GwtIncompatible
104       @J2ktIncompatible
105       @Override
106       // returns the types available in "normal" environments
exceptions()107       ImmutableMap<Class<? extends Throwable>, Predicate<Throwable>> exceptions() {
108         return ImmutableMap.of(
109             InvocationTargetException.class,
110             e -> e instanceof InvocationTargetException,
111             StackOverflowError.class,
112             e -> e instanceof StackOverflowError);
113       }
114     };
115 
116     // used under GWT, etc., since the override of this method does not exist there
exceptions()117     ImmutableMap<Class<? extends Throwable>, Predicate<Throwable>> exceptions() {
118       return ImmutableMap.of();
119     }
120   }
121 
122   private static final ImmutableMap<Class<? extends Throwable>, Predicate<Throwable>> INSTANCE_OF =
123       ImmutableMap.<Class<? extends Throwable>, Predicate<Throwable>>builder()
124           .put(ArithmeticException.class, e -> e instanceof ArithmeticException)
125           .put(
126               ArrayIndexOutOfBoundsException.class,
127               e -> e instanceof ArrayIndexOutOfBoundsException)
128           .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException)
129           .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError)
130           .put(CancellationException.class, e -> e instanceof CancellationException)
131           .put(ClassCastException.class, e -> e instanceof ClassCastException)
132           .put(
133               ConcurrentModificationException.class,
134               e -> e instanceof ConcurrentModificationException)
135           .put(ExecutionError.class, e -> e instanceof ExecutionError)
136           .put(ExecutionException.class, e -> e instanceof ExecutionException)
137           .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException)
138           .put(IllegalStateException.class, e -> e instanceof IllegalStateException)
139           .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException)
140           .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException)
141           .put(NullPointerException.class, e -> e instanceof NullPointerException)
142           .put(NumberFormatException.class, e -> e instanceof NumberFormatException)
143           .put(RuntimeException.class, e -> e instanceof RuntimeException)
144           .put(SomeCheckedException.class, e -> e instanceof SomeCheckedException)
145           .put(SomeError.class, e -> e instanceof SomeError)
146           .put(SomeOtherCheckedException.class, e -> e instanceof SomeOtherCheckedException)
147           .put(SomeUncheckedException.class, e -> e instanceof SomeUncheckedException)
148           .put(TimeoutException.class, e -> e instanceof TimeoutException)
149           .put(UncheckedExecutionException.class, e -> e instanceof UncheckedExecutionException)
150           .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException)
151           .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException)
152           .put(VerifyException.class, e -> e instanceof VerifyException)
153           .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions())
154           .buildOrThrow();
155 
ReflectionFreeAssertThrows()156   private ReflectionFreeAssertThrows() {}
157 }
158