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;
021import static org.junit.Assert.assertThrows;
022
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;
034import org.checkerframework.checker.nullness.qual.Nullable;
035
036/**
037 * Abstract test case parent for anything implementing {@link ListenableFuture}. Tests the two get
038 * methods and the addListener method.
039 *
040 * @author Sven Mawson
041 * @since 10.0
042 */
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, @Nullable 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    ExecutorService executor = Executors.newSingleThreadExecutor();
075
076    try {
077      Future<Boolean> getResult = executor.submit(() -> future.get());
078
079      // Release the future value.
080      latch.countDown();
081
082      assertTrue(getResult.get(10, SECONDS));
083    } finally {
084      executor.shutdownNow();
085    }
086
087    assertTrue(future.isDone());
088    assertFalse(future.isCancelled());
089  }
090
091  /** Tests that the {@link Future#get(long, TimeUnit)} method times out correctly. */
092  public void testTimeoutOnGetWorksCorrectly() throws InterruptedException, ExecutionException {
093
094    // The task thread waits for the latch, so we expect a timeout here.
095    try {
096      future.get(20, MILLISECONDS);
097      fail("Should have timed out trying to get the value.");
098    } catch (TimeoutException expected) {
099    } finally {
100      latch.countDown();
101    }
102  }
103
104  /**
105   * Tests that a canceled future throws a cancellation exception.
106   *
107   * <p>This method checks the cancel, isCancelled, and isDone methods.
108   */
109  public void testCanceledFutureThrowsCancellation() throws Exception {
110
111    assertFalse(future.isDone());
112    assertFalse(future.isCancelled());
113
114    CountDownLatch successLatch = new CountDownLatch(1);
115
116    // Run cancellation in a separate thread as an extra thread-safety test.
117    new Thread(
118            () -> {
119              assertThrows(CancellationException.class, future::get);
120              successLatch.countDown();
121            })
122        .start();
123
124    assertFalse(future.isDone());
125    assertFalse(future.isCancelled());
126
127    future.cancel(true);
128
129    assertTrue(future.isDone());
130    assertTrue(future.isCancelled());
131
132    assertTrue(successLatch.await(200, MILLISECONDS));
133
134    latch.countDown();
135  }
136
137  public void testListenersNotifiedOnError() throws Exception {
138    CountDownLatch successLatch = new CountDownLatch(1);
139    CountDownLatch listenerLatch = new CountDownLatch(1);
140
141    ExecutorService exec = Executors.newCachedThreadPool();
142
143    future.addListener(listenerLatch::countDown, exec);
144
145    new Thread(
146            () -> {
147              assertThrows(CancellationException.class, future::get);
148              successLatch.countDown();
149            })
150        .start();
151
152    future.cancel(true);
153
154    assertTrue(future.isCancelled());
155    assertTrue(future.isDone());
156
157    assertTrue(successLatch.await(200, MILLISECONDS));
158    assertTrue(listenerLatch.await(200, MILLISECONDS));
159
160    latch.countDown();
161
162    exec.shutdown();
163    exec.awaitTermination(100, MILLISECONDS);
164  }
165
166  /**
167   * Tests that all listeners complete, even if they were added before or after the future was
168   * finishing. Also acts as a concurrency test to make sure the locking is done correctly when a
169   * future is finishing so that no listeners can be lost.
170   */
171  public void testAllListenersCompleteSuccessfully()
172      throws InterruptedException, ExecutionException {
173
174    ExecutorService exec = Executors.newCachedThreadPool();
175
176    int listenerCount = 20;
177    CountDownLatch listenerLatch = new CountDownLatch(listenerCount);
178
179    // Test that listeners added both before and after the value is available
180    // get called correctly.
181    for (int i = 0; i < 20; i++) {
182
183      // Right in the middle start up a thread to close the latch.
184      if (i == 10) {
185        new Thread(() -> latch.countDown()).start();
186      }
187
188      future.addListener(listenerLatch::countDown, exec);
189    }
190
191    assertSame(Boolean.TRUE, future.get());
192    // Wait for the listener latch to complete.
193    listenerLatch.await(500, MILLISECONDS);
194
195    exec.shutdown();
196    exec.awaitTermination(500, MILLISECONDS);
197  }
198}