1 /* 2 * Copyright (C) 2011 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 org.junit.contrib.truth.Truth.ASSERT; 20 21 import junit.framework.AssertionFailedError; 22 import junit.framework.TestCase; 23 24 import java.util.concurrent.ExecutionException; 25 import java.util.concurrent.ExecutorService; 26 import java.util.concurrent.Executors; 27 import java.util.concurrent.atomic.AtomicReference; 28 29 /** 30 * Tests for {@link AbstractFuture}. 31 * 32 * @author Brian Stoler 33 */ 34 35 public class AbstractFutureTest extends TestCase { testSuccess()36 public void testSuccess() throws ExecutionException, InterruptedException { 37 final Object value = new Object(); 38 assertSame(value, new AbstractFuture<Object>() { 39 { 40 set(value); 41 } 42 }.get()); 43 } 44 testException()45 public void testException() throws InterruptedException { 46 final Throwable failure = new Throwable(); 47 AbstractFuture<String> future = new AbstractFuture<String>() { 48 { 49 setException(failure); 50 } 51 }; 52 53 ExecutionException ee1 = getExpectingExecutionException(future); 54 ExecutionException ee2 = getExpectingExecutionException(future); 55 56 // Ensure we get a unique execution exception on each get 57 assertNotSame(ee1, ee2); 58 59 assertSame(failure, ee1.getCause()); 60 assertSame(failure, ee2.getCause()); 61 62 checkStackTrace(ee1); 63 checkStackTrace(ee2); 64 } 65 testCancel_notDoneNoInterrupt()66 public void testCancel_notDoneNoInterrupt() { 67 InterruptibleFuture future = new InterruptibleFuture(); 68 assertTrue(future.cancel(false)); 69 assertTrue(future.isCancelled()); 70 assertTrue(future.isDone()); 71 assertFalse(future.wasInterrupted); 72 } 73 testCancel_notDoneInterrupt()74 public void testCancel_notDoneInterrupt() { 75 InterruptibleFuture future = new InterruptibleFuture(); 76 assertTrue(future.cancel(true)); 77 assertTrue(future.isCancelled()); 78 assertTrue(future.isDone()); 79 assertTrue(future.wasInterrupted); 80 } 81 testCancel_done()82 public void testCancel_done() { 83 AbstractFuture<String> future = new AbstractFuture<String>() { 84 { 85 set("foo"); 86 } 87 }; 88 assertFalse(future.cancel(true)); 89 assertFalse(future.isCancelled()); 90 assertTrue(future.isDone()); 91 } 92 testCompletionFinishesWithDone()93 public void testCompletionFinishesWithDone() { 94 ExecutorService executor = Executors.newFixedThreadPool(10); 95 for (int i = 0; i < 50000; i++) { 96 final AbstractFuture<String> future = new AbstractFuture<String>() {}; 97 final AtomicReference<String> errorMessage = new AtomicReference<String>(); 98 executor.execute(new Runnable() { 99 @Override 100 public void run() { 101 future.set("success"); 102 if (!future.isDone()) { 103 errorMessage.set("Set call exited before future was complete."); 104 } 105 } 106 }); 107 executor.execute(new Runnable() { 108 @Override 109 public void run() { 110 future.setException(new IllegalArgumentException("failure")); 111 if (!future.isDone()) { 112 errorMessage.set("SetException call exited before future was complete."); 113 } 114 } 115 }); 116 executor.execute(new Runnable() { 117 @Override 118 public void run() { 119 future.cancel(true); 120 if (!future.isDone()) { 121 errorMessage.set("Cancel call exited before future was complete."); 122 } 123 } 124 }); 125 try { 126 future.get(); 127 } catch (Throwable t) { 128 // Ignore, we just wanted to block. 129 } 130 String error = errorMessage.get(); 131 assertNull(error, error); 132 } 133 executor.shutdown(); 134 } 135 checkStackTrace(ExecutionException e)136 private void checkStackTrace(ExecutionException e) { 137 // Our call site for get() should be in the trace. 138 int index = findStackFrame( 139 e, getClass().getName(), "getExpectingExecutionException"); 140 141 ASSERT.that(index).isNotEqualTo(0); 142 143 // Above our method should be the call to get(). Don't assert on the class 144 // because it could be some superclass. 145 ASSERT.that(e.getStackTrace()[index - 1].getMethodName()).isEqualTo("get"); 146 } 147 findStackFrame( ExecutionException e, String clazz, String method)148 private static int findStackFrame( 149 ExecutionException e, String clazz, String method) { 150 StackTraceElement[] elements = e.getStackTrace(); 151 for (int i = 0; i < elements.length; i++) { 152 StackTraceElement element = elements[i]; 153 if (element.getClassName().equals(clazz) 154 && element.getMethodName().equals(method)) { 155 return i; 156 } 157 } 158 AssertionFailedError failure = 159 new AssertionFailedError("Expected element " + clazz + "." + method 160 + " not found in stack trace"); 161 failure.initCause(e); 162 throw failure; 163 } 164 getExpectingExecutionException( AbstractFuture<String> future)165 private ExecutionException getExpectingExecutionException( 166 AbstractFuture<String> future) throws InterruptedException { 167 try { 168 String got = future.get(); 169 fail("Expected exception but got " + got); 170 } catch (ExecutionException e) { 171 return e; 172 } 173 174 // unreachable, but compiler doesn't know that fail() always throws 175 return null; 176 } 177 178 private static final class InterruptibleFuture 179 extends AbstractFuture<String> { 180 boolean wasInterrupted; 181 interruptTask()182 @Override protected void interruptTask() { 183 wasInterrupted = true; 184 } 185 } 186 } 187