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 static java.util.concurrent.TimeUnit.MILLISECONDS;
020import static java.util.concurrent.TimeUnit.SECONDS;
021
022import com.google.common.annotations.Beta;
023import com.google.common.annotations.GwtIncompatible;
024import com.google.common.util.concurrent.ListenableFuture;
025import java.util.concurrent.CancellationException;
026import java.util.concurrent.CountDownLatch;
027import java.util.concurrent.ExecutionException;
028import java.util.concurrent.ExecutorService;
029import java.util.concurrent.Executors;
030import java.util.concurrent.Future;
031import java.util.concurrent.TimeUnit;
032import java.util.concurrent.TimeoutException;
033import junit.framework.TestCase;
034
035/**
036 * Abstract test case parent for anything implementing {@link ListenableFuture}. Tests the two get
037 * methods and the addListener method.
038 *
039 * @author Sven Mawson
040 * @since 10.0
041 */
042@Beta
043@GwtIncompatible
044public abstract class AbstractListenableFutureTest extends TestCase {
045
046  protected CountDownLatch latch;
047  protected ListenableFuture<Boolean> future;
048
049  @Override
050  protected void setUp() throws Exception {
051
052    // Create a latch and a future that waits on the latch.
053    latch = new CountDownLatch(1);
054    future = createListenableFuture(Boolean.TRUE, null, latch);
055  }
056
057  @Override
058  protected void tearDown() throws Exception {
059
060    // Make sure we have no waiting threads.
061    latch.countDown();
062  }
063
064  /** Constructs a listenable future with a value available after the latch has counted down. */
065  protected abstract <V> ListenableFuture<V> createListenableFuture(
066      V value, Exception except, CountDownLatch waitOn);
067
068  /** Tests that the {@link Future#get()} method blocks until a value is available. */
069  public void testGetBlocksUntilValueAvailable() throws Throwable {
070
071    assertFalse(future.isDone());
072    assertFalse(future.isCancelled());
073
074    CountDownLatch successLatch = new CountDownLatch(1);
075    Throwable[] badness = new Throwable[1];
076
077    // Wait on the future in a separate thread.
078    new Thread(
079            () -> {
080              try {
081                assertSame(Boolean.TRUE, future.get());
082                successLatch.countDown();
083              } catch (Throwable t) {
084                t.printStackTrace();
085                badness[0] = t;
086              }
087            })
088        .start();
089
090    // Release the future value.
091    latch.countDown();
092
093    assertTrue(successLatch.await(10, 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, 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    CountDownLatch successLatch = new CountDownLatch(1);
127
128    // Run cancellation in a separate thread as an extra thread-safety test.
129    new Thread(
130            () -> {
131              try {
132                future.get();
133              } catch (CancellationException expected) {
134                successLatch.countDown();
135              } catch (Exception ignored) {
136                // All other errors are ignored, we expect a cancellation.
137              }
138            })
139        .start();
140
141    assertFalse(future.isDone());
142    assertFalse(future.isCancelled());
143
144    future.cancel(true);
145
146    assertTrue(future.isDone());
147    assertTrue(future.isCancelled());
148
149    assertTrue(successLatch.await(200, MILLISECONDS));
150
151    latch.countDown();
152  }
153
154  public void testListenersNotifiedOnError() throws Exception {
155    CountDownLatch successLatch = new CountDownLatch(1);
156    CountDownLatch listenerLatch = new CountDownLatch(1);
157
158    ExecutorService exec = Executors.newCachedThreadPool();
159
160    future.addListener(listenerLatch::countDown, exec);
161
162    new Thread(
163            () -> {
164              try {
165                future.get();
166              } catch (CancellationException expected) {
167                successLatch.countDown();
168              } catch (Exception ignored) {
169                // No success latch count down.
170              }
171            })
172        .start();
173
174    future.cancel(true);
175
176    assertTrue(future.isCancelled());
177    assertTrue(future.isDone());
178
179    assertTrue(successLatch.await(200, MILLISECONDS));
180    assertTrue(listenerLatch.await(200, MILLISECONDS));
181
182    latch.countDown();
183
184    exec.shutdown();
185    exec.awaitTermination(100, MILLISECONDS);
186  }
187
188  /**
189   * Tests that all listeners complete, even if they were added before or after the future was
190   * finishing. Also acts as a concurrency test to make sure the locking is done correctly when a
191   * future is finishing so that no listeners can be lost.
192   */
193  public void testAllListenersCompleteSuccessfully()
194      throws InterruptedException, ExecutionException {
195
196    ExecutorService exec = Executors.newCachedThreadPool();
197
198    int listenerCount = 20;
199    CountDownLatch listenerLatch = new CountDownLatch(listenerCount);
200
201    // Test that listeners added both before and after the value is available
202    // get called correctly.
203    for (int i = 0; i < 20; i++) {
204
205      // Right in the middle start up a thread to close the latch.
206      if (i == 10) {
207        new Thread(() -> latch.countDown()).start();
208      }
209
210      future.addListener(listenerLatch::countDown, exec);
211    }
212
213    assertSame(Boolean.TRUE, future.get());
214    // Wait for the listener latch to complete.
215    listenerLatch.await(500, MILLISECONDS);
216
217    exec.shutdown();
218    exec.awaitTermination(500, MILLISECONDS);
219  }
220}