// Path: src/main/java/com/spotify/trickle/Fallbacks.java
// public static <T> AsyncFunction<Throwable, T> always(@Nullable final T value) {
//   return new AsyncFunction<Throwable, T>() {
//     @Nullable
//     @Override
//     public ListenableFuture<T> apply(@Nullable Throwable input) {
//       return immediateFuture(value);
//     }
//   };
// }
// 
// Path: src/main/java/com/spotify/trickle/Trickle.java
// public static <R> ConfigurableGraph<R> call(Func0<R> func) {
//   return new GraphBuilder<R>(func);
// }
// 
// 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.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
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.Fallbacks.always;
import static com.spotify.trickle.Trickle.call;
import static com.spotify.trickle.Util.hasAncestor;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
        .bind(input2, "hum")
        .bind(input3, "häpp")
        .bind(input4, "Luis")
        .run().get();

    assertThat(result, equalTo("hey, 1, hey, ho, hum, häpp, Luis, 2"));
  }

  @Test
  public void shouldPropagateExceptionsToResultFuture() throws Exception {
    final RuntimeException expected = new RuntimeException("expected");
    Func1<String, String> node1 = new Func1<String, String>() {
      @Override
      public ListenableFuture<String> run(String arg) {
        return immediateFailedFuture(expected);
      }
    };
    Func2<String, String, String> node2 = new Func2<String, String, String>() {
      @Override
      public ListenableFuture<String> run(String arg1, String arg2) {
        return immediateFuture(arg1 + ", " + arg2 + ", 2");
      }
    };

    Input<String> input = Input.named("in");

    Graph<String> g1 = call(node1).with(input);
    Graph<String> g = call(node2).with(g1, input);

    thrown.expect(Exception.class);
// Path: src/main/java/com/spotify/trickle/Fallbacks.java
// public static <T> AsyncFunction<Throwable, T> always(@Nullable final T value) {
//   return new AsyncFunction<Throwable, T>() {
//     @Nullable
//     @Override
//     public ListenableFuture<T> apply(@Nullable Throwable input) {
//       return immediateFuture(value);
//     }
//   };
// }
// 
// Path: src/main/java/com/spotify/trickle/Trickle.java
// public static <R> ConfigurableGraph<R> call(Func0<R> func) {
//   return new GraphBuilder<R>(func);
// }
// 
// 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/TrickleTest.java
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
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.Fallbacks.always;
import static com.spotify.trickle.Trickle.call;
import static com.spotify.trickle.Util.hasAncestor;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

        .bind(input2, "hum")
        .bind(input3, "häpp")
        .bind(input4, "Luis")
        .run().get();

    assertThat(result, equalTo("hey, 1, hey, ho, hum, häpp, Luis, 2"));
  }

  @Test
  public void shouldPropagateExceptionsToResultFuture() throws Exception {
    final RuntimeException expected = new RuntimeException("expected");
    Func1<String, String> node1 = new Func1<String, String>() {
      @Override
      public ListenableFuture<String> run(String arg) {
        return immediateFailedFuture(expected);
      }
    };
    Func2<String, String, String> node2 = new Func2<String, String, String>() {
      @Override
      public ListenableFuture<String> run(String arg1, String arg2) {
        return immediateFuture(arg1 + ", " + arg2 + ", 2");
      }
    };

    Input<String> input = Input.named("in");

    Graph<String> g1 = call(node1).with(input);
    Graph<String> g = call(node2).with(g1, input);

    thrown.expect(Exception.class);
    thrown.expectCause(hasAncestor(expected));