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