use of com.google.devtools.build.skyframe.GraphTester.StringValue in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method warningAndErrorOnFailFastBuild.
@Test
public void warningAndErrorOnFailFastBuild() throws Exception {
SkyKey topKey = GraphTester.toSkyKey("top");
tester.set(topKey, new StringValue("top")).setWarning("warning msg").setHasError(true);
for (int i = 0; i < 2; i++) {
initializeReporter();
EvaluationResult<StringValue> result = tester.eval(false, "top");
assertTrue(result.hasError());
if (rootCausesStored()) {
assertThat(result.getError(topKey).getRootCauses()).containsExactly(topKey);
}
assertEquals(topKey.toString(), result.getError(topKey).getException().getMessage());
assertTrue(result.getError(topKey).getException() instanceof SomeErrorException);
if (i == 0 || eventsStored()) {
assertContainsEvent(eventCollector, "warning msg");
assertEventCount(1, eventCollector);
}
}
}
use of com.google.devtools.build.skyframe.GraphTester.StringValue in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method dirtyAndChanged.
@Test
public void dirtyAndChanged() throws Exception {
initializeTester();
SkyKey leaf = GraphTester.toSkyKey("leaf");
SkyKey mid = GraphTester.toSkyKey("mid");
SkyKey top = GraphTester.toSkyKey("top");
tester.getOrCreate(top).addDependency(mid).setComputedValue(COPY);
tester.getOrCreate(mid).addDependency(leaf).setComputedValue(COPY);
tester.set(leaf, new StringValue("leafy"));
// For invalidation.
tester.set("dummy", new StringValue("dummy"));
StringValue topValue = (StringValue) tester.evalAndGet("top");
assertEquals("leafy", topValue.getValue());
tester.set(leaf, new StringValue("crunchy"));
tester.invalidate();
// For invalidation.
tester.evalAndGet("dummy");
tester.getOrCreate(mid, /*markAsModified=*/
true);
tester.invalidate();
topValue = (StringValue) tester.evalAndGet("top");
assertEquals("crunchy", topValue.getValue());
}
use of com.google.devtools.build.skyframe.GraphTester.StringValue in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method incompleteDirectDepsAreClearedBeforeKeepGoing.
/**
* Regression test: error on clearMaybeDirtyValue. Same as the previous test, but the second
* evaluation is keepGoing, which should cause an access of the children of topKey.
*/
@Test
public void incompleteDirectDepsAreClearedBeforeKeepGoing() throws Exception {
initializeTester();
CountDownLatch slowStart = new CountDownLatch(1);
CountDownLatch errorFinish = new CountDownLatch(1);
SkyKey errorKey = GraphTester.toSkyKey("error");
tester.getOrCreate(errorKey).setBuilder(new ChainedFunction(/*notifyStart=*/
null, /*waitToFinish=*/
slowStart, /*notifyFinish=*/
errorFinish, /*waitForException=*/
false, /*value=*/
null, /*deps=*/
ImmutableList.<SkyKey>of()));
SkyKey slowKey = GraphTester.toSkyKey("slow");
tester.getOrCreate(slowKey).setBuilder(new ChainedFunction(/*notifyStart=*/
slowStart, /*waitToFinish=*/
errorFinish, /*notifyFinish=*/
null, /*waitForException=*/
true, new StringValue("slow"), /*deps=*/
ImmutableList.<SkyKey>of()));
SkyKey midKey = GraphTester.toSkyKey("mid");
tester.getOrCreate(midKey).addDependency(slowKey).setComputedValue(COPY);
SkyKey topKey = GraphTester.toSkyKey("top");
tester.getOrCreate(topKey).addDependency(midKey).addDependency(errorKey).setComputedValue(CONCATENATE);
// slowKey starts -> errorKey finishes, written to graph -> slowKey finishes & (Visitor aborts)
// -> topKey builds.
EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
false, topKey);
assertThat(result.getError().getRootCauses()).containsExactly(errorKey);
// Make sure midKey didn't finish building.
assertEquals(null, tester.getExistingValue(midKey));
// Give slowKey a nice ordinary builder.
tester.getOrCreate(slowKey, /*markAsModified=*/
false).setBuilder(null).setConstantValue(new StringValue("slow"));
// Put midKey into the graph. It won't have a reverse dependence on topKey.
tester.evalAndGet(/*keepGoing=*/
false, midKey);
// topKey should not access midKey as if it were already registered as a dependency.
// We don't invalidate errors, but because topKey wasn't actually written to the graph last
// build, it should be rebuilt here.
tester.eval(/*keepGoing=*/
true, topKey);
}
use of com.google.devtools.build.skyframe.GraphTester.StringValue in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method dirtyChildEnqueuesParentDuringCheckDependencies.
/**
* We are checking here that we are resilient to a race condition in which a value that is
* checking its children for dirtiness is signaled by all of its children, putting it in a ready
* state, before the thread has terminated. Optionally, one of its children may throw an error,
* shutting down the threadpool. The essential race is that a child about to throw signals its
* parent and the parent's builder restarts itself before the exception is thrown. Here, the
* signaling happens while dirty dependencies are being checked. We control the timing by blocking
* "top"'s registering itself on its deps.
*/
private void dirtyChildEnqueuesParentDuringCheckDependencies(final boolean throwError) throws Exception {
// Value to be built. It will be signaled to rebuild before it has finished checking its deps.
final SkyKey top = GraphTester.toSkyKey("top");
// Dep that blocks before it acknowledges being added as a dep by top, so the firstKey value has
// time to signal top. (Importantly its key is alphabetically after 'firstKey').
final SkyKey slowAddingDep = GraphTester.toSkyKey("slowDep");
// Don't perform any blocking on the first build.
final AtomicBoolean delayTopSignaling = new AtomicBoolean(false);
final CountDownLatch topSignaled = new CountDownLatch(1);
final CountDownLatch topRestartedBuild = new CountDownLatch(1);
injectGraphListenerForTesting(new Listener() {
@Override
public void accept(SkyKey key, EventType type, Order order, @Nullable Object context) {
if (!delayTopSignaling.get()) {
return;
}
if (key.equals(top) && type == EventType.SIGNAL && order == Order.AFTER) {
// top is signaled by firstKey (since slowAddingDep is blocking), so slowAddingDep
// is now free to acknowledge top as a parent.
topSignaled.countDown();
return;
}
if (key.equals(slowAddingDep) && type == EventType.ADD_REVERSE_DEP && top.equals(context) && order == Order.BEFORE) {
// If top is trying to declare a dep on slowAddingDep, wait until firstKey has
// signaled top. Then this add dep will return DONE and top will be signaled,
// making it ready, so it will be enqueued.
TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(topSignaled, "first key didn't signal top in time");
}
}
}, /*deterministic=*/
true);
// Value that is modified on the second build. Its thread won't finish until it signals top,
// which will wait for the signal before it enqueues its next dep. We prevent the thread from
// finishing by having the listener to which it reports its warning block until top's builder
// starts.
final SkyKey firstKey = GraphTester.skyKey("first");
tester.set(firstKey, new StringValue("biding"));
tester.set(slowAddingDep, new StringValue("dep"));
final AtomicInteger numTopInvocations = new AtomicInteger(0);
tester.getOrCreate(top).setBuilder(new NoExtractorFunction() {
@Override
public SkyValue compute(SkyKey key, SkyFunction.Environment env) throws InterruptedException {
numTopInvocations.incrementAndGet();
if (delayTopSignaling.get()) {
// The reporter will be given firstKey's warning to emit when it is requested as a dep
// below, if firstKey is already built, so we release the reporter's latch beforehand.
topRestartedBuild.countDown();
}
// top's builder just requests both deps in a group.
env.getValuesOrThrow(ImmutableList.of(firstKey, slowAddingDep), SomeErrorException.class);
return env.valuesMissing() ? null : new StringValue("top");
}
});
reporter = new DelegatingEventHandler(reporter) {
@Override
public void handle(Event e) {
super.handle(e);
if (e.getKind() == EventKind.WARNING) {
if (!throwError) {
TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(topRestartedBuild, "top's builder did not start in time");
}
}
}
};
// First build : just prime the graph.
EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
false, top);
assertFalse(result.hasError());
assertEquals(new StringValue("top"), result.get(top));
assertEquals(2, numTopInvocations.get());
// Now dirty the graph, and maybe have firstKey throw an error.
String warningText = "warning text";
tester.getOrCreate(firstKey, /*markAsModified=*/
true).setHasError(throwError).setWarning(warningText);
tester.invalidate();
delayTopSignaling.set(true);
result = tester.eval(/*keepGoing=*/
false, top);
if (throwError) {
assertTrue(result.hasError());
// No successfully evaluated values.
assertThat(result.keyNames()).isEmpty();
ErrorInfo errorInfo = result.getError(top);
assertThat(errorInfo.getRootCauses()).containsExactly(firstKey);
assertEquals("on the incremental build, top's builder should have only been used in error " + "bubbling", 3, numTopInvocations.get());
} else {
assertEquals(new StringValue("top"), result.get(top));
assertFalse(result.hasError());
assertEquals("on the incremental build, top's builder should have only been executed once in " + "normal evaluation", 3, numTopInvocations.get());
}
assertContainsEvent(eventCollector, warningText);
assertEquals(0, topSignaled.getCount());
assertEquals(0, topRestartedBuild.getCount());
}
use of com.google.devtools.build.skyframe.GraphTester.StringValue in project bazel by bazelbuild.
the class MemoizingEvaluatorTest method interruptAfterFailFails.
@Test
public void interruptAfterFailFails() throws Exception {
SkyKey failKey = GraphTester.skyKey("fail");
SkyKey interruptedKey = GraphTester.skyKey("interrupted");
// Given a SkyFunction implementation that is properly coded to as not to throw a
// runtime exception when it is interrupted,
final CountDownLatch interruptStarted = new CountDownLatch(1);
tester.getOrCreate(interruptedKey).setBuilder(new SkyFunction() {
@Nullable
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
interruptStarted.countDown();
Thread.sleep(TestUtils.WAIT_TIMEOUT_MILLISECONDS);
throw new AssertionError("Shouldn't have slept so long");
}
@Nullable
@Override
public String extractTag(SkyKey skyKey) {
return null;
}
});
// And another SkyFunction that waits for the first to start, and then throws,
tester.getOrCreate(failKey).setBuilder(new ChainedFunction(null, interruptStarted, null, /*waitForException=*/
false, null, ImmutableList.<SkyKey>of()));
// When it is interrupted during evaluation (here, caused by the failure of a sibling node
// during a no-keep-going evaluation),
EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
false, interruptedKey, failKey);
// Then the Evaluator#evaluate call returns an EvaluationResult that has no error for the
// interrupted SkyFunction.
assertWithMessage(result.toString()).that(result.hasError()).isTrue();
assertWithMessage(result.toString()).that(result.getError(failKey)).isNotNull();
assertWithMessage(result.toString()).that(result.getError(interruptedKey)).isNull();
}
Aggregations