use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method valueInErrorWithGroups.
/**
* Regression test -- if value top requests {depA, depB}, depC, with depA and depC there and depB
* absent, and then throws an exception, the stored deps should be depA, depC (in different
* groups), not {depA, depC} (same group).
*/
@Test
public void valueInErrorWithGroups() throws Exception {
initializeTester();
SkyKey topKey = GraphTester.toSkyKey("top");
final SkyKey groupDepA = GraphTester.toSkyKey("groupDepA");
final SkyKey groupDepB = GraphTester.toSkyKey("groupDepB");
SkyKey depC = GraphTester.toSkyKey("depC");
tester.set(groupDepA, new StringValue("depC"));
tester.set(groupDepB, new StringValue(""));
tester.getOrCreate(depC).setHasError(true);
tester.getOrCreate(topKey).setBuilder(new NoExtractorFunction() {
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException {
StringValue val = ((StringValue) env.getValues(ImmutableList.of(groupDepA, groupDepB)).get(groupDepA));
if (env.valuesMissing()) {
return null;
}
String nextDep = val.getValue();
try {
env.getValueOrThrow(GraphTester.toSkyKey(nextDep), SomeErrorException.class);
} catch (SomeErrorException e) {
throw new GenericFunctionException(e, Transience.PERSISTENT);
}
return env.valuesMissing() ? null : new StringValue("top");
}
});
EvaluationResult<StringValue> evaluationResult = tester.eval(/*keepGoing=*/
true, groupDepA, depC);
assertTrue(evaluationResult.hasError());
assertEquals("depC", evaluationResult.get(groupDepA).getValue());
assertThat(evaluationResult.getError(depC).getRootCauses()).containsExactly(depC).inOrder();
evaluationResult = tester.eval(/*keepGoing=*/
false, topKey);
assertTrue(evaluationResult.hasError());
assertThat(evaluationResult.getError(topKey).getRootCauses()).containsExactly(topKey).inOrder();
tester.set(groupDepA, new StringValue("groupDepB"));
tester.getOrCreate(depC, /*markAsModified=*/
true);
tester.invalidate();
evaluationResult = tester.eval(/*keepGoing=*/
false, topKey);
assertFalse(evaluationResult.toString(), evaluationResult.hasError());
assertEquals("top", evaluationResult.get(topKey).getValue());
}
use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method cachedChildErrorDepWithSiblingDepOnNoKeepGoingEval.
@Test
public void cachedChildErrorDepWithSiblingDepOnNoKeepGoingEval() throws Exception {
SkyKey parent1Key = GraphTester.toSkyKey("parent1");
SkyKey parent2Key = GraphTester.toSkyKey("parent2");
final SkyKey errorKey = GraphTester.toSkyKey("error");
final SkyKey otherKey = GraphTester.toSkyKey("other");
SkyFunction parentBuilder = new SkyFunction() {
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
env.getValue(errorKey);
env.getValue(otherKey);
if (env.valuesMissing()) {
return null;
}
return new StringValue("parent");
}
@Override
public String extractTag(SkyKey skyKey) {
return null;
}
};
tester.getOrCreate(parent1Key).setBuilder(parentBuilder);
tester.getOrCreate(parent2Key).setBuilder(parentBuilder);
tester.getOrCreate(errorKey).setConstantValue(new StringValue("no error yet"));
tester.getOrCreate(otherKey).setConstantValue(new StringValue("other"));
tester.eval(/*keepGoing=*/
true, parent1Key);
tester.eval(/*keepGoing=*/
false, parent2Key);
tester.getOrCreate(errorKey, /*markAsModified=*/
true).setHasError(true);
tester.invalidate();
tester.eval(/*keepGoing=*/
true, parent1Key);
tester.eval(/*keepGoing=*/
false, parent2Key);
}
use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method cycleAndErrorAndReady.
/**
* 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 {
// This value will not have finished building on the second build when the error is thrown.
final SkyKey otherTop = GraphTester.toSkyKey("otherTop");
final SkyKey errorKey = GraphTester.toSkyKey("error");
// Is the graph state all set up and ready for the error to be thrown? The three values are
// exceptionMarker, cycle2Key, and dep1 (via signaling otherTop).
final CountDownLatch valuesReady = new CountDownLatch(3);
// Is evaluation being shut down? This is counted down by the exceptionMarker's builder, after
// it has waited for the threadpool's exception latch to be released.
final CountDownLatch errorThrown = new CountDownLatch(1);
// We don't do anything on the first build.
final AtomicBoolean secondBuild = new AtomicBoolean(false);
injectGraphListenerForTesting(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) {
// otherTop is being signaled that dep1 is done. Tell the error value that it is
// ready, then wait until the error is thrown, so that otherTop's builder is not
// re-entered.
valuesReady.countDown();
TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(errorThrown, "error not thrown");
return;
}
}
}, /*deterministic=*/
true);
final SkyKey dep1 = GraphTester.toSkyKey("dep1");
tester.set(dep1, new StringValue("dep1"));
final SkyKey dep2 = GraphTester.toSkyKey("dep2");
tester.set(dep2, new StringValue("dep2"));
// otherTop should request the deps one at a time, so that it can be in the CHECK_DEPENDENCIES
// state even after one dep is re-evaluated.
tester.getOrCreate(otherTop).setBuilder(new NoExtractorFunction() {
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
env.getValue(dep1);
if (env.valuesMissing()) {
return null;
}
env.getValue(dep2);
return env.valuesMissing() ? null : new StringValue("otherTop");
}
});
// Prime the graph with otherTop, so we can dirty it next build.
assertEquals(new StringValue("otherTop"), tester.evalAndGet(/*keepGoing=*/
false, otherTop));
// Mark dep1 changed, so otherTop will be dirty and request re-evaluation of dep1.
tester.getOrCreate(dep1, /*markAsModified=*/
true);
SkyKey topKey = GraphTester.toSkyKey("top");
// Note that since DeterministicHelper alphabetizes reverse deps, it is important that
// "cycle2" comes before "top".
final SkyKey cycle1Key = GraphTester.toSkyKey("cycle1");
final SkyKey cycle2Key = GraphTester.toSkyKey("cycle2");
tester.getOrCreate(topKey).addDependency(cycle1Key).setComputedValue(CONCATENATE);
tester.getOrCreate(cycle1Key).addDependency(errorKey).addDependency(cycle2Key).setComputedValue(CONCATENATE);
tester.getOrCreate(errorKey).setBuilder(new ChainedFunction(/*notifyStart=*/
null, /*waitToFinish=*/
valuesReady, /*notifyFinish=*/
null, /*waitForException=*/
false, /*value=*/
null, ImmutableList.<SkyKey>of()));
// Make sure cycle2Key has declared its dependence on cycle1Key before error throws.
tester.getOrCreate(cycle2Key).setBuilder(new ChainedFunction(/*notifyStart=*/
valuesReady, null, null, false, new StringValue("never returned"), ImmutableList.<SkyKey>of(cycle1Key)));
// Value that waits until an exception is thrown to finish building. We use it just to be
// informed when the threadpool is shutting down.
final SkyKey exceptionMarker = GraphTester.toSkyKey("exceptionMarker");
tester.getOrCreate(exceptionMarker).setBuilder(new ChainedFunction(/*notifyStart=*/
valuesReady, /*waitToFinish=*/
new CountDownLatch(0), /*notifyFinish=*/
errorThrown, /*waitForException=*/
true, new StringValue("exception marker"), ImmutableList.<SkyKey>of()));
tester.invalidate();
secondBuild.set(true);
// otherTop must be first, since we check top-level values for cycles in the order in which
// they appear here.
EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
false, otherTop, topKey, exceptionMarker);
Iterable<CycleInfo> cycleInfos = result.getError(topKey).getCycleInfo();
assertWithMessage(result.toString()).that(cycleInfos).isNotEmpty();
CycleInfo cycleInfo = Iterables.getOnlyElement(cycleInfos);
if (cyclesDetected()) {
assertThat(result.errorMap().keySet()).containsExactly(topKey);
assertThat(cycleInfo.getPathToCycle()).containsExactly(topKey);
assertThat(cycleInfo.getCycle()).containsExactly(cycle1Key, cycle2Key);
}
}
use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method changePruningFromOtherNodeAfterParentPrunes.
@Test
public void changePruningFromOtherNodeAfterParentPrunes() throws Exception {
initializeTester();
final SkyKey leaf = GraphTester.toSkyKey("leaf");
final SkyKey other = GraphTester.toSkyKey("other");
SkyKey top = GraphTester.toSkyKey("top");
tester.set(leaf, new StringValue("leafy"));
tester.set(other, new StringValue("other"));
// When top depends on leaf and other, but always returns the same value,
final StringValue fixedTopValue = new StringValue("top");
final AtomicBoolean topEvaluated = new AtomicBoolean(false);
tester.getOrCreate(top).setBuilder(new SkyFunction() {
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
topEvaluated.set(true);
return env.getValue(other) == null || env.getValue(leaf) == null ? null : fixedTopValue;
}
@Nullable
@Override
public String extractTag(SkyKey skyKey) {
return null;
}
});
// And top is evaluated,
StringValue topValue = (StringValue) tester.evalAndGet("top");
// Then top's value is as expected,
assertEquals(fixedTopValue, topValue);
// And top was actually evaluated.
assertThat(topEvaluated.get()).isTrue();
// When leaf is changed,
tester.set(leaf, new StringValue("crunchy"));
tester.invalidate();
topEvaluated.set(false);
// And top is evaluated,
StringValue topValue2 = (StringValue) tester.evalAndGet("top");
// Then top's value is as expected,
assertEquals(fixedTopValue, topValue2);
// And top was actually evaluated.
assertThat(topEvaluated.get()).isTrue();
// When other is invalidated but not actually changed,
tester.getOrCreate(other, /*markAsModified=*/
true);
tester.invalidate();
topEvaluated.set(false);
// And top is evaluated,
StringValue topValue3 = (StringValue) tester.evalAndGet("top");
// Then top's value is as expected,
assertEquals(fixedTopValue, topValue3);
// And top was *not* actually evaluated, because change pruning cut off evaluation.
assertThat(topEvaluated.get()).isFalse();
}
use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method dirtyWithRecoveryErrorDependsOnErrorTurningGood.
@Test
public void dirtyWithRecoveryErrorDependsOnErrorTurningGood() throws Exception {
initializeTester();
final SkyKey error = GraphTester.toSkyKey("error");
tester.getOrCreate(error).setHasError(true);
SkyKey topKey = GraphTester.toSkyKey("top");
SkyFunction recoveryErrorFunction = new SkyFunction() {
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException {
try {
env.getValueOrThrow(error, SomeErrorException.class);
} catch (SomeErrorException e) {
throw new GenericFunctionException(e, Transience.PERSISTENT);
}
return null;
}
@Override
public String extractTag(SkyKey skyKey) {
throw new UnsupportedOperationException();
}
};
tester.getOrCreate(topKey).setBuilder(recoveryErrorFunction);
EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
false, topKey);
assertThat(result.getError(topKey).getRootCauses()).containsExactly(topKey);
tester.getOrCreate(error).setHasError(false);
StringValue reformed = new StringValue("reformed");
tester.set(error, reformed);
tester.getOrCreate(topKey).setBuilder(null).addDependency(error).setComputedValue(COPY);
tester.invalidate();
result = tester.eval(/*keepGoing=*/
false, topKey);
assertEquals(reformed, result.get(topKey));
assertFalse(result.hasError());
}
Aggregations