| /* |
| * Copyright (C) 2015 The Guava Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.common.util.concurrent; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.util.concurrent.Futures.getDone; |
| import static com.google.common.util.concurrent.Futures.immediateFuture; |
| import static com.google.common.util.concurrent.MoreExecutors.directExecutor; |
| import static com.google.common.util.concurrent.Runnables.doNothing; |
| import static com.google.common.util.concurrent.TestPlatform.getDoneFromTimeoutOverload; |
| import static com.google.common.util.concurrent.TestPlatform.verifyGetOnPendingFuture; |
| import static com.google.common.util.concurrent.TestPlatform.verifyTimedGetOnPendingFuture; |
| import static java.util.concurrent.TimeUnit.NANOSECONDS; |
| import static java.util.concurrent.TimeUnit.SECONDS; |
| |
| import com.google.common.annotations.GwtCompatible; |
| import com.google.common.annotations.GwtIncompatible; |
| import com.google.common.util.concurrent.AbstractFutureTest.TimedWaiterThread; |
| import java.util.concurrent.CancellationException; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.TimeoutException; |
| import junit.framework.TestCase; |
| |
| /** |
| * Base class for tests for emulated {@link AbstractFuture} that allow subclasses to swap in a |
| * different "source Future" for {@link AbstractFuture#setFuture} calls. |
| */ |
| @GwtCompatible(emulated = true) |
| abstract class AbstractAbstractFutureTest extends TestCase { |
| private TestedFuture<Integer> future; |
| private AbstractFuture<Integer> delegate; |
| |
| abstract AbstractFuture<Integer> newDelegate(); |
| |
| @Override |
| protected void setUp() { |
| future = TestedFuture.create(); |
| delegate = newDelegate(); |
| } |
| |
| public void testPending() { |
| assertPending(future); |
| } |
| |
| public void testSuccessful() throws Exception { |
| assertThat(future.set(1)).isTrue(); |
| assertSuccessful(future, 1); |
| } |
| |
| public void testFailed() throws Exception { |
| Exception cause = new Exception(); |
| assertThat(future.setException(cause)).isTrue(); |
| assertFailed(future, cause); |
| } |
| |
| public void testCanceled() throws Exception { |
| assertThat(future.cancel(false /* mayInterruptIfRunning */)).isTrue(); |
| assertCancelled(future, false); |
| } |
| |
| public void testInterrupted() throws Exception { |
| assertThat(future.cancel(true /* mayInterruptIfRunning */)).isTrue(); |
| assertCancelled(future, true); |
| } |
| |
| public void testSetFuturePending() throws Exception { |
| assertThat(future.setFuture(delegate)).isTrue(); |
| assertSetAsynchronously(future); |
| } |
| |
| public void testSetFutureThenCancel() throws Exception { |
| assertThat(future.setFuture(delegate)).isTrue(); |
| assertThat(future.cancel(false /* mayInterruptIfRunning */)).isTrue(); |
| assertCancelled(future, false); |
| assertCancelled(delegate, false); |
| } |
| |
| public void testSetFutureThenInterrupt() throws Exception { |
| assertThat(future.setFuture(delegate)).isTrue(); |
| assertThat(future.cancel(true /* mayInterruptIfRunning */)).isTrue(); |
| assertCancelled(future, true); |
| assertCancelled(delegate, true); |
| } |
| |
| public void testSetFutureDelegateAlreadySuccessful() throws Exception { |
| delegate.set(5); |
| assertThat(future.setFuture(delegate)).isTrue(); |
| assertSuccessful(future, 5); |
| } |
| |
| public void testSetFutureDelegateLaterSuccessful() throws Exception { |
| assertThat(future.setFuture(delegate)).isTrue(); |
| delegate.set(6); |
| assertSuccessful(future, 6); |
| } |
| |
| public void testSetFutureDelegateAlreadyCancelled() throws Exception { |
| delegate.cancel( |
| false |
| /** mayInterruptIfRunning */ |
| ); |
| assertThat(future.setFuture(delegate)).isTrue(); |
| assertCancelled(future, false); |
| } |
| |
| public void testSetFutureDelegateLaterCancelled() throws Exception { |
| assertThat(future.setFuture(delegate)).isTrue(); |
| delegate.cancel( |
| false |
| /** mayInterruptIfRunning */ |
| ); |
| assertCancelled(future, false); |
| } |
| |
| public void testSetFutureDelegateAlreadyInterrupted() throws Exception { |
| delegate.cancel( |
| true |
| /** mayInterruptIfRunning */ |
| ); |
| assertThat(future.setFuture(delegate)).isTrue(); |
| assertCancelled(future, /* expectWasInterrupted= */ false); |
| } |
| |
| public void testSetFutureDelegateLaterInterrupted() throws Exception { |
| assertThat(future.setFuture(delegate)).isTrue(); |
| delegate.cancel( |
| true |
| /** mayInterruptIfRunning */ |
| ); |
| assertCancelled(future, /* expectWasInterrupted= */ false); |
| } |
| |
| public void testListenLaterSuccessful() { |
| CountingRunnable listener = new CountingRunnable(); |
| |
| future.addListener(listener, directExecutor()); |
| listener.assertNotRun(); |
| |
| future.set(1); |
| listener.assertRun(); |
| } |
| |
| public void testListenLaterFailed() { |
| CountingRunnable listener = new CountingRunnable(); |
| |
| future.addListener(listener, directExecutor()); |
| listener.assertNotRun(); |
| |
| future.setException(new Exception()); |
| listener.assertRun(); |
| } |
| |
| public void testListenLaterCancelled() { |
| CountingRunnable listener = new CountingRunnable(); |
| |
| future.addListener(listener, directExecutor()); |
| listener.assertNotRun(); |
| |
| future.cancel(false); |
| listener.assertRun(); |
| } |
| |
| public void testListenLaterInterrupted() { |
| CountingRunnable listener = new CountingRunnable(); |
| |
| future.addListener(listener, directExecutor()); |
| listener.assertNotRun(); |
| |
| future.cancel(true); |
| listener.assertRun(); |
| } |
| |
| public void testListenLaterSetAsynchronously() { |
| CountingRunnable listener = new CountingRunnable(); |
| |
| future.addListener(listener, directExecutor()); |
| listener.assertNotRun(); |
| |
| future.setFuture(delegate); |
| listener.assertNotRun(); |
| } |
| |
| public void testListenLaterSetAsynchronouslyLaterDelegateSuccessful() { |
| CountingRunnable before = new CountingRunnable(); |
| CountingRunnable inBetween = new CountingRunnable(); |
| CountingRunnable after = new CountingRunnable(); |
| |
| future.addListener(before, directExecutor()); |
| future.setFuture(delegate); |
| future.addListener(inBetween, directExecutor()); |
| delegate.set(1); |
| future.addListener(after, directExecutor()); |
| |
| before.assertRun(); |
| inBetween.assertRun(); |
| after.assertRun(); |
| } |
| |
| public void testListenLaterSetAsynchronouslyLaterDelegateFailed() { |
| CountingRunnable before = new CountingRunnable(); |
| CountingRunnable inBetween = new CountingRunnable(); |
| CountingRunnable after = new CountingRunnable(); |
| |
| future.addListener(before, directExecutor()); |
| future.setFuture(delegate); |
| future.addListener(inBetween, directExecutor()); |
| delegate.setException(new Exception()); |
| future.addListener(after, directExecutor()); |
| |
| before.assertRun(); |
| inBetween.assertRun(); |
| after.assertRun(); |
| } |
| |
| public void testListenLaterSetAsynchronouslyLaterDelegateCancelled() { |
| CountingRunnable before = new CountingRunnable(); |
| CountingRunnable inBetween = new CountingRunnable(); |
| CountingRunnable after = new CountingRunnable(); |
| |
| future.addListener(before, directExecutor()); |
| future.setFuture(delegate); |
| future.addListener(inBetween, directExecutor()); |
| delegate.cancel(false); |
| future.addListener(after, directExecutor()); |
| |
| before.assertRun(); |
| inBetween.assertRun(); |
| after.assertRun(); |
| } |
| |
| public void testListenLaterSetAsynchronouslyLaterDelegateInterrupted() { |
| CountingRunnable before = new CountingRunnable(); |
| CountingRunnable inBetween = new CountingRunnable(); |
| CountingRunnable after = new CountingRunnable(); |
| |
| future.addListener(before, directExecutor()); |
| future.setFuture(delegate); |
| future.addListener(inBetween, directExecutor()); |
| delegate.cancel(true); |
| future.addListener(after, directExecutor()); |
| |
| before.assertRun(); |
| inBetween.assertRun(); |
| after.assertRun(); |
| } |
| |
| public void testListenLaterSetAsynchronouslyLaterSelfCancelled() { |
| CountingRunnable before = new CountingRunnable(); |
| CountingRunnable inBetween = new CountingRunnable(); |
| CountingRunnable after = new CountingRunnable(); |
| |
| future.addListener(before, directExecutor()); |
| future.setFuture(delegate); |
| future.addListener(inBetween, directExecutor()); |
| future.cancel(false); |
| future.addListener(after, directExecutor()); |
| |
| before.assertRun(); |
| inBetween.assertRun(); |
| after.assertRun(); |
| } |
| |
| public void testListenLaterSetAsynchronouslyLaterSelfInterrupted() { |
| CountingRunnable before = new CountingRunnable(); |
| CountingRunnable inBetween = new CountingRunnable(); |
| CountingRunnable after = new CountingRunnable(); |
| |
| future.addListener(before, directExecutor()); |
| future.setFuture(delegate); |
| future.addListener(inBetween, directExecutor()); |
| future.cancel(true); |
| future.addListener(after, directExecutor()); |
| |
| before.assertRun(); |
| inBetween.assertRun(); |
| after.assertRun(); |
| } |
| |
| public void testMisbehavingListenerAlreadyDone() { |
| class BadRunnableException extends RuntimeException {} |
| |
| Runnable bad = |
| new Runnable() { |
| @Override |
| public void run() { |
| throw new BadRunnableException(); |
| } |
| }; |
| |
| future.set(1); |
| future.addListener(bad, directExecutor()); // BadRunnableException must not propagate. |
| } |
| |
| public void testMisbehavingListenerLaterDone() { |
| class BadRunnableException extends RuntimeException {} |
| |
| CountingRunnable before = new CountingRunnable(); |
| Runnable bad = |
| new Runnable() { |
| @Override |
| public void run() { |
| throw new BadRunnableException(); |
| } |
| }; |
| CountingRunnable after = new CountingRunnable(); |
| |
| future.addListener(before, directExecutor()); |
| future.addListener(bad, directExecutor()); |
| future.addListener(after, directExecutor()); |
| |
| future.set(1); // BadRunnableException must not propagate. |
| |
| before.assertRun(); |
| after.assertRun(); |
| } |
| |
| public void testNullListener() { |
| try { |
| future.addListener(null, directExecutor()); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| public void testNullExecutor() { |
| try { |
| future.addListener(doNothing(), null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| public void testNullTimeUnit() throws Exception { |
| future.set(1); |
| try { |
| future.get(0, null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| public void testNegativeTimeout() throws Exception { |
| future.set(1); |
| assertEquals(1, future.get(-1, SECONDS).intValue()); |
| } |
| |
| @GwtIncompatible // threads |
| |
| public void testOverflowTimeout() throws Exception { |
| // First, sanity check that naive multiplication would really overflow to a negative number: |
| long nanosPerSecond = NANOSECONDS.convert(1, SECONDS); |
| assertThat(nanosPerSecond * Long.MAX_VALUE).isLessThan(0L); |
| |
| // Check that we wait long enough anyway (presumably as long as MAX_VALUE nanos): |
| TimedWaiterThread waiter = new TimedWaiterThread(future, Long.MAX_VALUE, SECONDS); |
| waiter.start(); |
| waiter.awaitWaiting(); |
| |
| future.set(1); |
| waiter.join(); |
| } |
| |
| public void testSetNull() throws Exception { |
| future.set(null); |
| assertSuccessful(future, null); |
| } |
| |
| public void testSetExceptionNull() throws Exception { |
| try { |
| future.setException(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| assertThat(future.isDone()).isFalse(); |
| assertThat(future.set(1)).isTrue(); |
| assertSuccessful(future, 1); |
| } |
| |
| public void testSetFutureNull() throws Exception { |
| try { |
| future.setFuture(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| assertThat(future.isDone()).isFalse(); |
| assertThat(future.set(1)).isTrue(); |
| assertSuccessful(future, 1); |
| } |
| |
| /** Concrete subclass for testing. */ |
| private static class TestedFuture<V> extends AbstractFuture<V> { |
| private static <V> TestedFuture<V> create() { |
| return new TestedFuture<V>(); |
| } |
| } |
| |
| private static final class CountingRunnable implements Runnable { |
| int count; |
| |
| @Override |
| public void run() { |
| count++; |
| } |
| |
| void assertNotRun() { |
| assertEquals(0, count); |
| } |
| |
| void assertRun() { |
| assertEquals(1, count); |
| } |
| } |
| |
| private static void assertSetAsynchronously(AbstractFuture<Integer> future) { |
| assertCannotSet(future); |
| assertPending(future); |
| } |
| |
| private static void assertPending(AbstractFuture<Integer> future) { |
| assertThat(future.isDone()).isFalse(); |
| assertThat(future.isCancelled()).isFalse(); |
| |
| CountingRunnable listener = new CountingRunnable(); |
| future.addListener(listener, directExecutor()); |
| listener.assertNotRun(); |
| |
| verifyGetOnPendingFuture(future); |
| verifyTimedGetOnPendingFuture(future); |
| } |
| |
| private static void assertSuccessful(AbstractFuture<Integer> future, Integer expectedResult) |
| throws InterruptedException, TimeoutException, ExecutionException { |
| assertDone(future); |
| assertThat(future.isCancelled()).isFalse(); |
| |
| assertThat(getDone(future)).isEqualTo(expectedResult); |
| assertThat(getDoneFromTimeoutOverload(future)).isEqualTo(expectedResult); |
| } |
| |
| private static void assertFailed(AbstractFuture<Integer> future, Throwable expectedException) |
| throws InterruptedException, TimeoutException { |
| assertDone(future); |
| assertThat(future.isCancelled()).isFalse(); |
| |
| try { |
| getDone(future); |
| fail(); |
| } catch (ExecutionException e) { |
| assertThat(e.getCause()).isSameInstanceAs(expectedException); |
| } |
| |
| try { |
| getDoneFromTimeoutOverload(future); |
| fail(); |
| } catch (ExecutionException e) { |
| assertThat(e).hasCauseThat().isSameInstanceAs(expectedException); |
| } |
| } |
| |
| private static void assertCancelled(AbstractFuture<Integer> future, boolean expectWasInterrupted) |
| throws InterruptedException, TimeoutException, ExecutionException { |
| assertDone(future); |
| assertThat(future.isCancelled()).isTrue(); |
| assertThat(future.wasInterrupted()).isEqualTo(expectWasInterrupted); |
| |
| try { |
| getDone(future); |
| fail(); |
| } catch (CancellationException expected) { |
| } |
| |
| try { |
| getDoneFromTimeoutOverload(future); |
| fail(); |
| } catch (CancellationException expected) { |
| } |
| } |
| |
| private static void assertDone(AbstractFuture<Integer> future) { |
| CountingRunnable listener = new CountingRunnable(); |
| future.addListener(listener, directExecutor()); |
| listener.assertRun(); |
| |
| assertThat(future.isDone()).isTrue(); |
| assertCannotSet(future); |
| assertCannotCancel(future); |
| } |
| |
| private static void assertCannotSet(AbstractFuture<Integer> future) { |
| assertThat(future.set(99)).isFalse(); |
| assertThat(future.setException(new IndexOutOfBoundsException())).isFalse(); |
| assertThat(future.setFuture(new AbstractFuture<Integer>() {})).isFalse(); |
| assertThat(future.setFuture(immediateFuture(99))).isFalse(); |
| } |
| |
| private static void assertCannotCancel(AbstractFuture<Integer> future) { |
| assertThat(future.cancel(true)).isFalse(); |
| assertThat(future.cancel(false)).isFalse(); |
| } |
| } |