blob: c1de1a58bf83d995754cb77bc0920f349d542237 [file] [log] [blame]
// Copyright 2020 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 static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.ActionLookupData;
import com.google.devtools.build.lib.actions.ActionLookupKey;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.ArchivedTreeArtifact;
import com.google.devtools.build.lib.actions.Artifact.DerivedArtifact;
import com.google.devtools.build.lib.actions.Artifact.OwnerlessArtifactWrapper;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.actions.ArtifactRoot.RootType;
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil.NullAction;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.skyframe.TreeArtifactValue.ArchivedRepresentation;
import com.google.devtools.build.lib.testutil.Scratch;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import java.io.IOException;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for {@link ActionExecutionValue#transformForSharedAction} for values including tree
* artifacts.
*/
@RunWith(TestParameterInjector.class)
public final class ActionExecutionValueTransformSharedTreeArtifactsTest {
private static final PathFragment DERIVED_PATH_PREFIX = PathFragment.create("bazel-out");
private static final ActionLookupKey KEY_1 = mock(ActionLookupKey.class);
private static final ActionLookupKey KEY_2 = mock(ActionLookupKey.class);
@TestParameter private boolean includeArchivedTreeArtifacts;
private final Scratch scratch = new Scratch();
private ArtifactRoot derivedRoot;
@BeforeClass
public static void initMocks() {
when(KEY_1.toKey()).thenReturn(KEY_1);
when(KEY_2.toKey()).thenReturn(KEY_2);
}
@Before
public void createDerivedRoot() throws IOException {
derivedRoot =
ArtifactRoot.asDerivedRoot(scratch.dir("/execroot"), RootType.Output, DERIVED_PATH_PREFIX);
}
@Test
public void transformForSharedAction_createsCopyOfEmptyTreeArtifact() throws Exception {
SpecialArtifact tree = createTreeArtifact("dir", KEY_1);
TreeArtifactValue value = createTreeArtifactValue(tree);
ActionExecutionValue actionExecutionValue =
createActionExecutionValue(ImmutableMap.of(tree, value));
SpecialArtifact tree2 = createTreeArtifact("dir", KEY_2);
ActionExecutionValue transformedValue =
actionExecutionValue.transformForSharedAction(new NullAction(tree2));
assertThat(transformedValue.getAllFileValues()).isEmpty();
assertThat(transformedValue.getAllTreeArtifactValues().keySet()).containsExactly(tree2);
assertEqualsWithNewParent(value, tree2, transformedValue.getTreeArtifactValue(tree2));
}
@Test
public void transformForSharedAction_createsCopyOfTreeArtifact() throws Exception {
SpecialArtifact tree = createTreeArtifact("dir", KEY_1);
TreeArtifactValue value = createTreeArtifactValue(tree, "file1", "file2");
ActionExecutionValue actionExecutionValue =
createActionExecutionValue(ImmutableMap.of(tree, value));
SpecialArtifact tree2 = createTreeArtifact("dir", KEY_2);
ActionExecutionValue transformedValue =
actionExecutionValue.transformForSharedAction(new NullAction(tree2));
assertThat(transformedValue.getAllFileValues()).isEmpty();
assertThat(transformedValue.getAllTreeArtifactValues().keySet()).containsExactly(tree2);
assertEqualsWithNewParent(value, tree2, transformedValue.getTreeArtifactValue(tree2));
}
@Test
public void transformForSharedAction_createsCopyOfMultipleTreeArtifacts() throws Exception {
SpecialArtifact tree1 = createTreeArtifact("dir1", KEY_1);
SpecialArtifact tree2 = createTreeArtifact("dir2", KEY_1);
TreeArtifactValue value1 = createTreeArtifactValue(tree1, "file1");
TreeArtifactValue value2 = createTreeArtifactValue(tree2, "file2", "file3");
ActionExecutionValue actionExecutionValue =
createActionExecutionValue(ImmutableMap.of(tree1, value1, tree2, value2));
SpecialArtifact sharedTree1 = createTreeArtifact("dir1", KEY_2);
SpecialArtifact sharedTree2 = createTreeArtifact("dir2", KEY_2);
ActionExecutionValue transformedValue =
actionExecutionValue.transformForSharedAction(new NullAction(sharedTree1, sharedTree2));
assertThat(transformedValue.getAllFileValues()).isEmpty();
assertThat(transformedValue.getAllTreeArtifactValues().keySet())
.containsExactly(sharedTree1, sharedTree2);
assertEqualsWithNewParent(
value1, sharedTree1, transformedValue.getTreeArtifactValue(sharedTree1));
assertEqualsWithNewParent(
value2, sharedTree2, transformedValue.getTreeArtifactValue(sharedTree2));
}
@Test
public void transformForSharedAction_createsCopyForFileAndTreeArtifacts() throws Exception {
DerivedArtifact file = createFileArtifact("file", KEY_1);
createFile(file.getPath());
FileArtifactValue fileValue = FileArtifactValue.createForTesting(file);
SpecialArtifact tree = createTreeArtifact("dir", KEY_1);
TreeArtifactValue treeValue = createTreeArtifactValue(tree, "file1", "file2");
ActionExecutionValue actionExecutionValue =
createActionExecutionValue(
ImmutableMap.of(file, fileValue), ImmutableMap.of(tree, treeValue));
SpecialArtifact sharedTree = createTreeArtifact("dir", KEY_2);
DerivedArtifact sharedFile = createFileArtifact("file", KEY_2);
ActionExecutionValue transformedValue =
actionExecutionValue.transformForSharedAction(new NullAction(sharedFile, sharedTree));
assertThat(transformedValue.getAllFileValues().keySet()).containsExactly(sharedFile);
assertThat(transformedValue.getAllFileValues().get(sharedFile)).isSameInstanceAs(fileValue);
assertThat(transformedValue.getAllTreeArtifactValues().keySet()).containsExactly(sharedTree);
assertEqualsWithNewParent(
treeValue, sharedTree, transformedValue.getTreeArtifactValue(sharedTree));
}
/**
* Checks that provided {@link TreeArtifactValue} has equal metadata to the original one, expected
* parent for all of the included artifacts and otherwise the same artifacts as the original one.
*/
private void assertEqualsWithNewParent(
TreeArtifactValue originalValue,
SpecialArtifact expectedTree,
TreeArtifactValue actualValue) {
assertThat(actualValue.getDigest()).isEqualTo(originalValue.getDigest());
assertThat(actualValue.getMetadata()).isEqualTo(originalValue.getMetadata());
assertThat(actualValue.getChildPaths()).isEqualTo(originalValue.getChildPaths());
assertThat(actualValue.getArchivedRepresentation().isPresent())
.isEqualTo(includeArchivedTreeArtifacts);
actualValue
.getArchivedRepresentation()
.ifPresent(
archivedRepresentation -> {
ArchivedRepresentation originalRepresentation =
originalValue.getArchivedRepresentation().get();
assertEqualsWithNewParent(
originalRepresentation, expectedTree, archivedRepresentation);
});
actualValue
.getChildValues()
.forEach(
(artifact, metadata) -> {
TreeFileArtifact originalArtifact =
originalValue.getChildren().stream()
.filter(
original ->
original
.getParentRelativePath()
.equals(artifact.getParentRelativePath()))
.findAny()
.get();
assertThat(artifact.getParent()).isEqualTo(expectedTree);
assertThat(artifact.getGeneratingActionKey())
.isEqualTo(expectedTree.getGeneratingActionKey());
assertOwnerlessEquals(originalArtifact, artifact);
assertThat(artifact.isChildOfDeclaredDirectory())
.isEqualTo(originalArtifact.isChildOfDeclaredDirectory());
assertThat(metadata)
.isSameInstanceAs(originalValue.getChildValues().get(originalArtifact));
});
}
private static void assertEqualsWithNewParent(
ArchivedRepresentation expectedRepresentation,
SpecialArtifact expectedTree,
ArchivedRepresentation actualRepresentation) {
assertThat(actualRepresentation.archivedTreeFileArtifact().getParent()).isEqualTo(expectedTree);
assertThat(actualRepresentation.archivedTreeFileArtifact().getGeneratingActionKey())
.isEqualTo(expectedTree.getGeneratingActionKey());
assertOwnerlessEquals(
expectedRepresentation.archivedTreeFileArtifact(),
actualRepresentation.archivedTreeFileArtifact());
assertThat(actualRepresentation.archivedFileValue())
.isSameInstanceAs(expectedRepresentation.archivedFileValue());
}
private static void assertOwnerlessEquals(Artifact expectedArtifact, Artifact actualArtifact) {
assertThat(new OwnerlessArtifactWrapper(actualArtifact))
.isEqualTo(new OwnerlessArtifactWrapper(expectedArtifact));
}
private TreeArtifactValue createTreeArtifactValue(
SpecialArtifact treeArtifact, String... parentRelativePaths) throws IOException {
TreeArtifactValue.Builder builder = TreeArtifactValue.newBuilder(treeArtifact);
for (String parentRelativePath : parentRelativePaths) {
TreeFileArtifact childArtifact =
TreeFileArtifact.createTreeOutput(treeArtifact, parentRelativePath);
createFile(childArtifact.getPath());
builder.putChild(childArtifact, FileArtifactValue.createForTesting(childArtifact));
}
if (includeArchivedTreeArtifacts) {
ArchivedTreeArtifact archivedArtifact = ArchivedTreeArtifact.createForTree(treeArtifact);
createFile(archivedArtifact.getPath());
builder.setArchivedRepresentation(
archivedArtifact, FileArtifactValue.createForTesting(archivedArtifact));
}
return builder.build();
}
private DerivedArtifact createFileArtifact(String relativePath, ActionLookupKey owner) {
return DerivedArtifact.create(
derivedRoot, DERIVED_PATH_PREFIX.getRelative(relativePath), owner);
}
private SpecialArtifact createTreeArtifact(String relativePath, ActionLookupKey owner) {
SpecialArtifact treeArtifact =
SpecialArtifact.create(
derivedRoot,
DERIVED_PATH_PREFIX.getRelative(relativePath),
owner,
SpecialArtifactType.TREE);
treeArtifact.setGeneratingActionKey(ActionLookupData.create(owner, 0));
return treeArtifact;
}
private static ActionExecutionValue createActionExecutionValue(
ImmutableMap<Artifact, TreeArtifactValue> treeArtifacts) {
return createActionExecutionValue(/*fileArtifacts=*/ ImmutableMap.of(), treeArtifacts);
}
private static ActionExecutionValue createActionExecutionValue(
ImmutableMap<Artifact, FileArtifactValue> fileArtifacts,
ImmutableMap<Artifact, TreeArtifactValue> treeArtifacts) {
return ActionExecutionValue.create(
fileArtifacts,
treeArtifacts,
/* outputSymlinks= */ ImmutableList.of(),
/* discoveredModules= */ NestedSetBuilder.emptySet(Order.STABLE_ORDER));
}
private static void createFile(Path file) throws IOException {
FileSystemUtils.writeIsoLatin1(file);
}
}