View Javadoc
1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License.  You may obtain a copy
6    * of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  
17  package com.google.common.util.concurrent.testing;
18  
19  import com.google.common.annotations.Beta;
20  import com.google.common.annotations.GwtIncompatible;
21  import com.google.common.util.concurrent.ListenableFuture;
22  import java.util.concurrent.CancellationException;
23  import java.util.concurrent.CountDownLatch;
24  import java.util.concurrent.ExecutionException;
25  import java.util.concurrent.ExecutorService;
26  import java.util.concurrent.Executors;
27  import java.util.concurrent.Future;
28  import java.util.concurrent.TimeUnit;
29  import java.util.concurrent.TimeoutException;
30  import junit.framework.TestCase;
31  
32  /**
33   * Abstract test case parent for anything implementing {@link ListenableFuture}.
34   * Tests the two get methods and the addListener method.
35   *
36   * @author Sven Mawson
37   * @since 10.0
38   */
39  @Beta
40  @GwtIncompatible
41  public abstract class AbstractListenableFutureTest extends TestCase {
42  
43    protected CountDownLatch latch;
44    protected ListenableFuture<Boolean> future;
45  
46    @Override
47    protected void setUp() throws Exception {
48  
49      // Create a latch and a future that waits on the latch.
50      latch = new CountDownLatch(1);
51      future = createListenableFuture(Boolean.TRUE, null, latch);
52    }
53  
54    @Override
55    protected void tearDown() throws Exception {
56  
57      // Make sure we have no waiting threads.
58      latch.countDown();
59    }
60  
61    /**
62     * Constructs a listenable future with a value available after the latch
63     * has counted down.
64     */
65    protected abstract <V> ListenableFuture<V> createListenableFuture(
66        V value, Exception except, CountDownLatch waitOn);
67  
68    /**
69     * Tests that the {@link Future#get()} method blocks until a value is
70     * available.
71     */
72    public void testGetBlocksUntilValueAvailable() throws Throwable {
73  
74      assertFalse(future.isDone());
75      assertFalse(future.isCancelled());
76  
77      final CountDownLatch successLatch = new CountDownLatch(1);
78      final Throwable[] badness = new Throwable[1];
79  
80      // Wait on the future in a separate thread.
81      new Thread(new Runnable() {
82        @Override
83        public void run() {
84          try {
85            assertSame(Boolean.TRUE, future.get());
86            successLatch.countDown();
87          } catch (Throwable t) {
88            t.printStackTrace();
89            badness[0] = t;
90          }
91        }}).start();
92  
93      // Release the future value.
94      latch.countDown();
95  
96      assertTrue(successLatch.await(10, TimeUnit.SECONDS));
97  
98      if (badness[0] != null) {
99        throw badness[0];
100     }
101 
102     assertTrue(future.isDone());
103     assertFalse(future.isCancelled());
104   }
105 
106   /**
107    * Tests that the {@link Future#get(long, TimeUnit)} method times out
108    * correctly.
109    */
110   public void testTimeoutOnGetWorksCorrectly() throws InterruptedException,
111       ExecutionException {
112 
113     // The task thread waits for the latch, so we expect a timeout here.
114     try {
115       future.get(20, TimeUnit.MILLISECONDS);
116       fail("Should have timed out trying to get the value.");
117     } catch (TimeoutException expected) {
118     } finally {
119       latch.countDown();
120     }
121   }
122 
123   /**
124    * Tests that a canceled future throws a cancellation exception.
125    *
126    * This method checks the cancel, isCancelled, and isDone methods.
127    */
128   public void testCanceledFutureThrowsCancellation() throws Exception {
129 
130     assertFalse(future.isDone());
131     assertFalse(future.isCancelled());
132 
133     final CountDownLatch successLatch = new CountDownLatch(1);
134 
135     // Run cancellation in a separate thread as an extra thread-safety test.
136     new Thread(new Runnable() {
137       @Override
138       public void run() {
139         try {
140           future.get();
141         } catch (CancellationException expected) {
142           successLatch.countDown();
143         } catch (Exception ignored) {
144           // All other errors are ignored, we expect a cancellation.
145         }
146       }
147     }).start();
148 
149     assertFalse(future.isDone());
150     assertFalse(future.isCancelled());
151 
152     future.cancel(true);
153 
154     assertTrue(future.isDone());
155     assertTrue(future.isCancelled());
156 
157     assertTrue(successLatch.await(200, TimeUnit.MILLISECONDS));
158 
159     latch.countDown();
160   }
161 
162   public void testListenersNotifiedOnError() throws Exception {
163     final CountDownLatch successLatch = new CountDownLatch(1);
164     final CountDownLatch listenerLatch = new CountDownLatch(1);
165 
166     ExecutorService exec = Executors.newCachedThreadPool();
167 
168     future.addListener(new Runnable() {
169       @Override
170       public void run() {
171         listenerLatch.countDown();
172       }
173     }, exec);
174 
175     new Thread(new Runnable() {
176       @Override
177       public void run() {
178         try {
179           future.get();
180         } catch (CancellationException expected) {
181           successLatch.countDown();
182         } catch (Exception ignored) {
183           // No success latch count down.
184         }
185       }
186     }).start();
187 
188     future.cancel(true);
189 
190     assertTrue(future.isCancelled());
191     assertTrue(future.isDone());
192 
193     assertTrue(successLatch.await(200, TimeUnit.MILLISECONDS));
194     assertTrue(listenerLatch.await(200, TimeUnit.MILLISECONDS));
195 
196     latch.countDown();
197 
198     exec.shutdown();
199     exec.awaitTermination(100, TimeUnit.MILLISECONDS);
200   }
201 
202   /**
203    * Tests that all listeners complete, even if they were added before or after
204    * the future was finishing.  Also acts as a concurrency test to make sure the
205    * locking is done correctly when a future is finishing so that no listeners
206    * can be lost.
207    */
208   public void testAllListenersCompleteSuccessfully()
209       throws InterruptedException, ExecutionException {
210 
211     ExecutorService exec = Executors.newCachedThreadPool();
212 
213     int listenerCount = 20;
214     final CountDownLatch listenerLatch = new CountDownLatch(listenerCount);
215 
216     // Test that listeners added both before and after the value is available
217     // get called correctly.
218     for (int i = 0; i < 20; i++) {
219 
220       // Right in the middle start up a thread to close the latch.
221       if (i == 10) {
222         new Thread(new Runnable() {
223           @Override
224           public void run() {
225             latch.countDown();
226           }
227         }).start();
228       }
229 
230       future.addListener(new Runnable() {
231         @Override
232         public void run() {
233           listenerLatch.countDown();
234         }
235       }, exec);
236     }
237 
238     assertSame(Boolean.TRUE, future.get());
239     // Wait for the listener latch to complete.
240     listenerLatch.await(500, TimeUnit.MILLISECONDS);
241 
242     exec.shutdown();
243     exec.awaitTermination(500, TimeUnit.MILLISECONDS);
244   }
245 }