Program: 111041
Project: 111041
Buggy source:
/** 
 * Regression test: IllegalStateException in BuildingState.isReady(). The ParallelEvaluator used to assume during cycle-checking that all values had been built as fully as possible -- that evaluation had not been interrupted. However, we also do cycle-checking in nokeep-going mode when a value throws an error (possibly prematurely shutting down evaluation) but that error then bubbles up into a cycle. <p>We want to achieve the following state: we are checking for a cycle; the value we examine has not yet finished checking its children to see if they are dirty; but all children checked so far have been unchanged. This value is "otherTop". We first build otherTop, then mark its first child changed (without actually changing it), and then do a second build. On the second build, we also build "top", which requests a cycle that depends on an error. We wait to signal otherTop that its first child is done until the error throws and shuts down evaluation. The error then bubbles up to the cycle, and so the bubbling is aborted. Finally, cycle checking happens, and otherTop is examined, as desired.
 */
@Test public void cycleAndErrorAndReady() throws Exception {
  final SkyKey otherTop=GraphTester.toSkyKey("String_Node_Str");
  final SkyKey errorKey=GraphTester.toSkyKey("String_Node_Str");
  final CountDownLatch valuesReady=new CountDownLatch(3);
  final CountDownLatch errorThrown=new CountDownLatch(1);
  final AtomicBoolean secondBuild=new AtomicBoolean(false);
  final TrackingAwaiter trackingAwaiter=new TrackingAwaiter();
  setGraphForTesting(new DeterministicInMemoryGraph(new Listener(){
    @Override public void accept(    SkyKey key,    EventType type,    Order order,    Object context){
      if (!secondBuild.get()) {
        return;
      }
      if (key.equals(errorKey) && type == EventType.SET_VALUE) {
        trackingAwaiter.awaitLatchAndTrackExceptions(valuesReady,"String_Node_Str");
        return;
      }
      if (key.equals(otherTop) && type == EventType.SIGNAL) {
        valuesReady.countDown();
        trackingAwaiter.awaitLatchAndTrackExceptions(errorThrown,"String_Node_Str");
        return;
      }
    }
  }
));
  final SkyKey dep1=GraphTester.toSkyKey("String_Node_Str");
  tester.set(dep1,new StringValue("String_Node_Str"));
  final SkyKey dep2=GraphTester.toSkyKey("String_Node_Str");
  tester.set(dep2,new StringValue("String_Node_Str"));
  tester.getOrCreate(otherTop).setBuilder(new NoExtractorFunction(){
    @Override public SkyValue compute(    SkyKey skyKey,    Environment env){
      env.getValue(dep1);
      if (env.valuesMissing()) {
        return null;
      }
      env.getValue(dep2);
      return env.valuesMissing() ? null : new StringValue("String_Node_Str");
    }
  }
);
  assertEquals(new StringValue("String_Node_Str"),tester.evalAndGet(false,otherTop));
  tester.getOrCreate(dep1,true);
  SkyKey topKey=GraphTester.toSkyKey("String_Node_Str");
  final SkyKey cycle1Key=GraphTester.toSkyKey("String_Node_Str");
  final SkyKey cycle2Key=GraphTester.toSkyKey("String_Node_Str");
  tester.getOrCreate(topKey).addDependency(cycle1Key).setComputedValue(CONCATENATE);
  tester.getOrCreate(cycle1Key).addDependency(errorKey).addDependency(cycle2Key).setComputedValue(CONCATENATE);
  tester.getOrCreate(errorKey).setHasError(true);
  tester.getOrCreate(cycle2Key).setBuilder(new ChainedFunction(valuesReady,null,null,false,new StringValue("String_Node_Str"),ImmutableList.<SkyKey>of(cycle1Key)));
  final SkyKey exceptionMarker=GraphTester.toSkyKey("String_Node_Str");
  tester.getOrCreate(exceptionMarker).setBuilder(new ChainedFunction(valuesReady,new CountDownLatch(0),errorThrown,true,new StringValue("String_Node_Str"),ImmutableList.<SkyKey>of()));
  tester.invalidate();
  secondBuild.set(true);
  EvaluationResult<StringValue> result=tester.eval(false,otherTop,topKey,exceptionMarker);
  trackingAwaiter.assertNoErrors();
  assertThat(result.errorMap().keySet()).containsExactly(topKey);
  Iterable<CycleInfo> cycleInfos=result.getError(topKey).getCycleInfo();
  assertWithMessage(result.toString()).that(cycleInfos).isNotEmpty();
  CycleInfo cycleInfo=Iterables.getOnlyElement(cycleInfos);
  assertThat(cycleInfo.getPathToCycle()).containsExactly(topKey);
  assertThat(cycleInfo.getCycle()).containsExactly(cycle1Key,cycle2Key);
}

