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 com.google.common.base.Preconditions; 20 21 import junit.framework.TestCase; 22 23 import org.mockito.Mockito; 24 25 import java.util.concurrent.CancellationException; 26 import java.util.concurrent.Executor; 27 import java.util.concurrent.Future; 28 import java.util.concurrent.TimeUnit; 29 30 import javax.annotation.Nullable; 31 32 /** 33 * Test for {@link FutureCallback}. 34 * 35 * @author Anthony Zana 36 */ 37 public class FutureCallbackTest extends TestCase { testSameThreadSuccess()38 public void testSameThreadSuccess() { 39 SettableFuture<String> f = SettableFuture.create(); 40 MockCallback callback = new MockCallback("foo"); 41 Futures.addCallback(f, callback); 42 f.set("foo"); 43 } 44 testExecutorSuccess()45 public void testExecutorSuccess() { 46 CountingSameThreadExecutor ex = new CountingSameThreadExecutor(); 47 SettableFuture<String> f = SettableFuture.create(); 48 MockCallback callback = new MockCallback("foo"); 49 Futures.addCallback(f, callback, ex); 50 f.set("foo"); 51 assertEquals(1, ex.runCount); 52 } 53 54 // Error cases testSameThreadExecutionException()55 public void testSameThreadExecutionException() { 56 SettableFuture<String> f = SettableFuture.create(); 57 Exception e = new IllegalArgumentException("foo not found"); 58 MockCallback callback = new MockCallback(e); 59 Futures.addCallback(f, callback); 60 f.setException(e); 61 } 62 testCancel()63 public void testCancel() { 64 SettableFuture<String> f = SettableFuture.create(); 65 FutureCallback<String> callback = 66 new FutureCallback<String>() { 67 private boolean called = false; 68 @Override 69 public void onSuccess(String result) { 70 fail("Was not expecting onSuccess() to be called."); 71 } 72 73 @Override 74 public synchronized void onFailure(Throwable t) { 75 assertFalse(called); 76 assertTrue(t instanceof CancellationException); 77 called = true; 78 } 79 }; 80 Futures.addCallback(f, callback); 81 f.cancel(true); 82 } 83 testThrowErrorFromGet()84 public void testThrowErrorFromGet() { 85 Error error = new AssertionError("ASSERT!"); 86 ListenableFuture<String> f = ThrowingFuture.throwingError(error); 87 MockCallback callback = new MockCallback(error); 88 Futures.addCallback(f, callback); 89 } 90 testRuntimeExeceptionFromGet()91 public void testRuntimeExeceptionFromGet() { 92 RuntimeException e = new IllegalArgumentException("foo not found"); 93 ListenableFuture<String> f = ThrowingFuture.throwingRuntimeException(e); 94 MockCallback callback = new MockCallback(e); 95 Futures.addCallback(f, callback); 96 } 97 testOnSuccessThrowsRuntimeException()98 public void testOnSuccessThrowsRuntimeException() throws Exception { 99 RuntimeException exception = new RuntimeException(); 100 String result = "result"; 101 SettableFuture<String> future = SettableFuture.create(); 102 @SuppressWarnings("unchecked") // Safe for a mock 103 FutureCallback<String> callback = Mockito.mock(FutureCallback.class); 104 Futures.addCallback(future, callback); 105 Mockito.doThrow(exception).when(callback).onSuccess(result); 106 future.set(result); 107 assertEquals(result, future.get()); 108 Mockito.verify(callback).onSuccess(result); 109 Mockito.verifyNoMoreInteractions(callback); 110 } 111 testOnSuccessThrowsError()112 public void testOnSuccessThrowsError() throws Exception { 113 class TestError extends Error {} 114 TestError error = new TestError(); 115 String result = "result"; 116 SettableFuture<String> future = SettableFuture.create(); 117 @SuppressWarnings("unchecked") // Safe for a mock 118 FutureCallback<String> callback = Mockito.mock(FutureCallback.class); 119 Futures.addCallback(future, callback); 120 Mockito.doThrow(error).when(callback).onSuccess(result); 121 try { 122 future.set(result); 123 fail("Should have thrown"); 124 } catch (TestError e) { 125 assertSame(error, e); 126 } 127 assertEquals(result, future.get()); 128 Mockito.verify(callback).onSuccess(result); 129 Mockito.verifyNoMoreInteractions(callback); 130 } 131 testWildcardFuture()132 public void testWildcardFuture() { 133 SettableFuture<String> settable = SettableFuture.create(); 134 ListenableFuture<?> f = settable; 135 FutureCallback<Object> callback = new FutureCallback<Object>() { 136 @Override 137 public void onSuccess(Object result) {} 138 139 @Override 140 public void onFailure(Throwable t) {} 141 }; 142 Futures.addCallback(f, callback); 143 } 144 145 private class CountingSameThreadExecutor implements Executor { 146 int runCount = 0; 147 @Override execute(Runnable command)148 public void execute(Runnable command) { 149 command.run(); 150 runCount++; 151 } 152 } 153 154 // TODO(user): Move to testing, unify with RuntimeExceptionThrowingFuture 155 156 /** 157 * A {@link Future} implementation which always throws directly from calls to 158 * get() (i.e. not wrapped in ExecutionException. 159 * For just a normal Future failure, use {@link SettableFuture}). 160 * 161 * <p>Useful for testing the behavior of Future utilities against odd futures. 162 * 163 * @author Anthony Zana 164 */ 165 private static class ThrowingFuture<V> implements ListenableFuture<V> { 166 private final Error error; 167 private final RuntimeException runtime; 168 throwingError(Error error)169 public static <V> ListenableFuture<V> throwingError(Error error) { 170 return new ThrowingFuture<V>(error); 171 } 172 173 public static <V> ListenableFuture<V> throwingRuntimeException(RuntimeException e)174 throwingRuntimeException(RuntimeException e) { 175 return new ThrowingFuture<V>(e); 176 } 177 ThrowingFuture(Error error)178 private ThrowingFuture(Error error) { 179 this.error = Preconditions.checkNotNull(error); 180 this.runtime = null; 181 } 182 ThrowingFuture(RuntimeException e)183 public ThrowingFuture(RuntimeException e) { 184 this.runtime = Preconditions.checkNotNull(e); 185 this.error = null; 186 } 187 188 @Override cancel(boolean mayInterruptIfRunning)189 public boolean cancel(boolean mayInterruptIfRunning) { 190 return false; 191 } 192 193 @Override isCancelled()194 public boolean isCancelled() { 195 return false; 196 } 197 198 @Override isDone()199 public boolean isDone() { 200 return true; 201 } 202 203 @Override get()204 public V get() { 205 throwOnGet(); 206 throw new AssertionError("Unreachable"); 207 } 208 209 @Override get(long timeout, TimeUnit unit)210 public V get(long timeout, TimeUnit unit) { 211 throwOnGet(); 212 throw new AssertionError("Unreachable"); 213 } 214 215 @Override addListener(Runnable listener, Executor executor)216 public void addListener(Runnable listener, Executor executor) { 217 executor.execute(listener); 218 } 219 throwOnGet()220 private void throwOnGet() { 221 if (error != null) { 222 throw error; 223 } else { 224 throw runtime; 225 } 226 } 227 } 228 229 private final class MockCallback implements FutureCallback<String> { 230 @Nullable private String value = null; 231 @Nullable private Throwable failure = null; 232 private boolean wasCalled = false; 233 MockCallback(String expectedValue)234 MockCallback(String expectedValue) { 235 this.value = expectedValue; 236 } 237 MockCallback(Throwable expectedFailure)238 public MockCallback(Throwable expectedFailure) { 239 this.failure = expectedFailure; 240 } 241 242 @Override onSuccess(String result)243 public synchronized void onSuccess(String result) { 244 assertFalse(wasCalled); 245 wasCalled = true; 246 assertEquals(value, result); 247 } 248 249 @Override onFailure(Throwable t)250 public synchronized void onFailure(Throwable t) { 251 assertFalse(wasCalled); 252 wasCalled = true; 253 assertEquals(failure, t); 254 } 255 } 256 } 257