1 /*
2  * Copyright (C) 2008 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.util.concurrent;
16 
17 import static com.google.common.truth.Truth.assertThat;
18 import static com.google.common.util.concurrent.ClassPathUtil.parseJavaClassPath;
19 import static com.google.common.util.concurrent.Futures.getChecked;
20 import static com.google.common.util.concurrent.Futures.immediateFuture;
21 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.CHECKED_EXCEPTION;
22 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.ERROR;
23 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.ERROR_FUTURE;
24 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.FAILED_FUTURE_CHECKED_EXCEPTION;
25 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.FAILED_FUTURE_ERROR;
26 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.FAILED_FUTURE_OTHER_THROWABLE;
27 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.FAILED_FUTURE_UNCHECKED_EXCEPTION;
28 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.OTHER_THROWABLE;
29 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.RUNTIME_EXCEPTION;
30 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.RUNTIME_EXCEPTION_FUTURE;
31 import static com.google.common.util.concurrent.FuturesGetCheckedInputs.UNCHECKED_EXCEPTION;
32 import static java.util.concurrent.TimeUnit.SECONDS;
33 
34 import com.google.common.testing.GcFinalization;
35 import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithBadConstructor;
36 import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithGoodAndBadConstructor;
37 import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithManyConstructors;
38 import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithPrivateConstructor;
39 import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithSomePrivateConstructors;
40 import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithWrongTypesConstructor;
41 import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithoutThrowableConstructor;
42 import com.google.common.util.concurrent.FuturesGetCheckedInputs.TwoArgConstructorException;
43 import com.google.common.util.concurrent.FuturesGetCheckedInputs.TwoArgConstructorRuntimeException;
44 import java.lang.ref.WeakReference;
45 import java.net.URLClassLoader;
46 import java.util.concurrent.CancellationException;
47 import java.util.concurrent.Future;
48 import java.util.concurrent.TimeUnit;
49 import java.util.concurrent.TimeoutException;
50 import junit.framework.TestCase;
51 
52 /** Unit tests for {@link Futures#getChecked(Future, Class)}. */
53 public class FuturesGetCheckedTest extends TestCase {
54   // Boring untimed-get tests:
55 
testGetCheckedUntimed_success()56   public void testGetCheckedUntimed_success() throws TwoArgConstructorException {
57     assertEquals("foo", getChecked(immediateFuture("foo"), TwoArgConstructorException.class));
58   }
59 
testGetCheckedUntimed_interrupted()60   public void testGetCheckedUntimed_interrupted() {
61     SettableFuture<String> future = SettableFuture.create();
62     Thread.currentThread().interrupt();
63     try {
64       getChecked(future, TwoArgConstructorException.class);
65       fail();
66     } catch (TwoArgConstructorException expected) {
67       assertThat(expected).hasCauseThat().isInstanceOf(InterruptedException.class);
68       assertTrue(Thread.currentThread().isInterrupted());
69     } finally {
70       Thread.interrupted();
71     }
72   }
73 
testGetCheckedUntimed_cancelled()74   public void testGetCheckedUntimed_cancelled() throws TwoArgConstructorException {
75     SettableFuture<String> future = SettableFuture.create();
76     future.cancel(true);
77     try {
78       getChecked(future, TwoArgConstructorException.class);
79       fail();
80     } catch (CancellationException expected) {
81     }
82   }
83 
testGetCheckedUntimed_ExecutionExceptionChecked()84   public void testGetCheckedUntimed_ExecutionExceptionChecked() {
85     try {
86       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorException.class);
87       fail();
88     } catch (TwoArgConstructorException expected) {
89       assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION);
90     }
91   }
92 
testGetCheckedUntimed_ExecutionExceptionUnchecked()93   public void testGetCheckedUntimed_ExecutionExceptionUnchecked()
94       throws TwoArgConstructorException {
95     try {
96       getChecked(FAILED_FUTURE_UNCHECKED_EXCEPTION, TwoArgConstructorException.class);
97       fail();
98     } catch (UncheckedExecutionException expected) {
99       assertThat(expected).hasCauseThat().isEqualTo(UNCHECKED_EXCEPTION);
100     }
101   }
102 
testGetCheckedUntimed_ExecutionExceptionError()103   public void testGetCheckedUntimed_ExecutionExceptionError() throws TwoArgConstructorException {
104     try {
105       getChecked(FAILED_FUTURE_ERROR, TwoArgConstructorException.class);
106       fail();
107     } catch (ExecutionError expected) {
108       assertThat(expected).hasCauseThat().isEqualTo(ERROR);
109     }
110   }
111 
testGetCheckedUntimed_ExecutionExceptionOtherThrowable()112   public void testGetCheckedUntimed_ExecutionExceptionOtherThrowable() {
113     try {
114       getChecked(FAILED_FUTURE_OTHER_THROWABLE, TwoArgConstructorException.class);
115       fail();
116     } catch (TwoArgConstructorException expected) {
117       assertThat(expected).hasCauseThat().isEqualTo(OTHER_THROWABLE);
118     }
119   }
120 
testGetCheckedUntimed_RuntimeException()121   public void testGetCheckedUntimed_RuntimeException() throws TwoArgConstructorException {
122     try {
123       getChecked(RUNTIME_EXCEPTION_FUTURE, TwoArgConstructorException.class);
124       fail();
125     } catch (RuntimeException expected) {
126       assertEquals(RUNTIME_EXCEPTION, expected);
127     }
128   }
129 
testGetCheckedUntimed_Error()130   public void testGetCheckedUntimed_Error() throws TwoArgConstructorException {
131     try {
132       getChecked(ERROR_FUTURE, TwoArgConstructorException.class);
133     } catch (Error expected) {
134       assertEquals(ERROR, expected);
135       return;
136     }
137     fail();
138   }
139 
testGetCheckedUntimed_badExceptionConstructor_failsEvenForSuccessfulInput()140   public void testGetCheckedUntimed_badExceptionConstructor_failsEvenForSuccessfulInput()
141       throws Exception {
142     try {
143       getChecked(immediateFuture("x"), ExceptionWithBadConstructor.class);
144       fail();
145     } catch (IllegalArgumentException expected) {
146     }
147   }
148 
testGetCheckedUntimed_badExceptionConstructor_wrapsOriginalChecked()149   public void testGetCheckedUntimed_badExceptionConstructor_wrapsOriginalChecked()
150       throws Exception {
151     try {
152       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithBadConstructor.class);
153       fail();
154     } catch (IllegalArgumentException expected) {
155     }
156   }
157 
testGetCheckedUntimed_withGoodAndBadExceptionConstructor()158   public void testGetCheckedUntimed_withGoodAndBadExceptionConstructor() throws Exception {
159     try {
160       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithGoodAndBadConstructor.class);
161       fail();
162     } catch (ExceptionWithGoodAndBadConstructor expected) {
163       assertThat(expected).hasCauseThat().isSameInstanceAs(CHECKED_EXCEPTION);
164     }
165   }
166 
167   // Boring timed-get tests:
168 
testGetCheckedTimed_success()169   public void testGetCheckedTimed_success() throws TwoArgConstructorException {
170     assertEquals(
171         "foo", getChecked(immediateFuture("foo"), TwoArgConstructorException.class, 0, SECONDS));
172   }
173 
testGetCheckedTimed_interrupted()174   public void testGetCheckedTimed_interrupted() {
175     SettableFuture<String> future = SettableFuture.create();
176     Thread.currentThread().interrupt();
177     try {
178       getChecked(future, TwoArgConstructorException.class, 0, SECONDS);
179       fail();
180     } catch (TwoArgConstructorException expected) {
181       assertThat(expected).hasCauseThat().isInstanceOf(InterruptedException.class);
182       assertTrue(Thread.currentThread().isInterrupted());
183     } finally {
184       Thread.interrupted();
185     }
186   }
187 
testGetCheckedTimed_cancelled()188   public void testGetCheckedTimed_cancelled() throws TwoArgConstructorException {
189     SettableFuture<String> future = SettableFuture.create();
190     future.cancel(true);
191     try {
192       getChecked(future, TwoArgConstructorException.class, 0, SECONDS);
193       fail();
194     } catch (CancellationException expected) {
195     }
196   }
197 
testGetCheckedTimed_ExecutionExceptionChecked()198   public void testGetCheckedTimed_ExecutionExceptionChecked() {
199     try {
200       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorException.class, 0, SECONDS);
201       fail();
202     } catch (TwoArgConstructorException expected) {
203       assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION);
204     }
205   }
206 
testGetCheckedTimed_ExecutionExceptionUnchecked()207   public void testGetCheckedTimed_ExecutionExceptionUnchecked() throws TwoArgConstructorException {
208     try {
209       getChecked(FAILED_FUTURE_UNCHECKED_EXCEPTION, TwoArgConstructorException.class, 0, SECONDS);
210       fail();
211     } catch (UncheckedExecutionException expected) {
212       assertThat(expected).hasCauseThat().isEqualTo(UNCHECKED_EXCEPTION);
213     }
214   }
215 
testGetCheckedTimed_ExecutionExceptionError()216   public void testGetCheckedTimed_ExecutionExceptionError() throws TwoArgConstructorException {
217     try {
218       getChecked(FAILED_FUTURE_ERROR, TwoArgConstructorException.class, 0, SECONDS);
219       fail();
220     } catch (ExecutionError expected) {
221       assertThat(expected).hasCauseThat().isEqualTo(ERROR);
222     }
223   }
224 
testGetCheckedTimed_ExecutionExceptionOtherThrowable()225   public void testGetCheckedTimed_ExecutionExceptionOtherThrowable() {
226     try {
227       getChecked(FAILED_FUTURE_OTHER_THROWABLE, TwoArgConstructorException.class, 0, SECONDS);
228       fail();
229     } catch (TwoArgConstructorException expected) {
230       assertThat(expected).hasCauseThat().isEqualTo(OTHER_THROWABLE);
231     }
232   }
233 
testGetCheckedTimed_RuntimeException()234   public void testGetCheckedTimed_RuntimeException() throws TwoArgConstructorException {
235     try {
236       getChecked(RUNTIME_EXCEPTION_FUTURE, TwoArgConstructorException.class, 0, SECONDS);
237       fail();
238     } catch (RuntimeException expected) {
239       assertEquals(RUNTIME_EXCEPTION, expected);
240     }
241   }
242 
testGetCheckedTimed_Error()243   public void testGetCheckedTimed_Error() throws TwoArgConstructorException {
244     try {
245       getChecked(ERROR_FUTURE, TwoArgConstructorException.class, 0, SECONDS);
246     } catch (Error expected) {
247       assertEquals(ERROR, expected);
248       return;
249     }
250     fail();
251   }
252 
testGetCheckedTimed_TimeoutException()253   public void testGetCheckedTimed_TimeoutException() {
254     SettableFuture<String> future = SettableFuture.create();
255     try {
256       getChecked(future, TwoArgConstructorException.class, 0, SECONDS);
257       fail();
258     } catch (TwoArgConstructorException expected) {
259       assertThat(expected).hasCauseThat().isInstanceOf(TimeoutException.class);
260     }
261   }
262 
testGetCheckedTimed_badExceptionConstructor_failsEvenForSuccessfulInput()263   public void testGetCheckedTimed_badExceptionConstructor_failsEvenForSuccessfulInput()
264       throws Exception {
265     try {
266       getChecked(immediateFuture("x"), ExceptionWithBadConstructor.class, 1, TimeUnit.SECONDS);
267       fail();
268     } catch (IllegalArgumentException expected) {
269     }
270   }
271 
testGetCheckedTimed_badExceptionConstructor_wrapsOriginalChecked()272   public void testGetCheckedTimed_badExceptionConstructor_wrapsOriginalChecked() throws Exception {
273     try {
274       getChecked(
275           FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithBadConstructor.class, 1, TimeUnit.SECONDS);
276       fail();
277     } catch (IllegalArgumentException expected) {
278     }
279   }
280 
testGetCheckedTimed_withGoodAndBadExceptionConstructor()281   public void testGetCheckedTimed_withGoodAndBadExceptionConstructor() {
282     try {
283       getChecked(
284           FAILED_FUTURE_CHECKED_EXCEPTION,
285           ExceptionWithGoodAndBadConstructor.class,
286           1,
287           TimeUnit.SECONDS);
288       fail();
289     } catch (ExceptionWithGoodAndBadConstructor expected) {
290       assertThat(expected).hasCauseThat().isSameInstanceAs(CHECKED_EXCEPTION);
291     }
292   }
293 
294   // Edge case tests of the exception-construction code through untimed get():
295 
296   @SuppressWarnings("FuturesGetCheckedIllegalExceptionType")
testGetCheckedUntimed_exceptionClassIsRuntimeException()297   public void testGetCheckedUntimed_exceptionClassIsRuntimeException() {
298     try {
299       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorRuntimeException.class);
300       fail();
301     } catch (IllegalArgumentException expected) {
302     }
303   }
304 
testGetCheckedUntimed_exceptionClassSomePrivateConstructors()305   public void testGetCheckedUntimed_exceptionClassSomePrivateConstructors() {
306     try {
307       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithSomePrivateConstructors.class);
308       fail();
309     } catch (ExceptionWithSomePrivateConstructors expected) {
310     }
311   }
312 
313   @SuppressWarnings("FuturesGetCheckedIllegalExceptionType")
testGetCheckedUntimed_exceptionClassNoPublicConstructor()314   public void testGetCheckedUntimed_exceptionClassNoPublicConstructor()
315       throws ExceptionWithPrivateConstructor {
316     try {
317       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithPrivateConstructor.class);
318       fail();
319     } catch (IllegalArgumentException expected) {
320     }
321   }
322 
323   @SuppressWarnings("FuturesGetCheckedIllegalExceptionType")
testGetCheckedUntimed_exceptionClassPublicConstructorWrongType()324   public void testGetCheckedUntimed_exceptionClassPublicConstructorWrongType()
325       throws ExceptionWithWrongTypesConstructor {
326     try {
327       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithWrongTypesConstructor.class);
328       fail();
329     } catch (IllegalArgumentException expected) {
330     }
331   }
332 
testGetCheckedUntimed_exceptionClassPrefersStringConstructor()333   public void testGetCheckedUntimed_exceptionClassPrefersStringConstructor() {
334     try {
335       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithManyConstructors.class);
336       fail();
337     } catch (ExceptionWithManyConstructors expected) {
338       assertTrue(expected.usedExpectedConstructor);
339     }
340   }
341 
testGetCheckedUntimed_exceptionClassUsedInitCause()342   public void testGetCheckedUntimed_exceptionClassUsedInitCause() {
343     try {
344       getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithoutThrowableConstructor.class);
345       fail();
346     } catch (ExceptionWithoutThrowableConstructor expected) {
347       assertThat(expected).hasMessageThat().contains("mymessage");
348       assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION);
349     }
350   }
351 
352   // Class unloading test:
353 
354   public static final class WillBeUnloadedException extends Exception {}
355 
testGetChecked_classUnloading()356   public void testGetChecked_classUnloading() throws Exception {
357     WeakReference<?> classUsedByGetChecked = doTestClassUnloading();
358     GcFinalization.awaitClear(classUsedByGetChecked);
359   }
360 
361   /**
362    * Loads {@link WillBeUnloadedException} in a separate {@code ClassLoader}, calls {@code
363    * getChecked(future, WillBeUnloadedException.class)}, and returns the loader. The caller can then
364    * test that the {@code ClassLoader} can still be GCed. The test amounts to a test that {@code
365    * getChecked} holds no strong references to the class.
366    */
doTestClassUnloading()367   private WeakReference<?> doTestClassUnloading() throws Exception {
368     URLClassLoader shadowLoader = new URLClassLoader(parseJavaClassPath(), null);
369     @SuppressWarnings("unchecked")
370     Class<WillBeUnloadedException> shadowClass =
371         (Class<WillBeUnloadedException>)
372             Class.forName(WillBeUnloadedException.class.getName(), false, shadowLoader);
373     assertNotSame(shadowClass, WillBeUnloadedException.class);
374     getChecked(immediateFuture("foo"), shadowClass);
375     return new WeakReference<>(shadowLoader);
376   }
377 
378   /*
379    * TODO(cpovirk): It would be great to run all these tests (including class unloading) in an
380    * environment that forces Futures.getChecked to its fallback WeakSetValidator. One awful way of
381    * doing so would be to derive a separate test library by using remove_from_jar to strip out
382    * ClassValueValidator.
383    */
384 }
385