// Path: java/gulava/testing/AssertingWriter.java
// public final class AssertingWriter extends FilterWriter {
//   int flushes;
// 
//   public AssertingWriter() {
//     super(new StringWriter());
//   }
// 
//   @Override
//   public void flush() throws IOException {
//     out.flush();
//     flushes++;
//   }
// 
//   public void assertLines(String... expectedLines) {
//     StringBuilder expected = new StringBuilder();
//     for (String line : expectedLines) {
//       expected.append(line);
//       expected.append('\n');
//     }
//     Assert.assertEquals(expected.toString(), out.toString());
//   }
// 
//   public void assertFlushes(int expected) {
//     Assert.assertEquals(expected, flushes);
//   }
// }
// 
// Path: java/gulava/testing/LogicAsserter.java
// public final class LogicAsserter {
//   private Stream stream;
//   private int expectedWorkUnits = -1;
//   private List<Map<Var, Object>> expectedSubsts = new ArrayList<>();
//   private List<Var> requestedVars = new ArrayList<>();
//   private boolean expectedFinishes = true;
// 
//   /**
//    * Set the stream whose results to assert on.
//    */
//   public LogicAsserter stream(Stream stream) {
//     this.stream = stream;
//     return this;
//   }
// 
//   /**
//    * Set the stream whose results to assert on. The stream is the result of running the goal on the
//    * empty substitution.
//    */
//   public LogicAsserter stream(Goal goal) {
//     this.stream = goal.run(Subst.EMPTY);
//     return this;
//   }
// 
//   /**
//    * Set the number of work units, which roughly corresponds to how many times we realize or obtain
//    * a sub-{@link Stream}. If we expect to finish, this should be set to the exact number of work
//    * units before we realize the stream. Otherwise, this is the number of work units before we stop
//    * obtaining results for the test.
//    */
//   public LogicAsserter workUnits(int expectedWorkUnits) {
//     this.expectedWorkUnits = expectedWorkUnits;
//     return this;
//   }
// 
//   /**
//    * Begins a new substitution map.
//    */
//   public LogicAsserter startSubst() {
//     expectedSubsts.add(new HashMap<>());
//     return this;
//   }
// 
//   /**
//    * Adds an entry to the current substitution map.
//    */
//   public LogicAsserter put(Var var, Object value) {
//     expectedSubsts.get(expectedSubsts.size() - 1).put(var, value);
//     return this;
//   }
// 
//   /**
//    * Indicates whether the stream is expected to finish. This is true by default.
//    */
//   public LogicAsserter finishes(boolean expectedFinishes) {
//     this.expectedFinishes = expectedFinishes;
//     return this;
//   }
// 
//   public LogicAsserter addRequestedVar(Var... vars) {
//     this.requestedVars.addAll(Arrays.asList(vars));
//     return this;
//   }
// 
//   /**
//    * Performs the test by verifying whether the expected values set with the other methods match the
//    * actual results obtained from the stream.
//    */
//   public void test() {
//     Assert.assertEquals(expectedSubsts, execute());
//   }
// 
//   private List<Map<Object, Object>> execute() {
//     List<Map<Object, Object>> actualSubsts = new ArrayList<>();
//     boolean actualFinishes = false;
//     int actualWorkUnits = 0;
// 
//     while (expectedFinishes || (actualWorkUnits < expectedWorkUnits)) {
//       if (stream == Streams.EMPTY) {
//         actualFinishes = true;
//         break;
//       }
//       actualWorkUnits += 1;
//       Subst subst = stream.subst();
//       if (subst != null) {
//         actualSubsts.add(new View.Builder()
//             .setSubst(subst)
//             .addAllRequestedVars(requestedVars)
//             .build()
//             .map());
//       }
//       stream = stream.rest();
//     }
// 
//     if (expectedWorkUnits != -1) {
//       Assert.assertEquals(expectedWorkUnits, actualWorkUnits);
//     }
//     Assert.assertEquals(expectedFinishes, actualFinishes);
//     return actualSubsts;
//   }
// 
//   /**
//    * Rather than compare expected substitutions to actual ones (like {@link #test()} does), just
//    * returns the substitutions and allows the caller to verify them. This still asserts that the
//    * correct number of work units were spent and and that the stream did or did not finish according
//    * to expectations.
//    */
//   public List<Map<Object, Object>> actualSubsts() {
//     Assert.assertEquals(Collections.emptyList(), expectedSubsts);
//     return execute();
//   }
// }
// 
// Path: java/gulava/testing/RecordsCallGoal.java
// public final class RecordsCallGoal implements Goal {
//   public final StringBuilder reportTo;
//   public final String name;
// 
//   public RecordsCallGoal(StringBuilder reportTo, String name) {
//     this.reportTo = reportTo;
//     this.name = name;
//   }
// 
//   @Override
//   public Stream run(Subst s) {
//     reportTo.append(name);
//     return s;
//   }
// }


