• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
testListenInPoolThreadReturnsSameFuture()47   public void testListenInPoolThreadReturnsSameFuture() throws Exception {
48     ListenableFuture<String> listenableFuture = immediateFuture(DATA1);
49     assertSame(listenableFuture, listenInPoolThread(listenableFuture));
50   }
51 
testListenInPoolThreadIgnoresExecutorWhenDelegateIsDone()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 
testListenInPoolThreadUsesGivenExecutor()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> {
create()112     static <V> NonListenableSettableFuture<V> create() {
113       return new NonListenableSettableFuture<V>();
114     }
115 
116     final SettableFuture<V> delegate = SettableFuture.create();
117 
delegate()118     @Override protected Future<V> delegate() {
119       return delegate;
120     }
121 
set(V value)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
cancel(boolean mayInterruptIfRunning)132     public boolean cancel(boolean mayInterruptIfRunning) {
133       throw new AssertionFailedError();
134     }
135 
136     @Override
get()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
get(long timeout, TimeUnit unit)147     public V get(long timeout, TimeUnit unit) {
148       throw new AssertionFailedError();
149     }
150 
151     @Override
isCancelled()152     public boolean isCancelled() {
153       throw new AssertionFailedError();
154     }
155 
156     @Override
isDone()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
run()173     public synchronized void run() {
174       checkState(wasRun.getCount() > 0);
175       wasRun.countDown();
176     }
177   }
178 
testListenInPoolThreadRunsListenerAfterRuntimeException()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 
testNullArguments()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