AbstractFutureTest.java revision 3c77433663281544363151bf284b0240dfd22a42
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
17package com.google.common.util.concurrent;
18
19import static org.truth0.Truth.ASSERT;
20
21import junit.framework.AssertionFailedError;
22import junit.framework.TestCase;
23
24import java.util.concurrent.CancellationException;
25import java.util.concurrent.ExecutionException;
26import java.util.concurrent.ExecutorService;
27import java.util.concurrent.Executors;
28import java.util.concurrent.atomic.AtomicReference;
29
30/**
31 * Tests for {@link AbstractFuture}.
32 *
33 * @author Brian Stoler
34 */
35
36public class AbstractFutureTest extends TestCase {
37  public void testSuccess() throws ExecutionException, InterruptedException {
38    final Object value = new Object();
39    assertSame(value, new AbstractFuture<Object>() {
40      {
41        set(value);
42      }
43    }.get());
44  }
45
46  public void testException() throws InterruptedException {
47    final Throwable failure = new Throwable();
48    AbstractFuture<String> future = new AbstractFuture<String>() {
49      {
50        setException(failure);
51      }
52    };
53
54    ExecutionException ee1 = getExpectingExecutionException(future);
55    ExecutionException ee2 = getExpectingExecutionException(future);
56
57    // Ensure we get a unique execution exception on each get
58    assertNotSame(ee1, ee2);
59
60    assertSame(failure, ee1.getCause());
61    assertSame(failure, ee2.getCause());
62
63    checkStackTrace(ee1);
64    checkStackTrace(ee2);
65  }
66
67  public void testCancel_notDoneNoInterrupt() throws Exception {
68    InterruptibleFuture future = new InterruptibleFuture();
69    assertTrue(future.cancel(false));
70    assertTrue(future.isCancelled());
71    assertTrue(future.isDone());
72    assertFalse(future.wasInterrupted());
73    assertFalse(future.interruptTaskWasCalled);
74    try {
75      future.get();
76      fail("Expected CancellationException");
77    } catch (CancellationException e) {
78      assertNotNull(e.getCause());
79    }
80  }
81
82  public void testCancel_notDoneInterrupt() throws Exception {
83    InterruptibleFuture future = new InterruptibleFuture();
84    assertTrue(future.cancel(true));
85    assertTrue(future.isCancelled());
86    assertTrue(future.isDone());
87    assertTrue(future.wasInterrupted());
88    assertTrue(future.interruptTaskWasCalled);
89    try {
90      future.get();
91      fail("Expected CancellationException");
92    } catch (CancellationException e) {
93      assertNotNull(e.getCause());
94    }
95  }
96
97  public void testCancel_done() throws Exception {
98    AbstractFuture<String> future = new AbstractFuture<String>() {
99      {
100        set("foo");
101      }
102    };
103    assertFalse(future.cancel(true));
104    assertFalse(future.isCancelled());
105    assertTrue(future.isDone());
106  }
107
108  public void testCompletionFinishesWithDone() {
109    ExecutorService executor = Executors.newFixedThreadPool(10);
110    for (int i = 0; i < 50000; i++) {
111      final AbstractFuture<String> future = new AbstractFuture<String>() {};
112      final AtomicReference<String> errorMessage = new AtomicReference<String>();
113      executor.execute(new Runnable() {
114        @Override
115        public void run() {
116          future.set("success");
117          if (!future.isDone()) {
118            errorMessage.set("Set call exited before future was complete.");
119          }
120        }
121      });
122      executor.execute(new Runnable() {
123        @Override
124        public void run() {
125          future.setException(new IllegalArgumentException("failure"));
126          if (!future.isDone()) {
127            errorMessage.set("SetException call exited before future was complete.");
128          }
129        }
130      });
131      executor.execute(new Runnable() {
132        @Override
133        public void run() {
134          future.cancel(true);
135          if (!future.isDone()) {
136            errorMessage.set("Cancel call exited before future was complete.");
137          }
138        }
139      });
140      try {
141        future.get();
142      } catch (Throwable t) {
143        // Ignore, we just wanted to block.
144      }
145      String error = errorMessage.get();
146      assertNull(error, error);
147    }
148    executor.shutdown();
149  }
150
151  private void checkStackTrace(ExecutionException e) {
152    // Our call site for get() should be in the trace.
153    int index = findStackFrame(
154        e, getClass().getName(), "getExpectingExecutionException");
155
156    ASSERT.that(index).isNotEqualTo(0);
157
158    // Above our method should be the call to get(). Don't assert on the class
159    // because it could be some superclass.
160    ASSERT.that(e.getStackTrace()[index - 1].getMethodName()).isEqualTo("get");
161  }
162
163  private static int findStackFrame(
164      ExecutionException e, String clazz, String method) {
165    StackTraceElement[] elements = e.getStackTrace();
166    for (int i = 0; i < elements.length; i++) {
167      StackTraceElement element = elements[i];
168      if (element.getClassName().equals(clazz)
169          && element.getMethodName().equals(method)) {
170        return i;
171      }
172    }
173    AssertionFailedError failure =
174        new AssertionFailedError("Expected element " + clazz + "." + method
175            + " not found in stack trace");
176    failure.initCause(e);
177    throw failure;
178  }
179
180  private ExecutionException getExpectingExecutionException(
181      AbstractFuture<String> future) throws InterruptedException {
182    try {
183      String got = future.get();
184      fail("Expected exception but got " + got);
185    } catch (ExecutionException e) {
186      return e;
187    }
188
189    // unreachable, but compiler doesn't know that fail() always throws
190    return null;
191  }
192
193  private static final class InterruptibleFuture
194      extends AbstractFuture<String> {
195    boolean interruptTaskWasCalled;
196
197    @Override protected void interruptTask() {
198      assertFalse(interruptTaskWasCalled);
199      interruptTaskWasCalled = true;
200    }
201  }
202}
203