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}