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}