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.GwtIncompatible; 023import com.google.common.util.concurrent.ListenableFuture; 024import java.util.concurrent.CancellationException; 025import java.util.concurrent.CountDownLatch; 026import java.util.concurrent.ExecutionException; 027import java.util.concurrent.ExecutorService; 028import java.util.concurrent.Executors; 029import java.util.concurrent.Future; 030import java.util.concurrent.TimeUnit; 031import java.util.concurrent.TimeoutException; 032import junit.framework.TestCase; 033import org.checkerframework.checker.nullness.qual.Nullable; 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@GwtIncompatible 043public abstract class AbstractListenableFutureTest extends TestCase { 044 045 protected CountDownLatch latch; 046 protected ListenableFuture<Boolean> future; 047 048 @Override 049 protected void setUp() throws Exception { 050 051 // Create a latch and a future that waits on the latch. 052 latch = new CountDownLatch(1); 053 future = createListenableFuture(Boolean.TRUE, null, latch); 054 } 055 056 @Override 057 protected void tearDown() throws Exception { 058 059 // Make sure we have no waiting threads. 060 latch.countDown(); 061 } 062 063 /** Constructs a listenable future with a value available after the latch has counted down. */ 064 protected abstract <V> ListenableFuture<V> createListenableFuture( 065 V value, @Nullable Exception except, CountDownLatch waitOn); 066 067 /** Tests that the {@link Future#get()} method blocks until a value is available. */ 068 public void testGetBlocksUntilValueAvailable() throws Throwable { 069 070 assertFalse(future.isDone()); 071 assertFalse(future.isCancelled()); 072 073 CountDownLatch successLatch = new CountDownLatch(1); 074 Throwable[] badness = new Throwable[1]; 075 076 // Wait on the future in a separate thread. 077 new Thread( 078 () -> { 079 try { 080 assertSame(Boolean.TRUE, future.get()); 081 successLatch.countDown(); 082 } catch (Throwable t) { 083 t.printStackTrace(); 084 badness[0] = t; 085 } 086 }) 087 .start(); 088 089 // Release the future value. 090 latch.countDown(); 091 092 assertTrue(successLatch.await(10, SECONDS)); 093 094 if (badness[0] != null) { 095 throw badness[0]; 096 } 097 098 assertTrue(future.isDone()); 099 assertFalse(future.isCancelled()); 100 } 101 102 /** Tests that the {@link Future#get(long, TimeUnit)} method times out correctly. */ 103 public void testTimeoutOnGetWorksCorrectly() throws InterruptedException, ExecutionException { 104 105 // The task thread waits for the latch, so we expect a timeout here. 106 try { 107 future.get(20, MILLISECONDS); 108 fail("Should have timed out trying to get the value."); 109 } catch (TimeoutException expected) { 110 } finally { 111 latch.countDown(); 112 } 113 } 114 115 /** 116 * Tests that a canceled future throws a cancellation exception. 117 * 118 * <p>This method checks the cancel, isCancelled, and isDone methods. 119 */ 120 public void testCanceledFutureThrowsCancellation() throws Exception { 121 122 assertFalse(future.isDone()); 123 assertFalse(future.isCancelled()); 124 125 CountDownLatch successLatch = new CountDownLatch(1); 126 127 // Run cancellation in a separate thread as an extra thread-safety test. 128 new Thread( 129 () -> { 130 try { 131 future.get(); 132 } catch (CancellationException expected) { 133 successLatch.countDown(); 134 } catch (Exception ignored) { 135 // All other errors are ignored, we expect a cancellation. 136 } 137 }) 138 .start(); 139 140 assertFalse(future.isDone()); 141 assertFalse(future.isCancelled()); 142 143 future.cancel(true); 144 145 assertTrue(future.isDone()); 146 assertTrue(future.isCancelled()); 147 148 assertTrue(successLatch.await(200, MILLISECONDS)); 149 150 latch.countDown(); 151 } 152 153 public void testListenersNotifiedOnError() throws Exception { 154 CountDownLatch successLatch = new CountDownLatch(1); 155 CountDownLatch listenerLatch = new CountDownLatch(1); 156 157 ExecutorService exec = Executors.newCachedThreadPool(); 158 159 future.addListener(listenerLatch::countDown, exec); 160 161 new Thread( 162 () -> { 163 try { 164 future.get(); 165 } catch (CancellationException expected) { 166 successLatch.countDown(); 167 } catch (Exception ignored) { 168 // No success latch count down. 169 } 170 }) 171 .start(); 172 173 future.cancel(true); 174 175 assertTrue(future.isCancelled()); 176 assertTrue(future.isDone()); 177 178 assertTrue(successLatch.await(200, MILLISECONDS)); 179 assertTrue(listenerLatch.await(200, MILLISECONDS)); 180 181 latch.countDown(); 182 183 exec.shutdown(); 184 exec.awaitTermination(100, MILLISECONDS); 185 } 186 187 /** 188 * Tests that all listeners complete, even if they were added before or after the future was 189 * finishing. Also acts as a concurrency test to make sure the locking is done correctly when a 190 * future is finishing so that no listeners can be lost. 191 */ 192 public void testAllListenersCompleteSuccessfully() 193 throws InterruptedException, ExecutionException { 194 195 ExecutorService exec = Executors.newCachedThreadPool(); 196 197 int listenerCount = 20; 198 CountDownLatch listenerLatch = new CountDownLatch(listenerCount); 199 200 // Test that listeners added both before and after the value is available 201 // get called correctly. 202 for (int i = 0; i < 20; i++) { 203 204 // Right in the middle start up a thread to close the latch. 205 if (i == 10) { 206 new Thread(() -> latch.countDown()).start(); 207 } 208 209 future.addListener(listenerLatch::countDown, exec); 210 } 211 212 assertSame(Boolean.TRUE, future.get()); 213 // Wait for the listener latch to complete. 214 listenerLatch.await(500, MILLISECONDS); 215 216 exec.shutdown(); 217 exec.awaitTermination(500, MILLISECONDS); 218 } 219}