Home | History | Annotate | Download | only in testing
      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.util.concurrent.ListenableFuture;
     21 
     22 import junit.framework.TestCase;
     23 
     24 import java.util.concurrent.CancellationException;
     25 import java.util.concurrent.CountDownLatch;
     26 import java.util.concurrent.ExecutionException;
     27 import java.util.concurrent.ExecutorService;
     28 import java.util.concurrent.Executors;
     29 import java.util.concurrent.Future;
     30 import java.util.concurrent.TimeUnit;
     31 import java.util.concurrent.TimeoutException;
     32 
     33 /**
     34  * Abstract test case parent for anything implementing {@link ListenableFuture}.
     35  * Tests the two get methods and the addListener method.
     36  *
     37  * @author Sven Mawson
     38  * @since 10.0
     39  */
     40 @Beta
     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       // Expected.
    119     } finally {
    120       latch.countDown();
    121     }
    122   }
    123 
    124   /**
    125    * Tests that a canceled future throws a cancellation exception.
    126    *
    127    * This method checks the cancel, isCancelled, and isDone methods.
    128    */
    129   public void testCanceledFutureThrowsCancellation() throws Exception {
    130 
    131     assertFalse(future.isDone());
    132     assertFalse(future.isCancelled());
    133 
    134     final CountDownLatch successLatch = new CountDownLatch(1);
    135 
    136     // Run cancellation in a separate thread as an extra thread-safety test.
    137     new Thread(new Runnable() {
    138       @Override
    139       public void run() {
    140         try {
    141           future.get();
    142         } catch (CancellationException expected) {
    143           successLatch.countDown();
    144         } catch (Exception ignored) {
    145           // All other errors are ignored, we expect a cancellation.
    146         }
    147       }
    148     }).start();
    149 
    150     assertFalse(future.isDone());
    151     assertFalse(future.isCancelled());
    152 
    153     future.cancel(true);
    154 
    155     assertTrue(future.isDone());
    156     assertTrue(future.isCancelled());
    157 
    158     assertTrue(successLatch.await(200, TimeUnit.MILLISECONDS));
    159 
    160     latch.countDown();
    161   }
    162 
    163   public void testListenersNotifiedOnError() throws Exception {
    164     final CountDownLatch successLatch = new CountDownLatch(1);
    165     final CountDownLatch listenerLatch = new CountDownLatch(1);
    166 
    167     ExecutorService exec = Executors.newCachedThreadPool();
    168 
    169     future.addListener(new Runnable() {
    170       @Override
    171       public void run() {
    172         listenerLatch.countDown();
    173       }
    174     }, exec);
    175 
    176     new Thread(new Runnable() {
    177       @Override
    178       public void run() {
    179         try {
    180           future.get();
    181         } catch (CancellationException expected) {
    182           successLatch.countDown();
    183         } catch (Exception ignored) {
    184           // No success latch count down.
    185         }
    186       }
    187     }).start();
    188 
    189     future.cancel(true);
    190 
    191     assertTrue(future.isCancelled());
    192     assertTrue(future.isDone());
    193 
    194     assertTrue(successLatch.await(200, TimeUnit.MILLISECONDS));
    195     assertTrue(listenerLatch.await(200, TimeUnit.MILLISECONDS));
    196 
    197     latch.countDown();
    198 
    199     exec.shutdown();
    200     exec.awaitTermination(100, TimeUnit.MILLISECONDS);
    201   }
    202 
    203   /**
    204    * Tests that all listeners complete, even if they were added before or after
    205    * the future was finishing.  Also acts as a concurrency test to make sure the
    206    * locking is done correctly when a future is finishing so that no listeners
    207    * can be lost.
    208    */
    209   public void testAllListenersCompleteSuccessfully()
    210       throws InterruptedException, ExecutionException {
    211 
    212     ExecutorService exec = Executors.newCachedThreadPool();
    213 
    214     int listenerCount = 20;
    215     final CountDownLatch listenerLatch = new CountDownLatch(listenerCount);
    216 
    217     // Test that listeners added both before and after the value is available
    218     // get called correctly.
    219     for (int i = 0; i < 20; i++) {
    220 
    221       // Right in the middle start up a thread to close the latch.
    222       if (i == 10) {
    223         new Thread(new Runnable() {
    224           @Override
    225           public void run() {
    226             latch.countDown();
    227           }
    228         }).start();
    229       }
    230 
    231       future.addListener(new Runnable() {
    232         @Override
    233         public void run() {
    234           listenerLatch.countDown();
    235         }
    236       }, exec);
    237     }
    238 
    239     assertSame(Boolean.TRUE, future.get());
    240     // Wait for the listener latch to complete.
    241     listenerLatch.await(500, TimeUnit.MILLISECONDS);
    242 
    243     exec.shutdown();
    244     exec.awaitTermination(500, TimeUnit.MILLISECONDS);
    245   }
    246 }
    247