/* * Copyright (C) 2011 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 org.junit.contrib.truth.Truth.ASSERT; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; /** * Tests for {@link AbstractFuture}. * * @author Brian Stoler */ public class AbstractFutureTest extends TestCase { public void testSuccess() throws ExecutionException, InterruptedException { final Object value = new Object(); assertSame(value, new AbstractFuture() { { set(value); } }.get()); } public void testException() throws InterruptedException { final Throwable failure = new Throwable(); AbstractFuture future = new AbstractFuture() { { setException(failure); } }; ExecutionException ee1 = getExpectingExecutionException(future); ExecutionException ee2 = getExpectingExecutionException(future); // Ensure we get a unique execution exception on each get assertNotSame(ee1, ee2); assertSame(failure, ee1.getCause()); assertSame(failure, ee2.getCause()); checkStackTrace(ee1); checkStackTrace(ee2); } public void testCancel_notDoneNoInterrupt() { InterruptibleFuture future = new InterruptibleFuture(); assertTrue(future.cancel(false)); assertTrue(future.isCancelled()); assertTrue(future.isDone()); assertFalse(future.wasInterrupted); } public void testCancel_notDoneInterrupt() { InterruptibleFuture future = new InterruptibleFuture(); assertTrue(future.cancel(true)); assertTrue(future.isCancelled()); assertTrue(future.isDone()); assertTrue(future.wasInterrupted); } public void testCancel_done() { AbstractFuture future = new AbstractFuture() { { set("foo"); } }; assertFalse(future.cancel(true)); assertFalse(future.isCancelled()); assertTrue(future.isDone()); } public void testCompletionFinishesWithDone() { ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 50000; i++) { final AbstractFuture future = new AbstractFuture() {}; final AtomicReference errorMessage = new AtomicReference(); executor.execute(new Runnable() { @Override public void run() { future.set("success"); if (!future.isDone()) { errorMessage.set("Set call exited before future was complete."); } } }); executor.execute(new Runnable() { @Override public void run() { future.setException(new IllegalArgumentException("failure")); if (!future.isDone()) { errorMessage.set("SetException call exited before future was complete."); } } }); executor.execute(new Runnable() { @Override public void run() { future.cancel(true); if (!future.isDone()) { errorMessage.set("Cancel call exited before future was complete."); } } }); try { future.get(); } catch (Throwable t) { // Ignore, we just wanted to block. } String error = errorMessage.get(); assertNull(error, error); } executor.shutdown(); } private void checkStackTrace(ExecutionException e) { // Our call site for get() should be in the trace. int index = findStackFrame( e, getClass().getName(), "getExpectingExecutionException"); ASSERT.that(index).isNotEqualTo(0); // Above our method should be the call to get(). Don't assert on the class // because it could be some superclass. ASSERT.that(e.getStackTrace()[index - 1].getMethodName()).isEqualTo("get"); } private static int findStackFrame( ExecutionException e, String clazz, String method) { StackTraceElement[] elements = e.getStackTrace(); for (int i = 0; i < elements.length; i++) { StackTraceElement element = elements[i]; if (element.getClassName().equals(clazz) && element.getMethodName().equals(method)) { return i; } } AssertionFailedError failure = new AssertionFailedError("Expected element " + clazz + "." + method + " not found in stack trace"); failure.initCause(e); throw failure; } private ExecutionException getExpectingExecutionException( AbstractFuture future) throws InterruptedException { try { String got = future.get(); fail("Expected exception but got " + got); } catch (ExecutionException e) { return e; } // unreachable, but compiler doesn't know that fail() always throws return null; } private static final class InterruptibleFuture extends AbstractFuture { boolean wasInterrupted; @Override protected void interruptTask() { wasInterrupted = true; } } }