Fixed version (ground truth):
/** 
 * Regression test: IllegalStateException in BuildingState.isReady(). The ParallelEvaluator used to assume during cycle-checking that all values had been built as fully as possible -- that evaluation had not been interrupted. However, we also do cycle-checking in nokeep-going mode when a value throws an error (possibly prematurely shutting down evaluation) but that error then bubbles up into a cycle. <p>We want to achieve the following state: we are checking for a cycle; the value we examine has not yet finished checking its children to see if they are dirty; but all children checked so far have been unchanged. This value is "otherTop". We first build otherTop, then mark its first child changed (without actually changing it), and then do a second build. On the second build, we also build "top", which requests a cycle that depends on an error. We wait to signal otherTop that its first child is done until the error throws and shuts down evaluation. The error then bubbles up to the cycle, and so the bubbling is aborted. Finally, cycle checking happens, and otherTop is examined, as desired.
 */
@Test public void cycleAndErrorAndReady() throws Exception {
  final SkyKey otherTop=GraphTester.toSkyKey("String_Node_Str");
  final SkyKey errorKey=GraphTester.toSkyKey("String_Node_Str");
  final CountDownLatch valuesReady=new CountDownLatch(3);
  final CountDownLatch errorThrown=new CountDownLatch(1);
  final AtomicBoolean secondBuild=new AtomicBoolean(false);
  final TrackingAwaiter trackingAwaiter=new TrackingAwaiter();
  setGraphForTesting(new DeterministicInMemoryGraph(new Listener(){
    @Override public void accept(    SkyKey key,    EventType type,    Order order,    Object context){
      if (!secondBuild.get()) {
        return;
      }
      if (key.equals(otherTop) && type == EventType.SIGNAL) {
        valuesReady.countDown();
        trackingAwaiter.awaitLatchAndTrackExceptions(errorThrown,"String_Node_Str");
        return;
      }
    }
  }
));
  final SkyKey dep1=GraphTester.toSkyKey("String_Node_Str");
  tester.set(dep1,new StringValue("String_Node_Str"));
  final SkyKey dep2=GraphTester.toSkyKey("String_Node_Str");
  tester.set(dep2,new StringValue("String_Node_Str"));
  tester.getOrCreate(otherTop).setBuilder(new NoExtractorFunction(){
    @Override public SkyValue compute(    SkyKey skyKey,    Environment env){
      env.getValue(dep1);
      if (env.valuesMissing()) {
        return null;
      }
      env.getValue(dep2);
      return env.valuesMissing() ? null : new StringValue("String_Node_Str");
    }
  }
);
  assertEquals(new StringValue("String_Node_Str"),tester.evalAndGet(false,otherTop));
  tester.getOrCreate(dep1,true);
  SkyKey topKey=GraphTester.toSkyKey("String_Node_Str");
  final SkyKey cycle1Key=GraphTester.toSkyKey("String_Node_Str");
  final SkyKey cycle2Key=GraphTester.toSkyKey("String_Node_Str");
  tester.getOrCreate(topKey).addDependency(cycle1Key).setComputedValue(CONCATENATE);
  tester.getOrCreate(cycle1Key).addDependency(errorKey).addDependency(cycle2Key).setComputedValue(CONCATENATE);
  tester.getOrCreate(errorKey).setBuilder(new ChainedFunction(null,valuesReady,null,false,null,ImmutableList.<SkyKey>of()));
  tester.getOrCreate(cycle2Key).setBuilder(new ChainedFunction(valuesReady,null,null,false,new StringValue("String_Node_Str"),ImmutableList.<SkyKey>of(cycle1Key)));
  final SkyKey exceptionMarker=GraphTester.toSkyKey("String_Node_Str");
  tester.getOrCreate(exceptionMarker).setBuilder(new ChainedFunction(valuesReady,new CountDownLatch(0),errorThrown,true,new StringValue("String_Node_Str"),ImmutableList.<SkyKey>of()));
  tester.invalidate();
  secondBuild.set(true);
  EvaluationResult<StringValue> result=tester.eval(false,otherTop,topKey,exceptionMarker);
  trackingAwaiter.assertNoErrors();
  assertThat(result.errorMap().keySet()).containsExactly(topKey);
  Iterable<CycleInfo> cycleInfos=result.getError(topKey).getCycleInfo();
  assertWithMessage(result.toString()).that(cycleInfos).isNotEmpty();
  CycleInfo cycleInfo=Iterables.getOnlyElement(cycleInfos);
  assertThat(cycleInfo.getPathToCycle()).containsExactly(topKey);
  assertThat(cycleInfo.getCycle()).containsExactly(cycle1Key,cycle2Key);
}