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