Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 1 | // 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. |
| 14 | package com.google.devtools.build.lib.skyframe; |
| 15 | |
| 16 | import static com.google.common.truth.Truth.assertThat; |
| 17 | import static org.junit.Assert.fail; |
| 18 | |
tomlu | a155b53 | 2017-11-08 20:12:47 +0100 | [diff] [blame] | 19 | import com.google.common.base.Preconditions; |
janakr | 93e3eea | 2017-03-30 22:09:37 +0000 | [diff] [blame] | 20 | import com.google.common.base.Suppliers; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 21 | import com.google.common.collect.ImmutableList; |
| 22 | import com.google.common.collect.ImmutableMap; |
| 23 | import com.google.common.collect.Iterables; |
| 24 | import com.google.devtools.build.lib.actions.Action; |
| 25 | import com.google.devtools.build.lib.actions.ActionInputHelper; |
| 26 | import com.google.devtools.build.lib.actions.Artifact; |
| 27 | import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; |
| 28 | import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType; |
| 29 | import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact; |
| 30 | import com.google.devtools.build.lib.actions.ArtifactOwner; |
| 31 | import com.google.devtools.build.lib.actions.ArtifactPrefixConflictException; |
| 32 | import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; |
| 33 | import com.google.devtools.build.lib.actions.Root; |
| 34 | import com.google.devtools.build.lib.actions.util.ActionsTestUtil; |
| 35 | import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; |
| 36 | import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate; |
| 37 | import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate.OutputPathMapper; |
| 38 | import com.google.devtools.build.lib.events.NullEventHandler; |
| 39 | import com.google.devtools.build.lib.pkgcache.PathPackageLocator; |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 40 | import com.google.devtools.build.lib.skyframe.ArtifactSkyKey.OwnedArtifact; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 41 | import com.google.devtools.build.lib.testutil.FoundationTestCase; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 42 | import com.google.devtools.build.lib.vfs.Path; |
| 43 | import com.google.devtools.build.lib.vfs.PathFragment; |
| 44 | import com.google.devtools.build.skyframe.EvaluationResult; |
| 45 | import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator; |
| 46 | import com.google.devtools.build.skyframe.MemoizingEvaluator; |
| 47 | import com.google.devtools.build.skyframe.RecordingDifferencer; |
janakr | 1cde872 | 2017-10-10 03:22:21 +0200 | [diff] [blame] | 48 | import com.google.devtools.build.skyframe.SequencedRecordingDifferencer; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 49 | import com.google.devtools.build.skyframe.SequentialBuildDriver; |
| 50 | import com.google.devtools.build.skyframe.SkyFunction; |
| 51 | import com.google.devtools.build.skyframe.SkyFunctionName; |
| 52 | import com.google.devtools.build.skyframe.SkyKey; |
| 53 | import com.google.devtools.build.skyframe.SkyValue; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 54 | import java.util.LinkedHashMap; |
| 55 | import java.util.List; |
| 56 | import java.util.Map; |
| 57 | import java.util.UUID; |
| 58 | import java.util.concurrent.atomic.AtomicReference; |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 59 | import org.junit.Before; |
| 60 | import org.junit.Test; |
| 61 | import org.junit.runner.RunWith; |
| 62 | import org.junit.runners.JUnit4; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 63 | |
| 64 | /** Tests for {@link ActionTemplateExpansionFunction}. */ |
| 65 | @RunWith(JUnit4.class) |
| 66 | public final class ActionTemplateExpansionFunctionTest extends FoundationTestCase { |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 67 | private Map<Artifact, TreeArtifactValue> artifactValueMap; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 68 | private SequentialBuildDriver driver; |
| 69 | |
| 70 | @Before |
| 71 | public void setUp() throws Exception { |
| 72 | artifactValueMap = new LinkedHashMap<>(); |
John Cater | e0d1d0e | 2017-11-28 20:47:41 -0800 | [diff] [blame] | 73 | AtomicReference<PathPackageLocator> pkgLocator = |
| 74 | new AtomicReference<>( |
| 75 | new PathPackageLocator( |
| 76 | rootDirectory.getFileSystem().getPath("/outputbase"), |
| 77 | ImmutableList.of(rootDirectory), |
| 78 | BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY)); |
janakr | 1cde872 | 2017-10-10 03:22:21 +0200 | [diff] [blame] | 79 | RecordingDifferencer differencer = new SequencedRecordingDifferencer(); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 80 | MemoizingEvaluator evaluator = |
| 81 | new InMemoryMemoizingEvaluator( |
| 82 | ImmutableMap.<SkyFunctionName, SkyFunction>builder() |
janakr | 93e3eea | 2017-03-30 22:09:37 +0000 | [diff] [blame] | 83 | .put(SkyFunctions.ARTIFACT, new DummyArtifactFunction(artifactValueMap)) |
| 84 | .put( |
| 85 | SkyFunctions.ACTION_TEMPLATE_EXPANSION, |
| 86 | new ActionTemplateExpansionFunction(Suppliers.ofInstance(false))) |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 87 | .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) { |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 129 | return PathFragment.create("conflict_path"); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 130 | } |
| 131 | }; |
| 132 | SpawnActionTemplate spawnActionTemplate = |
| 133 | new SpawnActionTemplate.Builder(inputTreeArtifact, outputTreeArtifact) |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 134 | .setExecutable(PathFragment.create("/bin/cp")) |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 135 | .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: |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 160 | path = PathFragment.create("path_prefix"); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 161 | break; |
| 162 | case 1: |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 163 | path = PathFragment.create("path_prefix/conflict"); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 164 | 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) |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 175 | .setExecutable(PathFragment.create("/bin/cp")) |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 176 | .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 | } |
janakr | 93e3eea | 2017-03-30 22:09:37 +0000 | [diff] [blame] | 198 | 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 Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | private Artifact createTreeArtifact(String path) { |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 207 | PathFragment execPath = PathFragment.create("out").getRelative(path); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 208 | 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( |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 224 | treeArtifact, PathFragment.create(childRelativePath)); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 225 | 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 Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 238 | private final Map<Artifact, TreeArtifactValue> artifactValueMap; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 239 | |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 240 | DummyArtifactFunction(Map<Artifact, TreeArtifactValue> artifactValueMap) { |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 241 | 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 | } |