| // Copyright 2022 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 com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.testing.EqualsTester; | |
| 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.DerivedArtifact; | |
| 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.FileArtifactValue.RemoteFileArtifactValue; | |
| import com.google.devtools.build.lib.actions.FilesetOutputSymlink; | |
| import com.google.devtools.build.lib.actions.util.ActionsTestUtil; | |
| 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.skyframe.serialization.testutils.SerializationDepsUtils; | |
| import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester; | |
| import com.google.devtools.build.lib.testutil.Scratch; | |
| import com.google.devtools.build.lib.vfs.FileSystem; | |
| import com.google.devtools.build.lib.vfs.PathFragment; | |
| import com.google.devtools.build.lib.vfs.Root.RootCodecDependencies; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.JUnit4; | |
| @RunWith(JUnit4.class) | |
| public final class ActionExecutionValueTest { | |
| private static final FileArtifactValue VALUE_1_REMOTE = | |
| RemoteFileArtifactValue.create( | |
| /* digest= */ new byte[0], | |
| /* size= */ 0, | |
| /* locationIndex= */ 1, | |
| /* expireAtEpochMilli= */ -1); | |
| private static final FileArtifactValue VALUE_2_REMOTE = | |
| RemoteFileArtifactValue.create( | |
| /* digest= */ new byte[0], | |
| /* size= */ 0, | |
| /* locationIndex= */ 2, | |
| /* expireAtEpochMilli= */ -1); | |
| private static final ActionLookupKey KEY = ActionsTestUtil.NULL_ARTIFACT_OWNER; | |
| private static final ActionLookupData ACTION_LOOKUP_DATA_1 = ActionLookupData.create(KEY, 1); | |
| private static final ActionLookupData ACTION_LOOKUP_DATA_2 = ActionLookupData.create(KEY, 2); | |
| private static final ArtifactRoot OUTPUT_ROOT = | |
| ArtifactRoot.asDerivedRoot(new Scratch().resolve("/execroot"), RootType.Output, "out"); | |
| @Test | |
| public void equality() { | |
| SpecialArtifact tree1 = tree("tree1"); | |
| TreeArtifactValue tree1Value1 = | |
| TreeArtifactValue.newBuilder(tree1) | |
| .putChild(TreeFileArtifact.createTreeOutput(tree1, "file1"), VALUE_1_REMOTE) | |
| .build(); | |
| TreeArtifactValue tree1Value2 = | |
| TreeArtifactValue.newBuilder(tree1) | |
| .putChild(TreeFileArtifact.createTreeOutput(tree1, "file1"), VALUE_2_REMOTE) | |
| .build(); | |
| FilesetOutputSymlink symlink1 = | |
| FilesetOutputSymlink.createForTesting( | |
| PathFragment.create("name1"), | |
| PathFragment.create("target1"), | |
| PathFragment.create("execPath1")); | |
| FilesetOutputSymlink symlink2 = | |
| FilesetOutputSymlink.createForTesting( | |
| PathFragment.create("name2"), | |
| PathFragment.create("target2"), | |
| PathFragment.create("execPath2")); | |
| new EqualsTester() | |
| .addEqualityGroup(createWithArtifactData(ImmutableMap.of(output("file1"), VALUE_1_REMOTE))) | |
| .addEqualityGroup(createWithArtifactData(ImmutableMap.of(output("file1"), VALUE_2_REMOTE))) | |
| .addEqualityGroup( | |
| createWithArtifactData( | |
| ImmutableMap.of(output("file1", ACTION_LOOKUP_DATA_2), VALUE_1_REMOTE))) | |
| .addEqualityGroup(createWithArtifactData(ImmutableMap.of(output("file2"), VALUE_1_REMOTE))) | |
| .addEqualityGroup( | |
| createWithArtifactData( | |
| ImmutableMap.of(output("file1"), VALUE_1_REMOTE, output("file2"), VALUE_2_REMOTE))) | |
| // treeArtifactData | |
| .addEqualityGroup( | |
| createWithTreeArtifactData(ImmutableMap.of(tree1, TreeArtifactValue.empty()))) | |
| .addEqualityGroup( | |
| createWithTreeArtifactData(ImmutableMap.of(tree("tree2"), TreeArtifactValue.empty()))) | |
| .addEqualityGroup( | |
| ImmutableMap.of( | |
| tree1, TreeArtifactValue.empty(), tree("tree2"), TreeArtifactValue.empty())) | |
| .addEqualityGroup(ImmutableMap.of(tree1, tree1Value1)) | |
| .addEqualityGroup(ImmutableMap.of(tree1, tree1Value2)) | |
| // outputSymlinks | |
| .addEqualityGroup(createWithOutputSymlinks(ImmutableList.of(symlink1))) | |
| .addEqualityGroup(createWithOutputSymlinks(ImmutableList.of(symlink2))) | |
| .addEqualityGroup(createWithOutputSymlinks(ImmutableList.of(symlink1, symlink2))) | |
| .addEqualityGroup(createWithOutputSymlinks(ImmutableList.of(symlink2, symlink1))) | |
| // discoveredModules | |
| .addEqualityGroup( | |
| createWithDiscoveredModules( | |
| NestedSetBuilder.create(Order.STABLE_ORDER, output("file1"))), | |
| createWithDiscoveredModules( | |
| NestedSetBuilder.create(Order.STABLE_ORDER, output("file1")))) | |
| .addEqualityGroup( | |
| createWithDiscoveredModules( | |
| NestedSetBuilder.create(Order.STABLE_ORDER, output("file1", ACTION_LOOKUP_DATA_2)))) | |
| .addEqualityGroup( | |
| createWithDiscoveredModules( | |
| NestedSetBuilder.<Artifact>stableOrder().add(output("file2")))) | |
| .addEqualityGroup( | |
| createWithDiscoveredModules( | |
| NestedSetBuilder.<Artifact>stableOrder().add(output("file1")).add(output("file2")))) | |
| // Does not detect equality for identical sets with different shape. | |
| .addEqualityGroup( | |
| createWithDiscoveredModules( | |
| NestedSetBuilder.<Artifact>stableOrder() | |
| .add(output("file1")) | |
| .addTransitive(NestedSetBuilder.create(Order.STABLE_ORDER, output("file2"))))) | |
| .testEquals(); | |
| } | |
| @Test | |
| public void serialization() throws Exception { | |
| new SerializationTester( | |
| // Single output file | |
| createWithArtifactData(ImmutableMap.of(output("output1"), VALUE_1_REMOTE)), | |
| // Fileset | |
| createWithOutputSymlinks( | |
| ImmutableList.of( | |
| FilesetOutputSymlink.createForTesting( | |
| PathFragment.create("name"), | |
| PathFragment.create("target"), | |
| PathFragment.create("execPath")))), | |
| // Module discovering | |
| createWithDiscoveredModules( | |
| NestedSetBuilder.create(Order.STABLE_ORDER, output("module"))), | |
| // Multiple output files | |
| createWithArtifactData( | |
| ImmutableMap.of( | |
| output("output1"), VALUE_1_REMOTE, output("output2"), VALUE_2_REMOTE)), | |
| // Single tree | |
| createWithTreeArtifactData(ImmutableMap.of(tree("tree"), TreeArtifactValue.empty())), | |
| // Multiple trees | |
| createWithTreeArtifactData( | |
| ImmutableMap.of( | |
| tree("tree1"), | |
| TreeArtifactValue.empty(), | |
| tree("tree2"), | |
| TreeArtifactValue.empty())), | |
| // Mixed file and tree | |
| ActionExecutionValue.createFromOutputMetadataStore( | |
| ImmutableMap.of(output("file"), VALUE_1_REMOTE), | |
| ImmutableMap.of(tree("tree"), TreeArtifactValue.empty()), | |
| /* outputSymlinks= */ ImmutableList.of(), | |
| /* discoveredModules= */ NestedSetBuilder.emptySet(Order.STABLE_ORDER))) | |
| .addDependency(FileSystem.class, OUTPUT_ROOT.getRoot().getFileSystem()) | |
| .addDependency( | |
| RootCodecDependencies.class, new RootCodecDependencies(OUTPUT_ROOT.getRoot())) | |
| .addDependencies(SerializationDepsUtils.SERIALIZATION_DEPS_FOR_TEST) | |
| .runTests(); | |
| } | |
| private static ActionExecutionValue createWithArtifactData( | |
| ImmutableMap<Artifact, FileArtifactValue> artifactData) { | |
| return ActionExecutionValue.createFromOutputMetadataStore( | |
| /* artifactData= */ artifactData, | |
| /* treeArtifactData= */ ImmutableMap.of(), | |
| /* outputSymlinks= */ ImmutableList.of(), | |
| /* discoveredModules= */ NestedSetBuilder.emptySet(Order.STABLE_ORDER)); | |
| } | |
| private static ActionExecutionValue createWithTreeArtifactData( | |
| ImmutableMap<Artifact, TreeArtifactValue> treeArtifactData) { | |
| return ActionExecutionValue.createFromOutputMetadataStore( | |
| /* artifactData= */ ImmutableMap.of(), | |
| treeArtifactData, | |
| /* outputSymlinks= */ ImmutableList.of(), | |
| /* discoveredModules= */ NestedSetBuilder.emptySet(Order.STABLE_ORDER)); | |
| } | |
| private static ActionExecutionValue createWithOutputSymlinks( | |
| ImmutableList<FilesetOutputSymlink> outputSymlinks) { | |
| return ActionExecutionValue.createFromOutputMetadataStore( | |
| ImmutableMap.of(output("fileset.manifest"), VALUE_1_REMOTE), | |
| /* treeArtifactData= */ ImmutableMap.of(), | |
| outputSymlinks, | |
| /* discoveredModules= */ NestedSetBuilder.emptySet(Order.STABLE_ORDER)); | |
| } | |
| private static ActionExecutionValue createWithDiscoveredModules( | |
| NestedSetBuilder<Artifact> discoveredModules) { | |
| return createWithDiscoveredModules(discoveredModules.build()); | |
| } | |
| private static ActionExecutionValue createWithDiscoveredModules( | |
| NestedSet<Artifact> discoveredModules) { | |
| return ActionExecutionValue.createFromOutputMetadataStore( | |
| /* artifactData= */ ImmutableMap.of(output("modules.pcm"), VALUE_1_REMOTE), | |
| /* treeArtifactData= */ ImmutableMap.of(), | |
| /* outputSymlinks= */ ImmutableList.of(), | |
| discoveredModules); | |
| } | |
| private static DerivedArtifact output(String rootRelativePath) { | |
| return output(rootRelativePath, ACTION_LOOKUP_DATA_1); | |
| } | |
| private static DerivedArtifact output( | |
| String rootRelativePath, ActionLookupData generatingAction) { | |
| DerivedArtifact result = | |
| DerivedArtifact.create( | |
| OUTPUT_ROOT, | |
| OUTPUT_ROOT.getExecPath().getRelative(rootRelativePath), | |
| generatingAction.getActionLookupKey()); | |
| result.setGeneratingActionKey(generatingAction); | |
| return result; | |
| } | |
| private static SpecialArtifact tree(String rootRelativePath) { | |
| SpecialArtifact result = | |
| SpecialArtifact.create( | |
| OUTPUT_ROOT, | |
| OUTPUT_ROOT.getExecPath().getRelative(rootRelativePath), | |
| KEY, | |
| SpecialArtifactType.TREE); | |
| result.setGeneratingActionKey(ACTION_LOOKUP_DATA_1); | |
| return result; | |
| } | |
| } |