Home | History | Annotate | Download | only in concurrent
      1 /*
      2  * Copyright (C) 2009 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.util.concurrent;
     18 
     19 import static com.google.common.base.Preconditions.checkState;
     20 import static com.google.common.util.concurrent.Futures.immediateFuture;
     21 import static com.google.common.util.concurrent.JdkFutureAdapters.listenInPoolThread;
     22 import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
     23 import static java.util.concurrent.Executors.newCachedThreadPool;
     24 import static java.util.concurrent.TimeUnit.SECONDS;
     25 
     26 import com.google.common.testing.NullPointerTester;
     27 import com.google.common.util.concurrent.FuturesTest.ExecutorSpy;
     28 import com.google.common.util.concurrent.FuturesTest.SingleCallListener;
     29 
     30 import junit.framework.AssertionFailedError;
     31 import junit.framework.TestCase;
     32 
     33 import java.util.concurrent.CountDownLatch;
     34 import java.util.concurrent.ExecutorService;
     35 import java.util.concurrent.Future;
     36 import java.util.concurrent.TimeUnit;
     37 
     38 /**
     39  * Unit tests for {@link JdkFutureAdapters}.
     40  *
     41  * @author Sven Mawson
     42  * @author Kurt Alfred Kluever
     43  */
     44 public class JdkFutureAdaptersTest extends TestCase {
     45   private static final String DATA1 = "data";
     46 
     47   public void testListenInPoolThreadReturnsSameFuture() throws Exception {
     48     ListenableFuture<String> listenableFuture = immediateFuture(DATA1);
     49     assertSame(listenableFuture, listenInPoolThread(listenableFuture));
     50   }
     51 
     52   public void testListenInPoolThreadIgnoresExecutorWhenDelegateIsDone()
     53       throws Exception {
     54     NonListenableSettableFuture<String> abstractFuture =
     55         NonListenableSettableFuture.create();
     56     abstractFuture.set(DATA1);
     57     ExecutorSpy spy = new ExecutorSpy(sameThreadExecutor());
     58     ListenableFuture<String> listenableFuture =
     59         listenInPoolThread(abstractFuture, spy);
     60 
     61     SingleCallListener singleCallListener = new SingleCallListener();
     62     singleCallListener.expectCall();
     63 
     64     assertFalse(spy.wasExecuted);
     65     assertFalse(singleCallListener.wasCalled());
     66     assertTrue(listenableFuture.isDone()); // We call AbstractFuture#set above.
     67 
     68     // #addListener() will run the listener immediately because the Future is
     69     // already finished (we explicitly set the result of it above).
     70     listenableFuture.addListener(singleCallListener, sameThreadExecutor());
     71     assertEquals(DATA1, listenableFuture.get());
     72 
     73     // 'spy' should have been ignored since 'abstractFuture' was done before
     74     // a listener was added.
     75     assertFalse(spy.wasExecuted);
     76     assertTrue(singleCallListener.wasCalled());
     77     assertTrue(listenableFuture.isDone());
     78   }
     79 
     80   public void testListenInPoolThreadUsesGivenExecutor() throws Exception {
     81     ExecutorService executorService = newCachedThreadPool(
     82         new ThreadFactoryBuilder().setDaemon(true).build());
     83     NonListenableSettableFuture<String> abstractFuture =
     84         NonListenableSettableFuture.create();
     85     ExecutorSpy spy = new ExecutorSpy(executorService);
     86     ListenableFuture<String> listenableFuture =
     87         listenInPoolThread(abstractFuture, spy);
     88 
     89     SingleCallListener singleCallListener = new SingleCallListener();
     90     singleCallListener.expectCall();
     91 
     92     assertFalse(spy.wasExecuted);
     93     assertFalse(singleCallListener.wasCalled());
     94     assertFalse(listenableFuture.isDone());
     95 
     96     listenableFuture.addListener(singleCallListener, executorService);
     97     abstractFuture.set(DATA1);
     98     assertEquals(DATA1, listenableFuture.get());
     99     singleCallListener.waitForCall();
    100 
    101     assertTrue(spy.wasExecuted);
    102     assertTrue(singleCallListener.wasCalled());
    103     assertTrue(listenableFuture.isDone());
    104   }
    105 
    106   /**
    107    * A Future that doesn't implement ListenableFuture, useful for testing
    108    * listenInPoolThread.
    109    */
    110   private static final class NonListenableSettableFuture<V>
    111       extends ForwardingFuture<V> {
    112     static <V> NonListenableSettableFuture<V> create() {
    113       return new NonListenableSettableFuture<V>();
    114     }
    115 
    116     final SettableFuture<V> delegate = SettableFuture.create();
    117 
    118     @Override protected Future<V> delegate() {
    119       return delegate;
    120     }
    121 
    122     void set(V value) {
    123       delegate.set(value);
    124     }
    125   }
    126 
    127   private static final class RuntimeExceptionThrowingFuture<V>
    128       implements Future<V> {
    129     final CountDownLatch allowGetToComplete = new CountDownLatch(1);
    130 
    131     @Override
    132     public boolean cancel(boolean mayInterruptIfRunning) {
    133       throw new AssertionFailedError();
    134     }
    135 
    136     @Override
    137     public V get() throws InterruptedException {
    138       /*
    139        * Wait a little to give us time to call addListener before the future's
    140        * value is set in addition to the call we'll make after then.
    141        */
    142       allowGetToComplete.await(1, SECONDS);
    143       throw new RuntimeException("expected, should be caught");
    144     }
    145 
    146     @Override
    147     public V get(long timeout, TimeUnit unit) {
    148       throw new AssertionFailedError();
    149     }
    150 
    151     @Override
    152     public boolean isCancelled() {
    153       throw new AssertionFailedError();
    154     }
    155 
    156     @Override
    157     public boolean isDone() {
    158       /*
    159        * If isDone is true during the call to listenInPoolThread,
    160        * listenInPoolThread doesn't start a thread. Make sure it's false the
    161        * first time through (and forever after, since no one else cares about
    162        * it).
    163        */
    164       return false;
    165     }
    166   }
    167 
    168   private static final class RecordingRunnable implements Runnable {
    169     final CountDownLatch wasRun = new CountDownLatch(1);
    170 
    171     // synchronized so that checkState works as expected.
    172     @Override
    173     public synchronized void run() {
    174       checkState(wasRun.getCount() > 0);
    175       wasRun.countDown();
    176     }
    177   }
    178 
    179   public void testListenInPoolThreadRunsListenerAfterRuntimeException()
    180       throws Exception {
    181     RuntimeExceptionThrowingFuture<String> input =
    182         new RuntimeExceptionThrowingFuture<String>();
    183     /*
    184      * The compiler recognizes that "input instanceof ListenableFuture" is
    185      * impossible. We want the test, though, in case that changes in the future,
    186      * so we use isInstance instead.
    187      */
    188     assertFalse("Can't test the main listenInPoolThread path "
    189         + "if the input is already a ListenableFuture",
    190         ListenableFuture.class.isInstance(input));
    191     ListenableFuture<String> listenable = listenInPoolThread(input);
    192     /*
    193      * This will occur before the waiting get() in the
    194      * listenInPoolThread-spawned thread completes:
    195      */
    196     RecordingRunnable earlyListener = new RecordingRunnable();
    197     listenable.addListener(earlyListener, sameThreadExecutor());
    198 
    199     input.allowGetToComplete.countDown();
    200     // Now give the get() thread time to finish:
    201     assertTrue(earlyListener.wasRun.await(1, SECONDS));
    202 
    203     // Now test an additional addListener call, which will be run in-thread:
    204     RecordingRunnable lateListener = new RecordingRunnable();
    205     listenable.addListener(lateListener, sameThreadExecutor());
    206     assertTrue(lateListener.wasRun.await(1, SECONDS));
    207   }
    208 
    209   public void testNullArguments() throws Exception {
    210     NullPointerTester tester = new NullPointerTester();
    211     tester.setDefault(Future.class, immediateFuture(DATA1));
    212     tester.testAllPublicStaticMethods(JdkFutureAdapters.class);
    213   }
    214 }
    215