aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test
diff options
context:
space:
mode:
authorGravatar janakr <janakr@google.com>2018-08-10 23:36:52 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-08-10 23:38:44 -0700
commit468f177334d4375f762e1dcdb13c51feeed11dfb (patch)
tree7737d2af4390f758c710b9ad35e9bdf5fac1d134 /src/test
parent50af3b73ad40f37ee9f6be989d630d7ae32b4767 (diff)
Refactoring of SkyFunctionEnvironment to iterate over events/postables only when they're actually being put into a committed value. The previous behavior submitted deps' events twice, when the dep was added and when the node finished building.
The intention is to build on this refactoring to cut off events/postables across the analysis-execution boundary, so that actions are not carrying around nested sets of warnings coming from their configured targets. This will be safe because to execute an action, we must already have analyzed its configured target, so the warning would have been emitted there. As can be seen from the changed test, this is not a pure behavior no-op. We will now emit cached events slightly later, on value committal, rather than on first dep declaration. This should not be an issue: since the events are cached, the user must have already seen them on a prior build, so the delay should not be important. Inversely, we now report events slightly more quickly during bubbling up, since we report them at each stage, as opposed to just at ParallelEvaluator evaluation completion. PiperOrigin-RevId: 208316502
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/com/google/devtools/build/skyframe/DeterministicHelper.java7
-rw-r--r--src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java182
-rw-r--r--src/test/java/com/google/devtools/build/skyframe/NotifyingHelper.java4
3 files changed, 140 insertions, 53 deletions
diff --git a/src/test/java/com/google/devtools/build/skyframe/DeterministicHelper.java b/src/test/java/com/google/devtools/build/skyframe/DeterministicHelper.java
index 524ea9b098..d9e125b54d 100644
--- a/src/test/java/com/google/devtools/build/skyframe/DeterministicHelper.java
+++ b/src/test/java/com/google/devtools/build/skyframe/DeterministicHelper.java
@@ -145,5 +145,12 @@ public class DeterministicHelper extends NotifyingHelper {
result.addAll(super.getInProgressReverseDeps());
return result;
}
+
+ @Override
+ public Set<SkyKey> setValue(SkyValue value, Version version) throws InterruptedException {
+ TreeSet<SkyKey> result = new TreeSet<>(ALPHABETICAL_SKYKEY_COMPARATOR);
+ result.addAll(super.setValue(value, version));
+ return result;
+ }
}
}
diff --git a/src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java b/src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java
index dc28c772aa..3fc2e27ff7 100644
--- a/src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java
+++ b/src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java
@@ -38,7 +38,6 @@ import com.google.common.util.concurrent.Uninterruptibles;
import com.google.devtools.build.lib.events.DelegatingEventHandler;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventCollector;
-import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.testutil.TestThread;
@@ -659,6 +658,60 @@ public class MemoizingEvaluatorTest {
}
@Test
+ public void depMessageBeforeNodeMessageOrNodeValue() throws Exception {
+ SkyKey top = skyKey("top");
+ AtomicBoolean depWarningEmitted = new AtomicBoolean(false);
+ injectGraphListenerForTesting(
+ (key, type, order, context) -> {
+ if (key.equals(top) && type == EventType.SET_VALUE) {
+ assertThat(depWarningEmitted.get()).isTrue();
+ }
+ },
+ /*deterministic=*/ false);
+ String depWarning = "dep warning";
+ Event topWarning = Event.warn("top warning");
+ reporter =
+ new DelegatingEventHandler(reporter) {
+ @Override
+ public void handle(Event e) {
+ if (e.getMessage().equals(depWarning)) {
+ depWarningEmitted.set(true);
+ }
+ if (e.equals(topWarning)) {
+ assertThat(depWarningEmitted.get()).isTrue();
+ }
+ super.handle(e);
+ }
+ };
+ SkyKey leaf = skyKey("leaf");
+ tester.getOrCreate(leaf).setWarning(depWarning).setConstantValue(new StringValue("leaf"));
+ tester
+ .getOrCreate(top)
+ .setBuilder(
+ new NoExtractorFunction() {
+ @Nullable
+ @Override
+ public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
+ SkyValue depValue = env.getValue(leaf);
+ if (depValue != null) {
+ // Default GraphTester implementation warns before requesting deps, which doesn't
+ // work
+ // for ordering assertions with memoizing evaluator subclsses that don't store
+ // events
+ // and instead just pass them through directly. By warning after the dep is done
+ // we
+ // avoid that issue.
+ env.getListener().handle(topWarning);
+ }
+ return depValue;
+ }
+ });
+ EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/ false, top);
+ assertThatEvaluationResult(result).hasEntryThat(top).isEqualTo(new StringValue("leaf"));
+ assertThatEvents(eventCollector).containsExactly(depWarning, topWarning.getMessage()).inOrder();
+ }
+
+ @Test
public void invalidationWithChangeAndThenNothingChanged() throws Exception {
SkyKey bKey = GraphTester.nonHermeticKey("b");
tester.getOrCreate("a").addDependency(bKey).setComputedValue(COPY);
@@ -2079,46 +2132,66 @@ public class MemoizingEvaluatorTest {
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");
+ final SkyKey top = GraphTester.toSkyKey("a_top");
+ // otherTop is alphabetically after top.
+ SkyKey otherTop = skyKey("z_otherTop");
// 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");
+ // 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 graph listener block on the second reverse dep to signal.
+ SkyKey firstKey = GraphTester.nonHermeticKey("first");
+ tester.set(firstKey, new StringValue("biding"));
// 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);
+ final CountDownLatch topRequestedDepOrRestartedBuild = new CountDownLatch(1);
+ final CountDownLatch parentsRequested = new CountDownLatch(2);
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");
+ (key, type, order, context) -> {
+ if (!delayTopSignaling.get()) {
+ return;
+ }
+ if (key.equals(otherTop) && type == EventType.SIGNAL) {
+ TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(
+ topRequestedDepOrRestartedBuild, "top's builder did not start in time");
+ return;
+ }
+ if (key.equals(firstKey) && type == EventType.ADD_REVERSE_DEP && order == Order.AFTER) {
+ parentsRequested.countDown();
+ return;
+ }
+ if (key.equals(firstKey) && type == EventType.CHECK_IF_DONE && order == Order.AFTER) {
+ parentsRequested.countDown();
+ if (throwError) {
+ topRequestedDepOrRestartedBuild.countDown();
}
+ 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(firstKey) && type == EventType.SET_VALUE && order == Order.BEFORE) {
+ // Make sure both parents add themselves as rdeps.
+ TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(
+ parentsRequested, "parents did not request dep in time");
+ }
+ if (key.equals(slowAddingDep)
+ && type == EventType.CHECK_IF_DONE
+ && 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.
- SkyKey firstKey = GraphTester.nonHermeticKey("first");
- tester.set(firstKey, new StringValue("biding"));
tester.set(slowAddingDep, new StringValue("dep"));
final AtomicInteger numTopInvocations = new AtomicInteger(0);
tester
@@ -2130,9 +2203,9 @@ public class MemoizingEvaluatorTest {
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();
+ // The graph listener will block on firstKey's signaling of otherTop above until
+ // this thread starts running.
+ topRequestedDepOrRestartedBuild.countDown();
}
// top's builder just requests both deps in a group.
env.getValuesOrThrow(
@@ -2140,31 +2213,37 @@ public class MemoizingEvaluatorTest {
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);
assertThat(result.hasError()).isFalse();
assertThat(result.get(top)).isEqualTo(new StringValue("top"));
assertThat(numTopInvocations.get()).isEqualTo(2);
// Now dirty the graph, and maybe have firstKey throw an error.
- String warningText = "warning text";
- tester.getOrCreate(firstKey, /*markAsModified=*/true).setHasError(throwError)
- .setWarning(warningText);
+ if (throwError) {
+ tester
+ .getOrCreate(firstKey, /*markAsModified=*/ true)
+ .setConstantValue(null)
+ .setBuilder(
+ new NoExtractorFunction() {
+ @Nullable
+ @Override
+ public SkyValue compute(SkyKey skyKey, Environment env)
+ throws SkyFunctionException {
+ TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(
+ parentsRequested, "both parents didn't request in time");
+ throw new GenericFunctionException(
+ new SomeErrorException(firstKey.toString()), Transience.PERSISTENT);
+ }
+ });
+ } else {
+ tester
+ .getOrCreate(firstKey, /*markAsModified=*/ true)
+ .setConstantValue(new StringValue("new"));
+ }
+ tester.getOrCreate(otherTop).addDependency(firstKey).setComputedValue(CONCATENATE);
tester.invalidate();
delayTopSignaling.set(true);
- result = tester.eval(/*keepGoing=*/false, top);
+ result = tester.eval(/*keepGoing=*/ false, top, otherTop);
if (throwError) {
assertThat(result.hasError()).isTrue();
assertThat(result.keyNames()).isEmpty(); // No successfully evaluated values.
@@ -2184,9 +2263,8 @@ public class MemoizingEvaluatorTest {
.that(numTopInvocations.get())
.isEqualTo(3);
}
- assertThatEvents(eventCollector).containsExactly(warningText);
assertThat(topSignaled.getCount()).isEqualTo(0);
- assertThat(topRestartedBuild.getCount()).isEqualTo(0);
+ assertThat(topRequestedDepOrRestartedBuild.getCount()).isEqualTo(0);
}
@Test
diff --git a/src/test/java/com/google/devtools/build/skyframe/NotifyingHelper.java b/src/test/java/com/google/devtools/build/skyframe/NotifyingHelper.java
index c24f54e418..35d2c70e82 100644
--- a/src/test/java/com/google/devtools/build/skyframe/NotifyingHelper.java
+++ b/src/test/java/com/google/devtools/build/skyframe/NotifyingHelper.java
@@ -309,7 +309,9 @@ public class NotifyingHelper {
public DependencyState checkIfDoneForDirtyReverseDep(SkyKey reverseDep)
throws InterruptedException {
graphListener.accept(myKey, EventType.CHECK_IF_DONE, Order.BEFORE, reverseDep);
- return super.checkIfDoneForDirtyReverseDep(reverseDep);
+ DependencyState dependencyState = super.checkIfDoneForDirtyReverseDep(reverseDep);
+ graphListener.accept(myKey, EventType.CHECK_IF_DONE, Order.AFTER, reverseDep);
+ return dependencyState;
}
@Override