Home | History | Annotate | Download | only in concurrent
      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 {
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    197     @Override protected void interruptTask() {
    198       assertFalse(interruptTaskWasCalled);
    199       interruptTaskWasCalled = true;
    200     }
    201   }
    202 }
    203