// Path: src/main/java/com/spotify/trickle/GraphExceptionWrapper.java
// public static Throwable wrapException(Throwable t,
//                                       TraverseState.FutureCallInformation currentCall,
//                                       TraverseState traverseState) {
//   return new GraphExecutionException(t, asCallInfo(currentCall), callInfos(traverseState));
// }
// 
// Path: src/test/java/com/spotify/trickle/Util.java
// static Matcher<Throwable> hasAncestor(final Throwable expected) {
//   return new TypeSafeMatcher<Throwable>() {
//     @Override
//     protected boolean matchesSafely(Throwable item) {
//       for (Throwable cause = item ; cause != null ; cause = cause.getCause()) {
//         if (cause.equals(expected)) {
//           return true;
//         }
//       }
// 
//       return false;
//     }
// 
//     @Override
//     public void describeTo(Description description) {
//       description.appendText("with parent cause " + expected);
//     }
//   };
// }


import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static com.spotify.trickle.GraphExceptionWrapper.wrapException;
import static com.spotify.trickle.Util.hasAncestor;
import static java.util.Collections.emptyList;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

    assertThat(found1, is(true));
    assertThat(found2, is(false));
  }

  @Test
  public void shouldReportIncompleteInputs() throws Exception {
    ListenableFuture<Object> element = SettableFuture.create();
    List<ListenableFuture<?>> parameterValues = ImmutableList.of(
        immediateFuture("hi"),
        element
    );

    currentCall = new TraverseState.FutureCallInformation(currentNodeInfo, parameterValues);

    String message = wrapException(t, currentCall, traverseState).getMessage();

    assertThat(message, containsString("NOT TERMINATED FUTURE"));
  }

  @Test
  public void shouldThrowForFailedInputs() throws Exception {
    RuntimeException inputException = new RuntimeException("failing input");
    List<ListenableFuture<?>> parameterValues = ImmutableList.of(
        immediateFailedFuture(inputException),
        immediateFuture("hi")
    );

    currentCall = new TraverseState.FutureCallInformation(currentNodeInfo, parameterValues);

// Path: src/main/java/com/spotify/trickle/GraphExceptionWrapper.java
// public static Throwable wrapException(Throwable t,
//                                       TraverseState.FutureCallInformation currentCall,
//                                       TraverseState traverseState) {
//   return new GraphExecutionException(t, asCallInfo(currentCall), callInfos(traverseState));
// }
// 
// Path: src/test/java/com/spotify/trickle/Util.java
// static Matcher<Throwable> hasAncestor(final Throwable expected) {
//   return new TypeSafeMatcher<Throwable>() {
//     @Override
//     protected boolean matchesSafely(Throwable item) {
//       for (Throwable cause = item ; cause != null ; cause = cause.getCause()) {
//         if (cause.equals(expected)) {
//           return true;
//         }
//       }
// 
//       return false;
//     }
// 
//     @Override
//     public void describeTo(Description description) {
//       description.appendText("with parent cause " + expected);
//     }
//   };
// }


// Path: src/test/java/com/spotify/trickle/GraphExceptionWrapperTest.java
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static com.spotify.trickle.GraphExceptionWrapper.wrapException;
import static com.spotify.trickle.Util.hasAncestor;
import static java.util.Collections.emptyList;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;


    assertThat(found1, is(true));
    assertThat(found2, is(false));
  }

  @Test
  public void shouldReportIncompleteInputs() throws Exception {
    ListenableFuture<Object> element = SettableFuture.create();
    List<ListenableFuture<?>> parameterValues = ImmutableList.of(
        immediateFuture("hi"),
        element
    );

    currentCall = new TraverseState.FutureCallInformation(currentNodeInfo, parameterValues);

    String message = wrapException(t, currentCall, traverseState).getMessage();

    assertThat(message, containsString("NOT TERMINATED FUTURE"));
  }

  @Test
  public void shouldThrowForFailedInputs() throws Exception {
    RuntimeException inputException = new RuntimeException("failing input");
    List<ListenableFuture<?>> parameterValues = ImmutableList.of(
        immediateFailedFuture(inputException),
        immediateFuture("hi")
    );

    currentCall = new TraverseState.FutureCallInformation(currentNodeInfo, parameterValues);

    thrown.expect(hasAncestor(inputException));