blob: fecdccae31801e0b26726ced3c9a2b51d694171a [file] [log] [blame]
Rumou Duan73876202016-06-06 18:52:08 +00001// Copyright 2016 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.skyframe;
15
16import static com.google.common.truth.Truth.assertThat;
17import static org.junit.Assert.fail;
18
tomlua155b532017-11-08 20:12:47 +010019import com.google.common.base.Preconditions;
janakr93e3eea2017-03-30 22:09:37 +000020import com.google.common.base.Suppliers;
Rumou Duan73876202016-06-06 18:52:08 +000021import com.google.common.collect.ImmutableList;
22import com.google.common.collect.ImmutableMap;
23import com.google.common.collect.Iterables;
24import com.google.devtools.build.lib.actions.Action;
25import com.google.devtools.build.lib.actions.ActionInputHelper;
26import com.google.devtools.build.lib.actions.Artifact;
27import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
28import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType;
29import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
30import com.google.devtools.build.lib.actions.ArtifactOwner;
31import com.google.devtools.build.lib.actions.ArtifactPrefixConflictException;
32import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
33import com.google.devtools.build.lib.actions.Root;
34import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
35import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
36import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate;
37import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate.OutputPathMapper;
38import com.google.devtools.build.lib.events.NullEventHandler;
39import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000040import com.google.devtools.build.lib.skyframe.ArtifactSkyKey.OwnedArtifact;
Rumou Duan73876202016-06-06 18:52:08 +000041import com.google.devtools.build.lib.testutil.FoundationTestCase;
Rumou Duan73876202016-06-06 18:52:08 +000042import com.google.devtools.build.lib.vfs.Path;
43import com.google.devtools.build.lib.vfs.PathFragment;
44import com.google.devtools.build.skyframe.EvaluationResult;
45import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
46import com.google.devtools.build.skyframe.MemoizingEvaluator;
47import com.google.devtools.build.skyframe.RecordingDifferencer;
janakr1cde8722017-10-10 03:22:21 +020048import com.google.devtools.build.skyframe.SequencedRecordingDifferencer;
Rumou Duan73876202016-06-06 18:52:08 +000049import com.google.devtools.build.skyframe.SequentialBuildDriver;
50import com.google.devtools.build.skyframe.SkyFunction;
51import com.google.devtools.build.skyframe.SkyFunctionName;
52import com.google.devtools.build.skyframe.SkyKey;
53import com.google.devtools.build.skyframe.SkyValue;
Rumou Duan73876202016-06-06 18:52:08 +000054import java.util.LinkedHashMap;
55import java.util.List;
56import java.util.Map;
57import java.util.UUID;
58import java.util.concurrent.atomic.AtomicReference;
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000059import org.junit.Before;
60import org.junit.Test;
61import org.junit.runner.RunWith;
62import org.junit.runners.JUnit4;
Rumou Duan73876202016-06-06 18:52:08 +000063
64/** Tests for {@link ActionTemplateExpansionFunction}. */
65@RunWith(JUnit4.class)
66public final class ActionTemplateExpansionFunctionTest extends FoundationTestCase {
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000067 private Map<Artifact, TreeArtifactValue> artifactValueMap;
Rumou Duan73876202016-06-06 18:52:08 +000068 private SequentialBuildDriver driver;
69
70 @Before
71 public void setUp() throws Exception {
72 artifactValueMap = new LinkedHashMap<>();
John Catere0d1d0e2017-11-28 20:47:41 -080073 AtomicReference<PathPackageLocator> pkgLocator =
74 new AtomicReference<>(
75 new PathPackageLocator(
76 rootDirectory.getFileSystem().getPath("/outputbase"),
77 ImmutableList.of(rootDirectory),
78 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
janakr1cde8722017-10-10 03:22:21 +020079 RecordingDifferencer differencer = new SequencedRecordingDifferencer();
Rumou Duan73876202016-06-06 18:52:08 +000080 MemoizingEvaluator evaluator =
81 new InMemoryMemoizingEvaluator(
82 ImmutableMap.<SkyFunctionName, SkyFunction>builder()
janakr93e3eea2017-03-30 22:09:37 +000083 .put(SkyFunctions.ARTIFACT, new DummyArtifactFunction(artifactValueMap))
84 .put(
85 SkyFunctions.ACTION_TEMPLATE_EXPANSION,
86 new ActionTemplateExpansionFunction(Suppliers.ofInstance(false)))
Rumou Duan73876202016-06-06 18:52:08 +000087 .build(),
88 differencer);
89 driver = new SequentialBuildDriver(evaluator);
90 PrecomputedValue.BUILD_ID.set(differencer, UUID.randomUUID());
91 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
92 }
93
94 @Test
95 public void testActionTemplateExpansionFunction() throws Exception {
96 Artifact inputTreeArtifact = createAndPopulateTreeArtifact(
97 "inputTreeArtifact", "child0", "child1", "child2");
98 Artifact outputTreeArtifact = createTreeArtifact("outputTreeArtifact");
99
100 SpawnActionTemplate spawnActionTemplate = ActionsTestUtil.createDummySpawnActionTemplate(
101 inputTreeArtifact, outputTreeArtifact);
102 List<Action> actions = evaluate(spawnActionTemplate);
103 assertThat(actions).hasSize(3);
104
105 ArtifactOwner owner = ActionTemplateExpansionValue.createActionTemplateExpansionKey(
106 spawnActionTemplate);
107 int i = 0;
108 for (Action action : actions) {
109 String childName = "child" + i;
110 assertThat(Artifact.toExecPaths(action.getInputs())).contains(
111 "out/inputTreeArtifact/" + childName);
112 assertThat(Artifact.toExecPaths(action.getOutputs())).containsExactly(
113 "out/outputTreeArtifact/" + childName);
114 assertThat(Iterables.getOnlyElement(action.getOutputs()).getArtifactOwner()).isEqualTo(owner);
115 ++i;
116 }
117 }
118
119 @Test
120 public void testThrowsOnActionConflict() throws Exception {
121 Artifact inputTreeArtifact = createAndPopulateTreeArtifact(
122 "inputTreeArtifact", "child0", "child1", "child2");
123 Artifact outputTreeArtifact = createTreeArtifact("outputTreeArtifact");
124
125
126 OutputPathMapper mapper = new OutputPathMapper() {
127 @Override
128 public PathFragment parentRelativeOutputPath(TreeFileArtifact inputTreeFileArtifact) {
nharmatab4060b62017-04-04 17:11:39 +0000129 return PathFragment.create("conflict_path");
Rumou Duan73876202016-06-06 18:52:08 +0000130 }
131 };
132 SpawnActionTemplate spawnActionTemplate =
133 new SpawnActionTemplate.Builder(inputTreeArtifact, outputTreeArtifact)
nharmatab4060b62017-04-04 17:11:39 +0000134 .setExecutable(PathFragment.create("/bin/cp"))
Rumou Duan73876202016-06-06 18:52:08 +0000135 .setCommandLineTemplate(CustomCommandLine.builder().build())
136 .setOutputPathMapper(mapper)
137 .build(ActionsTestUtil.NULL_ACTION_OWNER);
138
139 try {
140 evaluate(spawnActionTemplate);
141 fail("Expected ActionConflictException");
142 } catch (ActionConflictException e) {
143 // Expected ActionConflictException
144 }
145 }
146
147 @Test
148 public void testThrowsOnArtifactPrefixConflict() throws Exception {
149 Artifact inputTreeArtifact = createAndPopulateTreeArtifact(
150 "inputTreeArtifact", "child0", "child1", "child2");
151 Artifact outputTreeArtifact = createTreeArtifact("outputTreeArtifact");
152
153 OutputPathMapper mapper = new OutputPathMapper() {
154 private int i = 0;
155 @Override
156 public PathFragment parentRelativeOutputPath(TreeFileArtifact inputTreeFileArtifact) {
157 PathFragment path;
158 switch (i) {
159 case 0:
nharmatab4060b62017-04-04 17:11:39 +0000160 path = PathFragment.create("path_prefix");
Rumou Duan73876202016-06-06 18:52:08 +0000161 break;
162 case 1:
nharmatab4060b62017-04-04 17:11:39 +0000163 path = PathFragment.create("path_prefix/conflict");
Rumou Duan73876202016-06-06 18:52:08 +0000164 break;
165 default:
166 path = inputTreeFileArtifact.getParentRelativePath();
167 }
168
169 ++i;
170 return path;
171 }
172 };
173 SpawnActionTemplate spawnActionTemplate =
174 new SpawnActionTemplate.Builder(inputTreeArtifact, outputTreeArtifact)
nharmatab4060b62017-04-04 17:11:39 +0000175 .setExecutable(PathFragment.create("/bin/cp"))
Rumou Duan73876202016-06-06 18:52:08 +0000176 .setCommandLineTemplate(CustomCommandLine.builder().build())
177 .setOutputPathMapper(mapper)
178 .build(ActionsTestUtil.NULL_ACTION_OWNER);
179
180 try {
181 evaluate(spawnActionTemplate);
182 fail("Expected ArtifactPrefixConflictException");
183 } catch (ArtifactPrefixConflictException e) {
184 // Expected ArtifactPrefixConflictException
185 }
186 }
187
188 private List<Action> evaluate(SpawnActionTemplate spawnActionTemplate) throws Exception {
189 SkyKey skyKey = ActionTemplateExpansionValue.key(spawnActionTemplate);
190 EvaluationResult<ActionTemplateExpansionValue> result = driver.evaluate(
191 ImmutableList.of(skyKey),
192 false,
193 SkyframeExecutor.DEFAULT_THREAD_COUNT,
194 NullEventHandler.INSTANCE);
195 if (result.hasError()) {
196 throw result.getError().getException();
197 }
janakr93e3eea2017-03-30 22:09:37 +0000198 ActionTemplateExpansionValue actionTemplateExpansionValue = result.get(skyKey);
199 ImmutableList.Builder<Action> actionList = ImmutableList.builder();
200 for (int i = 0; i < actionTemplateExpansionValue.getNumActions(); i++) {
201 actionList.add(actionTemplateExpansionValue.getAction(i));
202 }
203 return actionList.build();
Rumou Duan73876202016-06-06 18:52:08 +0000204 }
205
206 private Artifact createTreeArtifact(String path) {
nharmatab4060b62017-04-04 17:11:39 +0000207 PathFragment execPath = PathFragment.create("out").getRelative(path);
Rumou Duan73876202016-06-06 18:52:08 +0000208 Path fullPath = rootDirectory.getRelative(execPath);
209 return new SpecialArtifact(
210 fullPath,
211 Root.asDerivedRoot(rootDirectory, rootDirectory.getRelative("out")),
212 execPath,
213 ArtifactOwner.NULL_OWNER,
214 SpecialArtifactType.TREE);
215 }
216
217 private Artifact createAndPopulateTreeArtifact(String path, String... childRelativePaths)
218 throws Exception {
219 Artifact treeArtifact = createTreeArtifact(path);
220 Map<TreeFileArtifact, FileArtifactValue> treeFileArtifactMap = new LinkedHashMap<>();
221
222 for (String childRelativePath : childRelativePaths) {
223 TreeFileArtifact treeFileArtifact = ActionInputHelper.treeFileArtifact(
nharmatab4060b62017-04-04 17:11:39 +0000224 treeArtifact, PathFragment.create(childRelativePath));
Rumou Duan73876202016-06-06 18:52:08 +0000225 scratch.file(treeFileArtifact.getPath().toString(), childRelativePath);
226 // We do not care about the FileArtifactValues in this test.
227 treeFileArtifactMap.put(treeFileArtifact, FileArtifactValue.create(treeFileArtifact));
228 }
229
230 artifactValueMap.put(
231 treeArtifact, TreeArtifactValue.create(ImmutableMap.copyOf(treeFileArtifactMap)));
232
233 return treeArtifact;
234 }
235
236 /** Dummy ArtifactFunction that just returns injected values */
237 private static class DummyArtifactFunction implements SkyFunction {
Janak Ramakrishnanad77f972016-07-29 20:58:42 +0000238 private final Map<Artifact, TreeArtifactValue> artifactValueMap;
Rumou Duan73876202016-06-06 18:52:08 +0000239
Janak Ramakrishnanad77f972016-07-29 20:58:42 +0000240 DummyArtifactFunction(Map<Artifact, TreeArtifactValue> artifactValueMap) {
Rumou Duan73876202016-06-06 18:52:08 +0000241 this.artifactValueMap = artifactValueMap;
242 }
243 @Override
244 public SkyValue compute(SkyKey skyKey, Environment env) {
245 OwnedArtifact ownedArtifact = (OwnedArtifact) skyKey.argument();
246 Artifact artifact = ownedArtifact.getArtifact();
247 return Preconditions.checkNotNull(artifactValueMap.get(artifact));
248 }
249
250 @Override
251 public String extractTag(SkyKey skyKey) {
252 return null;
253 }
254 }
255}