blob: d6301559820d3afc53ad3426162927af7ec50657 [file] [log] [blame]
// Copyright 2019 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.skyframe;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.ActionKeyContext;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.util.TestAction;
import com.google.devtools.build.lib.actionsketch.ActionSketch;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for top-down, transitive action caching. */
@RunWith(JUnit4.class)
public class TopDownActionCacheTest extends TimestampBuilderTestCase {
@Override
protected TopDownActionCache initTopDownActionCache() {
return new InMemoryTopDownActionCache();
}
private void buildArtifacts(Artifact... artifacts) throws Exception {
buildArtifacts(amnesiacBuilder(), artifacts);
}
private static NestedSet<Artifact> asNestedSet(Artifact... artifacts) {
return NestedSetBuilder.create(Order.STABLE_ORDER, artifacts);
}
@Test
public void testAmnesiacBuilderGetsTopDownHit() throws Exception {
Artifact hello = createDerivedArtifact("hello");
Button button = createActionButton(emptyNestedSet, ImmutableSet.of(hello));
button.pressed = false;
buildArtifacts(hello);
assertThat(button.pressed).isTrue();
button.pressed = false;
buildArtifacts(hello);
assertThat(button.pressed).isFalse();
}
@Test
public void testTransitiveTopDownCache() throws Exception {
Artifact hello = createDerivedArtifact("hello");
Artifact hello2 = createDerivedArtifact("hello2");
Button button = createActionButton(emptyNestedSet, ImmutableSet.of(hello));
Button button2 = createActionButton(asNestedSet(hello), ImmutableSet.of(hello2));
button.pressed = false;
button2.pressed = false;
buildArtifacts(hello2);
assertThat(button.pressed).isTrue();
assertThat(button2.pressed).isTrue();
button.pressed = false;
button2.pressed = false;
buildArtifacts(hello2);
assertThat(button.pressed).isFalse();
assertThat(button2.pressed).isFalse();
}
@Test
public void testActionKeyCaching() throws Exception {
Artifact hello = createDerivedArtifact("hello");
Artifact hello2 = createDerivedArtifact("hello2");
ActionKeyButton button = createActionKeyButton(emptyNestedSet, ImmutableSet.of(hello), "abc");
ActionKeyButton button2 =
createActionKeyButton(asNestedSet(hello), ImmutableSet.of(hello2), "xyz");
button.pressed = false;
button2.pressed = false;
buildArtifacts(hello2);
assertThat(button.pressed).isTrue();
assertThat(button2.pressed).isTrue();
button.pressed = false;
button2.pressed = false;
buildArtifacts(hello2);
assertThat(button.pressed).isFalse();
assertThat(button2.pressed).isFalse();
clearActions();
hello = createDerivedArtifact("hello");
hello2 = createDerivedArtifact("hello2");
button = createActionKeyButton(emptyNestedSet, ImmutableSet.of(hello), "abc");
button2 = createActionKeyButton(asNestedSet(hello), ImmutableSet.of(hello2), "123");
button.pressed = false;
button2.pressed = false;
buildArtifacts(hello2);
assertThat(button.pressed).isFalse();
assertThat(button2.pressed).isTrue();
button.pressed = false;
button2.pressed = false;
buildArtifacts(hello2);
assertThat(button.pressed).isFalse();
assertThat(button2.pressed).isFalse();
clearActions();
hello = createDerivedArtifact("hello");
hello2 = createDerivedArtifact("hello2");
button = createActionKeyButton(emptyNestedSet, ImmutableSet.of(hello), "456");
button2 = createActionKeyButton(asNestedSet(hello), ImmutableSet.of(hello2), "123");
button.pressed = false;
button2.pressed = false;
buildArtifacts(hello2);
assertThat(button.pressed).isTrue();
assertThat(button2.pressed).isTrue();
button.pressed = false;
button2.pressed = false;
buildArtifacts(hello2);
assertThat(button.pressed).isFalse();
assertThat(button2.pressed).isFalse();
}
@Test
public void testSingleSourceArtifactChanged() throws Exception {
Artifact hello = createSourceArtifact("hello");
hello.getPath().getParentDirectory().createDirectoryAndParents();
FileSystemUtils.writeContentAsLatin1(hello.getPath(), "content1");
Artifact goodbye = createDerivedArtifact("goodbye");
Button button = createActionButton(asNestedSet(hello), ImmutableSet.of(goodbye));
button.pressed = false;
buildArtifacts(goodbye);
assertThat(button.pressed).isTrue();
button.pressed = false;
buildArtifacts(goodbye);
assertThat(button.pressed).isFalse(); // top-down cached
FileSystemUtils.writeContentAsLatin1(hello.getPath(), "content1");
button.pressed = false;
buildArtifacts(goodbye);
assertThat(button.pressed).isFalse(); // top-down cached
FileSystemUtils.writeContentAsLatin1(hello.getPath(), "content2");
button.pressed = false;
buildArtifacts(goodbye);
assertThat(button.pressed).isTrue(); // built
button.pressed = false;
buildArtifacts(goodbye);
assertThat(button.pressed).isFalse(); // top-down cached
}
private static class InMemoryTopDownActionCache implements TopDownActionCache {
private final Cache<ActionSketch, ActionExecutionValue> cache =
CacheBuilder.newBuilder().build();
@Nullable
@Override
public ActionExecutionValue get(ActionSketch sketch) {
return cache.getIfPresent(sketch);
}
@Override
public void put(ActionSketch sketch, ActionExecutionValue value) {
cache.put(sketch, value);
}
}
private static class MutableActionKeyAction extends TestAction {
private final ActionKeyButton button;
public MutableActionKeyAction(
ActionKeyButton button, NestedSet<Artifact> inputs, ImmutableSet<Artifact> outputs) {
super(button, inputs, outputs);
this.button = button;
}
@Override
protected void computeKey(
ActionKeyContext actionKeyContext,
@Nullable Artifact.ArtifactExpander artifactExpander,
Fingerprint fp) {
super.computeKey(actionKeyContext, artifactExpander, fp);
fp.addString(button.key);
}
}
private static class ActionKeyButton extends Button {
private final String key;
public ActionKeyButton(String key) {
this.key = key;
}
}
private ActionKeyButton createActionKeyButton(
NestedSet<Artifact> inputs, ImmutableSet<Artifact> outputs, String key) {
ActionKeyButton button = new ActionKeyButton(key);
registerAction(new MutableActionKeyAction(button, inputs, outputs));
return button;
}
}