import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static gulava.Goals.conj;
import static gulava.Goals.disj;
import static gulava.Goals.same;
import gulava.testing.AssertingWriter;
import gulava.testing.LogicAsserter;
import gulava.testing.RecordsCallGoal;
import org.junit.Assert;
        .addRequestedVar(X, Y)
        .startSubst()
        .put(X, 42)
        .startSubst()
        .put(Y, 24)
        .test();
  }

  @Test
  public void multipleCachedGoals() {
    Goal g1 = new CachedGoal(same(X, 44));
    Goal g2 = new CachedGoal(same(Y, 99));

    new LogicAsserter()
        .stream(conj(g1, g2))
        .addRequestedVar(X, Y)
        .startSubst()
        .put(X, 44)
        .put(Y, 99)
        .test();
  }

  @Test
  public void runNotInvokedAgain() {
    StringBuilder callReport = new StringBuilder();
    RecordsCallGoal callOnce = new RecordsCallGoal(callReport, "a");
    CachedGoal goal = new CachedGoal(callOnce);

    new LogicAsserter()
        .stream(conj(goal, goal))
// Path: java/gulava/testing/AssertingWriter.java
// public final class AssertingWriter extends FilterWriter {
//   int flushes;
// 
//   public AssertingWriter() {
//     super(new StringWriter());
//   }
// 
//   @Override
//   public void flush() throws IOException {
//     out.flush();
//     flushes++;
//   }
// 
//   public void assertLines(String... expectedLines) {
//     StringBuilder expected = new StringBuilder();
//     for (String line : expectedLines) {
//       expected.append(line);
//       expected.append('\n');
//     }
//     Assert.assertEquals(expected.toString(), out.toString());
//   }
// 
//   public void assertFlushes(int expected) {
//     Assert.assertEquals(expected, flushes);
//   }
// }
// 
// Path: java/gulava/testing/LogicAsserter.java
// public final class LogicAsserter {
//   private Stream stream;
//   private int expectedWorkUnits = -1;
//   private List<Map<Var, Object>> expectedSubsts = new ArrayList<>();
//   private List<Var> requestedVars = new ArrayList<>();
//   private boolean expectedFinishes = true;
// 
//   /**
//    * Set the stream whose results to assert on.
//    */
//   public LogicAsserter stream(Stream stream) {
//     this.stream = stream;
//     return this;
//   }
// 
//   /**
//    * Set the stream whose results to assert on. The stream is the result of running the goal on the
//    * empty substitution.
//    */
//   public LogicAsserter stream(Goal goal) {
//     this.stream = goal.run(Subst.EMPTY);
//     return this;
//   }
// 
//   /**
//    * Set the number of work units, which roughly corresponds to how many times we realize or obtain
//    * a sub-{@link Stream}. If we expect to finish, this should be set to the exact number of work
//    * units before we realize the stream. Otherwise, this is the number of work units before we stop
//    * obtaining results for the test.
//    */
//   public LogicAsserter workUnits(int expectedWorkUnits) {
//     this.expectedWorkUnits = expectedWorkUnits;
//     return this;
//   }
// 
//   /**
//    * Begins a new substitution map.
//    */
//   public LogicAsserter startSubst() {
//     expectedSubsts.add(new HashMap<>());
//     return this;
//   }
// 
//   /**
//    * Adds an entry to the current substitution map.
//    */
//   public LogicAsserter put(Var var, Object value) {
//     expectedSubsts.get(expectedSubsts.size() - 1).put(var, value);
//     return this;
//   }
// 
//   /**
//    * Indicates whether the stream is expected to finish. This is true by default.
//    */
//   public LogicAsserter finishes(boolean expectedFinishes) {
//     this.expectedFinishes = expectedFinishes;
//     return this;
//   }
// 
//   public LogicAsserter addRequestedVar(Var... vars) {
//     this.requestedVars.addAll(Arrays.asList(vars));
//     return this;
//   }
// 
//   /**
//    * Performs the test by verifying whether the expected values set with the other methods match the
//    * actual results obtained from the stream.
//    */
//   public void test() {
//     Assert.assertEquals(expectedSubsts, execute());
//   }
// 
//   private List<Map<Object, Object>> execute() {
//     List<Map<Object, Object>> actualSubsts = new ArrayList<>();
//     boolean actualFinishes = false;
//     int actualWorkUnits = 0;
// 
//     while (expectedFinishes || (actualWorkUnits < expectedWorkUnits)) {
//       if (stream == Streams.EMPTY) {
//         actualFinishes = true;
//         break;
//       }
//       actualWorkUnits += 1;
//       Subst subst = stream.subst();
//       if (subst != null) {
//         actualSubsts.add(new View.Builder()
//             .setSubst(subst)
//             .addAllRequestedVars(requestedVars)
//             .build()
//             .map());
//       }
//       stream = stream.rest();
//     }
// 
//     if (expectedWorkUnits != -1) {
//       Assert.assertEquals(expectedWorkUnits, actualWorkUnits);
//     }
//     Assert.assertEquals(expectedFinishes, actualFinishes);
//     return actualSubsts;
//   }
// 
//   /**
//    * Rather than compare expected substitutions to actual ones (like {@link #test()} does), just
//    * returns the substitutions and allows the caller to verify them. This still asserts that the
//    * correct number of work units were spent and and that the stream did or did not finish according
//    * to expectations.
//    */
//   public List<Map<Object, Object>> actualSubsts() {
//     Assert.assertEquals(Collections.emptyList(), expectedSubsts);
//     return execute();
//   }
// }
// 
// Path: java/gulava/testing/RecordsCallGoal.java
// public final class RecordsCallGoal implements Goal {
//   public final StringBuilder reportTo;
//   public final String name;
// 
//   public RecordsCallGoal(StringBuilder reportTo, String name) {
//     this.reportTo = reportTo;
//     this.name = name;
//   }
// 
//   @Override
//   public Stream run(Subst s) {
//     reportTo.append(name);
//     return s;
//   }
// }


// Path: javatests/gulava/CachedGoalTest.java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static gulava.Goals.conj;
import static gulava.Goals.disj;
import static gulava.Goals.same;
import gulava.testing.AssertingWriter;
import gulava.testing.LogicAsserter;
import gulava.testing.RecordsCallGoal;
import org.junit.Assert;

        .addRequestedVar(X, Y)
        .startSubst()
        .put(X, 42)
        .startSubst()
        .put(Y, 24)
        .test();
  }

  @Test
  public void multipleCachedGoals() {
    Goal g1 = new CachedGoal(same(X, 44));
    Goal g2 = new CachedGoal(same(Y, 99));

    new LogicAsserter()
        .stream(conj(g1, g2))
        .addRequestedVar(X, Y)
        .startSubst()
        .put(X, 44)
        .put(Y, 99)
        .test();
  }

  @Test
  public void runNotInvokedAgain() {
    StringBuilder callReport = new StringBuilder();
    RecordsCallGoal callOnce = new RecordsCallGoal(callReport, "a");
    CachedGoal goal = new CachedGoal(callOnce);

    new LogicAsserter()
        .stream(conj(goal, goal))
        .startSubst()