• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.util.concurrent;
18 
19 import static org.junit.contrib.truth.Truth.ASSERT;
20 
21 import junit.framework.AssertionFailedError;
22 import junit.framework.TestCase;
23 
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.ExecutorService;
26 import java.util.concurrent.Executors;
27 import java.util.concurrent.atomic.AtomicReference;
28 
29 /**
30  * Tests for {@link AbstractFuture}.
31  *
32  * @author Brian Stoler
33  */
34 
35 public class AbstractFutureTest extends TestCase {
testSuccess()36   public void testSuccess() throws ExecutionException, InterruptedException {
37     final Object value = new Object();
38     assertSame(value, new AbstractFuture<Object>() {
39       {
40         set(value);
41       }
42     }.get());
43   }
44 
testException()45   public void testException() throws InterruptedException {
46     final Throwable failure = new Throwable();
47     AbstractFuture<String> future = new AbstractFuture<String>() {
48       {
49         setException(failure);
50       }
51     };
52 
53     ExecutionException ee1 = getExpectingExecutionException(future);
54     ExecutionException ee2 = getExpectingExecutionException(future);
55 
56     // Ensure we get a unique execution exception on each get
57     assertNotSame(ee1, ee2);
58 
59     assertSame(failure, ee1.getCause());
60     assertSame(failure, ee2.getCause());
61 
62     checkStackTrace(ee1);
63     checkStackTrace(ee2);
64   }
65 
testCancel_notDoneNoInterrupt()66   public void testCancel_notDoneNoInterrupt() {
67     InterruptibleFuture future = new InterruptibleFuture();
68     assertTrue(future.cancel(false));
69     assertTrue(future.isCancelled());
70     assertTrue(future.isDone());
71     assertFalse(future.wasInterrupted);
72   }
73 
testCancel_notDoneInterrupt()74   public void testCancel_notDoneInterrupt() {
75     InterruptibleFuture future = new InterruptibleFuture();
76     assertTrue(future.cancel(true));
77     assertTrue(future.isCancelled());
78     assertTrue(future.isDone());
79     assertTrue(future.wasInterrupted);
80   }
81 
testCancel_done()82   public void testCancel_done() {
83     AbstractFuture<String> future = new AbstractFuture<String>() {
84       {
85         set("foo");
86       }
87     };
88     assertFalse(future.cancel(true));
89     assertFalse(future.isCancelled());
90     assertTrue(future.isDone());
91   }
92 
testCompletionFinishesWithDone()93   public void testCompletionFinishesWithDone() {
94     ExecutorService executor = Executors.newFixedThreadPool(10);
95     for (int i = 0; i < 50000; i++) {
96       final AbstractFuture<String> future = new AbstractFuture<String>() {};
97       final AtomicReference<String> errorMessage = new AtomicReference<String>();
98       executor.execute(new Runnable() {
99         @Override
100         public void run() {
101           future.set("success");
102           if (!future.isDone()) {
103             errorMessage.set("Set call exited before future was complete.");
104           }
105         }
106       });
107       executor.execute(new Runnable() {
108         @Override
109         public void run() {
110           future.setException(new IllegalArgumentException("failure"));
111           if (!future.isDone()) {
112             errorMessage.set("SetException call exited before future was complete.");
113           }
114         }
115       });
116       executor.execute(new Runnable() {
117         @Override
118         public void run() {
119           future.cancel(true);
120           if (!future.isDone()) {
121             errorMessage.set("Cancel call exited before future was complete.");
122           }
123         }
124       });
125       try {
126         future.get();
127       } catch (Throwable t) {
128         // Ignore, we just wanted to block.
129       }
130       String error = errorMessage.get();
131       assertNull(error, error);
132     }
133     executor.shutdown();
134   }
135 
checkStackTrace(ExecutionException e)136   private void checkStackTrace(ExecutionException e) {
137     // Our call site for get() should be in the trace.
138     int index = findStackFrame(
139         e, getClass().getName(), "getExpectingExecutionException");
140 
141     ASSERT.that(index).isNotEqualTo(0);
142 
143     // Above our method should be the call to get(). Don't assert on the class
144     // because it could be some superclass.
145     ASSERT.that(e.getStackTrace()[index - 1].getMethodName()).isEqualTo("get");
146   }
147 
findStackFrame( ExecutionException e, String clazz, String method)148   private static int findStackFrame(
149       ExecutionException e, String clazz, String method) {
150     StackTraceElement[] elements = e.getStackTrace();
151     for (int i = 0; i < elements.length; i++) {
152       StackTraceElement element = elements[i];
153       if (element.getClassName().equals(clazz)
154           && element.getMethodName().equals(method)) {
155         return i;
156       }
157     }
158     AssertionFailedError failure =
159         new AssertionFailedError("Expected element " + clazz + "." + method
160             + " not found in stack trace");
161     failure.initCause(e);
162     throw failure;
163   }
164 
getExpectingExecutionException( AbstractFuture<String> future)165   private ExecutionException getExpectingExecutionException(
166       AbstractFuture<String> future) throws InterruptedException {
167     try {
168       String got = future.get();
169       fail("Expected exception but got " + got);
170     } catch (ExecutionException e) {
171       return e;
172     }
173 
174     // unreachable, but compiler doesn't know that fail() always throws
175     return null;
176   }
177 
178   private static final class InterruptibleFuture
179       extends AbstractFuture<String> {
180     boolean wasInterrupted;
181 
interruptTask()182     @Override protected void interruptTask() {
183       wasInterrupted = true;
184     }
185   }
186 }
187