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}