001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License.  You may obtain a copy
006 * of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016
017package com.google.common.util.concurrent.testing;
018
019import com.google.common.annotations.Beta;
020import com.google.common.annotations.GwtIncompatible;
021import com.google.common.util.concurrent.ListenableFuture;
022import java.util.concurrent.CancellationException;
023import java.util.concurrent.CountDownLatch;
024import java.util.concurrent.ExecutionException;
025import java.util.concurrent.ExecutorService;
026import java.util.concurrent.Executors;
027import java.util.concurrent.Future;
028import java.util.concurrent.TimeUnit;
029import java.util.concurrent.TimeoutException;
030import junit.framework.TestCase;
031
032/**
033 * Abstract test case parent for anything implementing {@link ListenableFuture}. Tests the two get
034 * methods and the addListener method.
035 *
036 * @author Sven Mawson
037 * @since 10.0
038 */
039@Beta
040@GwtIncompatible
041public abstract class AbstractListenableFutureTest extends TestCase {
042
043  protected CountDownLatch latch;
044  protected ListenableFuture<Boolean> future;
045
046  @Override
047  protected void setUp() throws Exception {
048
049    // Create a latch and a future that waits on the latch.
050    latch = new CountDownLatch(1);
051    future = createListenableFuture(Boolean.TRUE, null, latch);
052  }
053
054  @Override
055  protected void tearDown() throws Exception {
056
057    // Make sure we have no waiting threads.
058    latch.countDown();
059  }
060
061  /** Constructs a listenable future with a value available after the latch has counted down. */
062  protected abstract <V> ListenableFuture<V> createListenableFuture(
063      V value, Exception except, CountDownLatch waitOn);
064
065  /** Tests that the {@link Future#get()} method blocks until a value is available. */
066  public void testGetBlocksUntilValueAvailable() throws Throwable {
067
068    assertFalse(future.isDone());
069    assertFalse(future.isCancelled());
070
071    final CountDownLatch successLatch = new CountDownLatch(1);
072    final Throwable[] badness = new Throwable[1];
073
074    // Wait on the future in a separate thread.
075    new Thread(
076            new Runnable() {
077              @Override
078              public void run() {
079                try {
080                  assertSame(Boolean.TRUE, future.get());
081                  successLatch.countDown();
082                } catch (Throwable t) {
083                  t.printStackTrace();
084                  badness[0] = t;
085                }
086              }
087            })
088        .start();
089
090    // Release the future value.
091    latch.countDown();
092
093    assertTrue(successLatch.await(10, TimeUnit.SECONDS));
094
095    if (badness[0] != null) {
096      throw badness[0];
097    }
098
099    assertTrue(future.isDone());
100    assertFalse(future.isCancelled());
101  }
102
103  /** Tests that the {@link Future#get(long, TimeUnit)} method times out correctly. */
104  public void testTimeoutOnGetWorksCorrectly() throws InterruptedException, ExecutionException {
105
106    // The task thread waits for the latch, so we expect a timeout here.
107    try {
108      future.get(20, TimeUnit.MILLISECONDS);
109      fail("Should have timed out trying to get the value.");
110    } catch (TimeoutException expected) {
111    } finally {
112      latch.countDown();
113    }
114  }
115
116  /**
117   * Tests that a canceled future throws a cancellation exception.
118   *
119   * <p>This method checks the cancel, isCancelled, and isDone methods.
120   */
121  public void testCanceledFutureThrowsCancellation() throws Exception {
122
123    assertFalse(future.isDone());
124    assertFalse(future.isCancelled());
125
126    final CountDownLatch successLatch = new CountDownLatch(1);
127
128    // Run cancellation in a separate thread as an extra thread-safety test.
129    new Thread(
130            new Runnable() {
131              @Override
132              public void run() {
133                try {
134                  future.get();
135                } catch (CancellationException expected) {
136                  successLatch.countDown();
137                } catch (Exception ignored) {
138                  // All other errors are ignored, we expect a cancellation.
139                }
140              }
141            })
142        .start();
143
144    assertFalse(future.isDone());
145    assertFalse(future.isCancelled());
146
147    future.cancel(true);
148
149    assertTrue(future.isDone());
150    assertTrue(future.isCancelled());
151
152    assertTrue(successLatch.await(200, TimeUnit.MILLISECONDS));
153
154    latch.countDown();
155  }
156
157  public void testListenersNotifiedOnError() throws Exception {
158    final CountDownLatch successLatch = new CountDownLatch(1);
159    final CountDownLatch listenerLatch = new CountDownLatch(1);
160
161    ExecutorService exec = Executors.newCachedThreadPool();
162
163    future.addListener(
164        new Runnable() {
165          @Override
166          public void run() {
167            listenerLatch.countDown();
168          }
169        },
170        exec);
171
172    new Thread(
173            new Runnable() {
174              @Override
175              public void run() {
176                try {
177                  future.get();
178                } catch (CancellationException expected) {
179                  successLatch.countDown();
180                } catch (Exception ignored) {
181                  // No success latch count down.
182                }
183              }
184            })
185        .start();
186
187    future.cancel(true);
188
189    assertTrue(future.isCancelled());
190    assertTrue(future.isDone());
191
192    assertTrue(successLatch.await(200, TimeUnit.MILLISECONDS));
193    assertTrue(listenerLatch.await(200, TimeUnit.MILLISECONDS));
194
195    latch.countDown();
196
197    exec.shutdown();
198    exec.awaitTermination(100, TimeUnit.MILLISECONDS);
199  }
200
201  /**
202   * Tests that all listeners complete, even if they were added before or after the future was
203   * finishing. Also acts as a concurrency test to make sure the locking is done correctly when a
204   * future is finishing so that no listeners can be lost.
205   */
206  public void testAllListenersCompleteSuccessfully()
207      throws InterruptedException, ExecutionException {
208
209    ExecutorService exec = Executors.newCachedThreadPool();
210
211    int listenerCount = 20;
212    final CountDownLatch listenerLatch = new CountDownLatch(listenerCount);
213
214    // Test that listeners added both before and after the value is available
215    // get called correctly.
216    for (int i = 0; i < 20; i++) {
217
218      // Right in the middle start up a thread to close the latch.
219      if (i == 10) {
220        new Thread(
221                new Runnable() {
222                  @Override
223                  public void run() {
224                    latch.countDown();
225                  }
226                })
227            .start();
228      }
229
230      future.addListener(
231          new Runnable() {
232            @Override
233            public void run() {
234              listenerLatch.countDown();
235            }
236          },
237          exec);
238    }
239
240    assertSame(Boolean.TRUE, future.get());
241    // Wait for the listener latch to complete.
242    listenerLatch.await(500, TimeUnit.MILLISECONDS);
243
244    exec.shutdown();
245    exec.awaitTermination(500, TimeUnit.MILLISECONDS);
246  }
247}