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.Executors.newCachedThreadPool;
020import static java.util.concurrent.Executors.newSingleThreadExecutor;
021import static java.util.concurrent.TimeUnit.MILLISECONDS;
022import static java.util.concurrent.TimeUnit.SECONDS;
023import static org.junit.Assert.assertThrows;
024
025import com.google.common.annotations.GwtIncompatible;
026import com.google.common.util.concurrent.ListenableFuture;
027import java.util.concurrent.CancellationException;
028import java.util.concurrent.CountDownLatch;
029import java.util.concurrent.ExecutionException;
030import java.util.concurrent.ExecutorService;
031import java.util.concurrent.Future;
032import java.util.concurrent.TimeUnit;
033import java.util.concurrent.TimeoutException;
034import junit.framework.TestCase;
035import org.jspecify.annotations.Nullable;
036
037/**
038 * Abstract test case parent for anything implementing {@link ListenableFuture}. Tests the two get
039 * methods and the addListener method.
040 *
041 * @author Sven Mawson
042 * @since 10.0
043 */
044@GwtIncompatible
045public abstract class AbstractListenableFutureTest extends TestCase {
046
047  protected CountDownLatch latch;
048  protected ListenableFuture<Boolean> future;
049
050  @Override
051  protected void setUp() throws Exception {
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 = 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() {
093    // The task thread waits for the latch, so we expect a timeout here.
094    try {
095      assertThrows(TimeoutException.class, () -> future.get(20, MILLISECONDS));
096    } finally {
097      latch.countDown();
098    }
099  }
100
101  /**
102   * Tests that a canceled future throws a cancellation exception.
103   *
104   * <p>This method checks the cancel, isCancelled, and isDone methods.
105   */
106  public void testCanceledFutureThrowsCancellation() throws Exception {
107
108    assertFalse(future.isDone());
109    assertFalse(future.isCancelled());
110
111    CountDownLatch successLatch = new CountDownLatch(1);
112
113    // Run cancellation in a separate thread as an extra thread-safety test.
114    new Thread(
115            () -> {
116              assertThrows(CancellationException.class, future::get);
117              successLatch.countDown();
118            })
119        .start();
120
121    assertFalse(future.isDone());
122    assertFalse(future.isCancelled());
123
124    future.cancel(true);
125
126    assertTrue(future.isDone());
127    assertTrue(future.isCancelled());
128
129    assertTrue(successLatch.await(200, MILLISECONDS));
130
131    latch.countDown();
132  }
133
134  public void testListenersNotifiedOnError() throws Exception {
135    CountDownLatch successLatch = new CountDownLatch(1);
136    CountDownLatch listenerLatch = new CountDownLatch(1);
137
138    ExecutorService exec = newCachedThreadPool();
139
140    future.addListener(listenerLatch::countDown, exec);
141
142    new Thread(
143            () -> {
144              assertThrows(CancellationException.class, future::get);
145              successLatch.countDown();
146            })
147        .start();
148
149    future.cancel(true);
150
151    assertTrue(future.isCancelled());
152    assertTrue(future.isDone());
153
154    assertTrue(successLatch.await(200, MILLISECONDS));
155    assertTrue(listenerLatch.await(200, MILLISECONDS));
156
157    latch.countDown();
158
159    exec.shutdown();
160    exec.awaitTermination(100, MILLISECONDS);
161  }
162
163  /**
164   * Tests that all listeners complete, even if they were added before or after the future was
165   * finishing. Also acts as a concurrency test to make sure the locking is done correctly when a
166   * future is finishing so that no listeners can be lost.
167   */
168  public void testAllListenersCompleteSuccessfully()
169      throws InterruptedException, ExecutionException {
170
171    ExecutorService exec = newCachedThreadPool();
172
173    int listenerCount = 20;
174    CountDownLatch listenerLatch = new CountDownLatch(listenerCount);
175
176    // Test that listeners added both before and after the value is available
177    // get called correctly.
178    for (int i = 0; i < 20; i++) {
179
180      // Right in the middle start up a thread to close the latch.
181      if (i == 10) {
182        new Thread(() -> latch.countDown()).start();
183      }
184
185      future.addListener(listenerLatch::countDown, exec);
186    }
187
188    assertSame(Boolean.TRUE, future.get());
189    // Wait for the listener latch to complete.
190    listenerLatch.await(500, MILLISECONDS);
191
192    exec.shutdown();
193    exec.awaitTermination(500, MILLISECONDS);
194  }
195}