Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 1 | // Copyright 2015 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; |
Janak Ramakrishnan | a5578af | 2017-03-21 17:28:39 +0000 | [diff] [blame] | 15 | |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 16 | import static com.google.common.truth.Truth.assertThat; |
| 17 | import static com.google.devtools.build.lib.skyframe.FileArtifactValue.create; |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 18 | import static org.junit.Assert.fail; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 19 | |
Michajlo Matijkiw | 528957e | 2016-01-19 21:17:45 +0000 | [diff] [blame] | 20 | import com.google.common.base.Predicate; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 21 | import com.google.common.collect.ImmutableList; |
| 22 | import com.google.common.collect.ImmutableMap; |
| 23 | import com.google.common.collect.ImmutableSet; |
| 24 | import com.google.common.collect.Iterables; |
| 25 | import com.google.common.testing.EqualsTester; |
| 26 | import com.google.devtools.build.lib.actions.Action; |
Rumou Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 27 | import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; |
| 28 | import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 29 | import com.google.devtools.build.lib.actions.ActionInputHelper; |
janakr | 93e3eea | 2017-03-30 22:09:37 +0000 | [diff] [blame] | 30 | import com.google.devtools.build.lib.actions.ActionLookupData; |
| 31 | import com.google.devtools.build.lib.actions.ActionLookupValue; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 32 | import com.google.devtools.build.lib.actions.Artifact; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 33 | import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; |
| 34 | import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType; |
| 35 | import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 36 | import com.google.devtools.build.lib.actions.MissingInputFileException; |
| 37 | import com.google.devtools.build.lib.actions.Root; |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 38 | import com.google.devtools.build.lib.actions.util.ActionsTestUtil; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 39 | import com.google.devtools.build.lib.actions.util.TestAction.DummyAction; |
| 40 | import com.google.devtools.build.lib.events.NullEventHandler; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 41 | import com.google.devtools.build.lib.util.Pair; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 42 | import com.google.devtools.build.lib.vfs.FileStatus; |
| 43 | import com.google.devtools.build.lib.vfs.FileSystemUtils; |
| 44 | import com.google.devtools.build.lib.vfs.Path; |
| 45 | import com.google.devtools.build.lib.vfs.PathFragment; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 46 | import com.google.devtools.build.skyframe.EvaluationResult; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 47 | import com.google.devtools.build.skyframe.SkyFunction; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 48 | import com.google.devtools.build.skyframe.SkyKey; |
| 49 | import com.google.devtools.build.skyframe.SkyValue; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 50 | import java.io.IOException; |
Michajlo Matijkiw | 528957e | 2016-01-19 21:17:45 +0000 | [diff] [blame] | 51 | import java.nio.charset.StandardCharsets; |
| 52 | import java.security.MessageDigest; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 53 | import java.util.Arrays; |
| 54 | import java.util.HashMap; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 55 | import java.util.Map; |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 56 | import org.junit.Before; |
| 57 | import org.junit.Test; |
| 58 | import org.junit.runner.RunWith; |
| 59 | import org.junit.runners.JUnit4; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 60 | |
| 61 | /** |
| 62 | * Tests for {@link ArtifactFunction}. |
| 63 | */ |
| 64 | // Doesn't actually need any particular Skyframe, but is only relevant to Skyframe full mode. |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 65 | @RunWith(JUnit4.class) |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 66 | public class ArtifactFunctionTest extends ArtifactFunctionTestCase { |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 67 | |
Michajlo Matijkiw | 528957e | 2016-01-19 21:17:45 +0000 | [diff] [blame] | 68 | private PathFragment allowedMissingInput = null; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 69 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 70 | @Before |
Florian Weikert | 92b2236 | 2015-12-03 10:17:18 +0000 | [diff] [blame] | 71 | public final void setUp() throws Exception { |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 72 | delegateActionExecutionFunction = new SimpleActionExecutionFunction(); |
| 73 | allowedMissingInputsPredicate = new Predicate<PathFragment>() { |
| 74 | @Override |
| 75 | public boolean apply(PathFragment input) { |
| 76 | return input.equals(allowedMissingInput); |
| 77 | } |
| 78 | }; |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | private void assertFileArtifactValueMatches(boolean expectDigest) throws Throwable { |
| 82 | Artifact output = createDerivedArtifact("output"); |
| 83 | Path path = output.getPath(); |
| 84 | file(path, "contents"); |
| 85 | assertValueMatches(path.stat(), expectDigest ? path.getMD5Digest() : null, evaluateFAN(output)); |
| 86 | } |
| 87 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 88 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 89 | public void testBasicArtifact() throws Throwable { |
| 90 | fastDigest = false; |
| 91 | assertFileArtifactValueMatches(/*expectDigest=*/ true); |
| 92 | } |
| 93 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 94 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 95 | public void testBasicArtifactWithXattr() throws Throwable { |
| 96 | fastDigest = true; |
| 97 | assertFileArtifactValueMatches(/*expectDigest=*/ true); |
| 98 | } |
| 99 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 100 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 101 | public void testMissingNonMandatoryArtifact() throws Throwable { |
| 102 | Artifact input = createSourceArtifact("input1"); |
lberki | e355e77 | 2017-05-31 14:34:53 +0200 | [diff] [blame^] | 103 | assertThat(evaluateArtifactValue(input, /*mandatory=*/ false)).isNotNull(); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 104 | } |
| 105 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 106 | @Test |
Michajlo Matijkiw | 528957e | 2016-01-19 21:17:45 +0000 | [diff] [blame] | 107 | public void testMissingMandatoryAllowedMissingArtifact() throws Throwable { |
| 108 | Artifact input = createSourceArtifact("allowedMissing"); |
| 109 | allowedMissingInput = input.getRootRelativePath(); |
| 110 | assertThat(evaluateArtifactValue(input, /*mandatory=*/ true)) |
| 111 | .isEqualTo(FileArtifactValue.MISSING_FILE_MARKER); |
| 112 | } |
| 113 | |
| 114 | @Test |
| 115 | public void testUnreadableMandatoryAllowedMissingArtifact() throws Throwable { |
| 116 | Artifact input = createSourceArtifact("allowedMissing"); |
| 117 | file(input.getPath(), "allowedMissing"); |
| 118 | input.getPath().chmod(0); |
| 119 | |
| 120 | allowedMissingInput = input.getRootRelativePath(); |
| 121 | assertThat(evaluateArtifactValue(input, /*mandatory=*/ true)) |
| 122 | .isEqualTo(FileArtifactValue.MISSING_FILE_MARKER); |
| 123 | } |
| 124 | |
| 125 | @Test |
| 126 | public void testUnreadableInputWithFsWithAvailableDigest() throws Throwable { |
| 127 | final byte[] expectedDigest = MessageDigest.getInstance("md5").digest( |
| 128 | "someunreadablecontent".getBytes(StandardCharsets.UTF_8)); |
| 129 | setupRoot( |
| 130 | new CustomInMemoryFs() { |
| 131 | @Override |
| 132 | public byte[] getMD5Digest(Path path) throws IOException { |
| 133 | return path.getBaseName().equals("unreadable") |
| 134 | ? expectedDigest |
| 135 | : super.getMD5Digest(path); |
| 136 | } |
| 137 | }); |
| 138 | |
| 139 | Artifact input = createSourceArtifact("unreadable"); |
| 140 | Path inputPath = input.getPath(); |
| 141 | file(inputPath, "dummynotused"); |
| 142 | inputPath.chmod(0); |
| 143 | |
| 144 | FileArtifactValue value = |
| 145 | (FileArtifactValue) evaluateArtifactValue(input, /*mandatory=*/ true); |
| 146 | |
| 147 | FileStatus stat = inputPath.stat(); |
| 148 | assertThat(value.getSize()).isEqualTo(stat.getSize()); |
| 149 | assertThat(value.getDigest()).isEqualTo(expectedDigest); |
| 150 | } |
| 151 | |
| 152 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 153 | public void testMissingMandatoryArtifact() throws Throwable { |
| 154 | Artifact input = createSourceArtifact("input1"); |
| 155 | try { |
| 156 | evaluateArtifactValue(input, /*mandatory=*/ true); |
| 157 | fail(); |
| 158 | } catch (MissingInputFileException ex) { |
| 159 | // Expected. |
| 160 | } |
| 161 | } |
| 162 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 163 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 164 | public void testMiddlemanArtifact() throws Throwable { |
Janak Ramakrishnan | a5578af | 2017-03-21 17:28:39 +0000 | [diff] [blame] | 165 | Artifact output = createMiddlemanArtifact("output"); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 166 | Artifact input1 = createSourceArtifact("input1"); |
| 167 | Artifact input2 = createDerivedArtifact("input2"); |
| 168 | Action action = |
| 169 | new DummyAction( |
| 170 | ImmutableList.of(input1, input2), output, MiddlemanType.AGGREGATING_MIDDLEMAN); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 171 | actions.add(action); |
| 172 | file(input2.getPath(), "contents"); |
| 173 | file(input1.getPath(), "source contents"); |
| 174 | evaluate( |
| 175 | Iterables.toArray( |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 176 | ArtifactSkyKey.mandatoryKeys(ImmutableSet.of(input2, input1, input2)), SkyKey.class)); |
| 177 | SkyValue value = evaluateArtifactValue(output); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 178 | assertThat(((AggregatingArtifactValue) value).getInputs()) |
| 179 | .containsExactly(Pair.of(input1, create(input1)), Pair.of(input2, create(input2))); |
| 180 | } |
| 181 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 182 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 183 | public void testIOException() throws Exception { |
| 184 | fastDigest = false; |
| 185 | final IOException exception = new IOException("beep"); |
| 186 | setupRoot( |
| 187 | new CustomInMemoryFs() { |
| 188 | @Override |
| 189 | public byte[] getMD5Digest(Path path) throws IOException { |
| 190 | throw exception; |
| 191 | } |
| 192 | }); |
| 193 | Artifact artifact = createDerivedArtifact("no-read"); |
| 194 | writeFile(artifact.getPath(), "content"); |
| 195 | try { |
| 196 | create(createDerivedArtifact("no-read")); |
| 197 | fail(); |
| 198 | } catch (IOException e) { |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 199 | assertThat(e).isSameAs(exception); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 200 | } |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * Tests that ArtifactFunction rethrows transitive {@link IOException}s as |
| 205 | * {@link MissingInputFileException}s. |
| 206 | */ |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 207 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 208 | public void testIOException_EndToEnd() throws Throwable { |
| 209 | final IOException exception = new IOException("beep"); |
| 210 | setupRoot( |
| 211 | new CustomInMemoryFs() { |
| 212 | @Override |
| 213 | public FileStatus stat(Path path, boolean followSymlinks) throws IOException { |
| 214 | if (path.getBaseName().equals("bad")) { |
| 215 | throw exception; |
| 216 | } |
| 217 | return super.stat(path, followSymlinks); |
| 218 | } |
| 219 | }); |
| 220 | try { |
| 221 | evaluateArtifactValue(createSourceArtifact("bad")); |
| 222 | fail(); |
| 223 | } catch (MissingInputFileException e) { |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 224 | assertThat(e).hasMessageThat().contains(exception.getMessage()); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 225 | } |
| 226 | } |
| 227 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 228 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 229 | public void testNoMtimeIfNonemptyFile() throws Exception { |
| 230 | Artifact artifact = createDerivedArtifact("no-digest"); |
| 231 | Path path = artifact.getPath(); |
| 232 | writeFile(path, "hello"); //Non-empty file. |
| 233 | FileArtifactValue value = create(artifact); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 234 | assertThat(value.getDigest()).isEqualTo(path.getMD5Digest()); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 235 | try { |
| 236 | value.getModifiedTime(); |
| 237 | fail("mtime for non-empty file should not be stored."); |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 238 | } catch (UnsupportedOperationException e) { |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 239 | // Expected. |
| 240 | } |
| 241 | } |
| 242 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 243 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 244 | public void testDirectory() throws Exception { |
| 245 | Artifact artifact = createDerivedArtifact("dir"); |
| 246 | Path path = artifact.getPath(); |
| 247 | FileSystemUtils.createDirectoryAndParents(path); |
| 248 | path.setLastModifiedTime(1L); |
| 249 | FileArtifactValue value = create(artifact); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 250 | assertThat(value.getDigest()).isNull(); |
| 251 | assertThat(value.getModifiedTime()).isEqualTo(1L); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 252 | } |
| 253 | |
Janak Ramakrishnan | 08b0f7f | 2016-07-13 17:00:59 +0000 | [diff] [blame] | 254 | // Empty files are the same as normal files -- mtime is not stored. |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 255 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 256 | public void testEmptyFile() throws Exception { |
| 257 | Artifact artifact = createDerivedArtifact("empty"); |
| 258 | Path path = artifact.getPath(); |
| 259 | writeFile(path, ""); |
| 260 | path.setLastModifiedTime(1L); |
| 261 | FileArtifactValue value = create(artifact); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 262 | assertThat(value.getDigest()).isEqualTo(path.getMD5Digest()); |
| 263 | assertThat(value.getSize()).isEqualTo(0L); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 264 | } |
| 265 | |
Han-Wen Nienhuys | 3b2eae3 | 2015-10-28 16:35:08 +0000 | [diff] [blame] | 266 | @Test |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 267 | public void testEquality() throws Exception { |
| 268 | Artifact artifact1 = createDerivedArtifact("artifact1"); |
| 269 | Artifact artifact2 = createDerivedArtifact("artifact2"); |
| 270 | Artifact diffDigest = createDerivedArtifact("diffDigest"); |
| 271 | Artifact diffMtime = createDerivedArtifact("diffMtime"); |
| 272 | Artifact empty1 = createDerivedArtifact("empty1"); |
| 273 | Artifact empty2 = createDerivedArtifact("empty2"); |
| 274 | Artifact empty3 = createDerivedArtifact("empty3"); |
| 275 | Artifact dir1 = createDerivedArtifact("dir1"); |
| 276 | Artifact dir2 = createDerivedArtifact("dir2"); |
| 277 | Artifact dir3 = createDerivedArtifact("dir3"); |
| 278 | Path path1 = artifact1.getPath(); |
| 279 | Path path2 = artifact2.getPath(); |
| 280 | Path digestPath = diffDigest.getPath(); |
| 281 | Path mtimePath = diffMtime.getPath(); |
| 282 | writeFile(artifact1.getPath(), "content"); |
| 283 | writeFile(artifact2.getPath(), "content"); |
| 284 | path1.setLastModifiedTime(0); |
| 285 | path2.setLastModifiedTime(0); |
| 286 | writeFile(diffDigest.getPath(), "1234567"); // Same size as artifact1. |
| 287 | digestPath.setLastModifiedTime(0); |
| 288 | writeFile(mtimePath, "content"); |
| 289 | mtimePath.setLastModifiedTime(1); |
| 290 | Path emptyPath1 = empty1.getPath(); |
| 291 | Path emptyPath2 = empty2.getPath(); |
| 292 | Path emptyPath3 = empty3.getPath(); |
| 293 | writeFile(emptyPath1, ""); |
| 294 | writeFile(emptyPath2, ""); |
| 295 | writeFile(emptyPath3, ""); |
| 296 | emptyPath1.setLastModifiedTime(0L); |
| 297 | emptyPath2.setLastModifiedTime(1L); |
| 298 | emptyPath3.setLastModifiedTime(1L); |
| 299 | Path dirPath1 = dir1.getPath(); |
| 300 | Path dirPath2 = dir2.getPath(); |
| 301 | Path dirPath3 = dir3.getPath(); |
| 302 | FileSystemUtils.createDirectoryAndParents(dirPath1); |
| 303 | FileSystemUtils.createDirectoryAndParents(dirPath2); |
| 304 | FileSystemUtils.createDirectoryAndParents(dirPath3); |
| 305 | dirPath1.setLastModifiedTime(0L); |
| 306 | dirPath2.setLastModifiedTime(1L); |
| 307 | dirPath3.setLastModifiedTime(1L); |
| 308 | EqualsTester equalsTester = new EqualsTester(); |
| 309 | equalsTester |
| 310 | .addEqualityGroup(create(artifact1), create(artifact2), create(diffMtime)) |
Janak Ramakrishnan | 08b0f7f | 2016-07-13 17:00:59 +0000 | [diff] [blame] | 311 | .addEqualityGroup(create(empty1), create(empty2), create(empty3)) |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 312 | .addEqualityGroup(create(dir1)) |
| 313 | .addEqualityGroup(create(dir2), create(dir3)) |
| 314 | .testEquals(); |
| 315 | } |
| 316 | |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 317 | @Test |
| 318 | public void testActionTreeArtifactOutput() throws Throwable { |
| 319 | Artifact artifact = createDerivedTreeArtifactWithAction("treeArtifact"); |
| 320 | TreeFileArtifact treeFileArtifact1 = createFakeTreeFileArtifact(artifact, "child1", "hello1"); |
| 321 | TreeFileArtifact treeFileArtifact2 = createFakeTreeFileArtifact(artifact, "child2", "hello2"); |
| 322 | |
| 323 | TreeArtifactValue value = (TreeArtifactValue) evaluateArtifactValue(artifact); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 324 | assertThat(value.getChildValues().get(treeFileArtifact1)).isNotNull(); |
| 325 | assertThat(value.getChildValues().get(treeFileArtifact2)).isNotNull(); |
| 326 | assertThat(value.getChildValues().get(treeFileArtifact1).getDigest()).isNotNull(); |
| 327 | assertThat(value.getChildValues().get(treeFileArtifact2).getDigest()).isNotNull(); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 328 | } |
| 329 | |
| 330 | @Test |
| 331 | public void testSpawnActionTemplate() throws Throwable { |
| 332 | // artifact1 is a tree artifact generated by normal action. |
| 333 | Artifact artifact1 = createDerivedTreeArtifactWithAction("treeArtifact1"); |
| 334 | createFakeTreeFileArtifact(artifact1, "child1", "hello1"); |
| 335 | createFakeTreeFileArtifact(artifact1, "child2", "hello2"); |
| 336 | |
| 337 | |
| 338 | // artifact2 is a tree artifact generated by action template. |
| 339 | Artifact artifact2 = createDerivedTreeArtifactOnly("treeArtifact2"); |
| 340 | TreeFileArtifact treeFileArtifact1 = createFakeTreeFileArtifact(artifact2, "child1", "hello1"); |
| 341 | TreeFileArtifact treeFileArtifact2 = createFakeTreeFileArtifact(artifact2, "child2", "hello2"); |
| 342 | |
| 343 | actions.add( |
| 344 | ActionsTestUtil.createDummySpawnActionTemplate(artifact1, artifact2)); |
| 345 | |
| 346 | TreeArtifactValue value = (TreeArtifactValue) evaluateArtifactValue(artifact2); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 347 | assertThat(value.getChildValues().get(treeFileArtifact1)).isNotNull(); |
| 348 | assertThat(value.getChildValues().get(treeFileArtifact2)).isNotNull(); |
| 349 | assertThat(value.getChildValues().get(treeFileArtifact1).getDigest()).isNotNull(); |
| 350 | assertThat(value.getChildValues().get(treeFileArtifact2).getDigest()).isNotNull(); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 351 | } |
| 352 | |
| 353 | @Test |
| 354 | public void testConsecutiveSpawnActionTemplates() throws Throwable { |
| 355 | // artifact1 is a tree artifact generated by normal action. |
| 356 | Artifact artifact1 = createDerivedTreeArtifactWithAction("treeArtifact1"); |
| 357 | createFakeTreeFileArtifact(artifact1, "child1", "hello1"); |
| 358 | createFakeTreeFileArtifact(artifact1, "child2", "hello2"); |
| 359 | |
| 360 | // artifact2 is a tree artifact generated by action template. |
| 361 | Artifact artifact2 = createDerivedTreeArtifactOnly("treeArtifact2"); |
| 362 | createFakeTreeFileArtifact(artifact2, "child1", "hello1"); |
| 363 | createFakeTreeFileArtifact(artifact2, "child2", "hello2"); |
| 364 | actions.add( |
| 365 | ActionsTestUtil.createDummySpawnActionTemplate(artifact1, artifact2)); |
| 366 | |
| 367 | // artifact3 is a tree artifact generated by action template. |
| 368 | Artifact artifact3 = createDerivedTreeArtifactOnly("treeArtifact3"); |
| 369 | TreeFileArtifact treeFileArtifact1 = createFakeTreeFileArtifact(artifact3, "child1", "hello1"); |
| 370 | TreeFileArtifact treeFileArtifact2 = createFakeTreeFileArtifact(artifact3, "child2", "hello2"); |
| 371 | actions.add( |
| 372 | ActionsTestUtil.createDummySpawnActionTemplate(artifact2, artifact3)); |
| 373 | |
| 374 | TreeArtifactValue value = (TreeArtifactValue) evaluateArtifactValue(artifact3); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 375 | assertThat(value.getChildValues().get(treeFileArtifact1)).isNotNull(); |
| 376 | assertThat(value.getChildValues().get(treeFileArtifact2)).isNotNull(); |
| 377 | assertThat(value.getChildValues().get(treeFileArtifact1).getDigest()).isNotNull(); |
| 378 | assertThat(value.getChildValues().get(treeFileArtifact2).getDigest()).isNotNull(); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 379 | } |
| 380 | |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 381 | private void file(Path path, String contents) throws Exception { |
| 382 | FileSystemUtils.createDirectoryAndParents(path.getParentDirectory()); |
| 383 | writeFile(path, contents); |
| 384 | } |
| 385 | |
| 386 | private Artifact createSourceArtifact(String path) { |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 387 | return new Artifact(PathFragment.create(path), Root.asSourceRoot(root)); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 388 | } |
| 389 | |
| 390 | private Artifact createDerivedArtifact(String path) { |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 391 | PathFragment execPath = PathFragment.create("out").getRelative(path); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 392 | Path fullPath = root.getRelative(execPath); |
| 393 | Artifact output = |
| 394 | new Artifact( |
| 395 | fullPath, Root.asDerivedRoot(root, root.getRelative("out")), execPath, ALL_OWNER); |
| 396 | actions.add(new DummyAction(ImmutableList.<Artifact>of(), output)); |
| 397 | return output; |
| 398 | } |
| 399 | |
Janak Ramakrishnan | a5578af | 2017-03-21 17:28:39 +0000 | [diff] [blame] | 400 | private Artifact createMiddlemanArtifact(String path) { |
| 401 | Root middlemanRoot = Root.middlemanRoot(middlemanPath, middlemanPath.getRelative("out")); |
| 402 | Path fullPath = middlemanRoot.getPath().getRelative(path); |
| 403 | return new Artifact( |
| 404 | fullPath, middlemanRoot, fullPath.relativeTo(middlemanRoot.getExecRoot()), ALL_OWNER); |
| 405 | } |
| 406 | |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 407 | private Artifact createDerivedTreeArtifactWithAction(String path) { |
| 408 | Artifact treeArtifact = createDerivedTreeArtifactOnly(path); |
| 409 | actions.add(new DummyAction(ImmutableList.<Artifact>of(), treeArtifact)); |
| 410 | return treeArtifact; |
| 411 | } |
| 412 | |
| 413 | private Artifact createDerivedTreeArtifactOnly(String path) { |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 414 | PathFragment execPath = PathFragment.create("out").getRelative(path); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 415 | Path fullPath = root.getRelative(execPath); |
| 416 | return new SpecialArtifact( |
| 417 | fullPath, |
| 418 | Root.asDerivedRoot(root, root.getRelative("out")), |
| 419 | execPath, |
| 420 | ALL_OWNER, |
| 421 | SpecialArtifactType.TREE); |
| 422 | } |
| 423 | |
| 424 | private TreeFileArtifact createFakeTreeFileArtifact(Artifact treeArtifact, |
| 425 | String parentRelativePath, String content) throws Exception { |
| 426 | TreeFileArtifact treeFileArtifact = ActionInputHelper.treeFileArtifact( |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 427 | treeArtifact, PathFragment.create(parentRelativePath)); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 428 | Path path = treeFileArtifact.getPath(); |
| 429 | FileSystemUtils.createDirectoryAndParents(path.getParentDirectory()); |
| 430 | writeFile(path, content); |
| 431 | return treeFileArtifact; |
| 432 | } |
| 433 | |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 434 | private void assertValueMatches(FileStatus file, byte[] digest, FileArtifactValue value) |
| 435 | throws IOException { |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 436 | assertThat(value.getSize()).isEqualTo(file.getSize()); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 437 | if (digest == null) { |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 438 | assertThat(value.getDigest()).isNull(); |
| 439 | assertThat(value.getModifiedTime()).isEqualTo(file.getLastModifiedTime()); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 440 | } else { |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 441 | assertThat(value.getDigest()).isEqualTo(digest); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 442 | } |
| 443 | } |
| 444 | |
| 445 | private FileArtifactValue evaluateFAN(Artifact artifact) throws Throwable { |
| 446 | return ((FileArtifactValue) evaluateArtifactValue(artifact)); |
| 447 | } |
| 448 | |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 449 | private SkyValue evaluateArtifactValue(Artifact artifact) throws Throwable { |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 450 | return evaluateArtifactValue(artifact, /*isMandatory=*/ true); |
| 451 | } |
| 452 | |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 453 | private SkyValue evaluateArtifactValue(Artifact artifact, boolean mandatory) throws Throwable { |
| 454 | SkyKey key = ArtifactSkyKey.key(artifact, mandatory); |
| 455 | EvaluationResult<SkyValue> result = evaluate(ImmutableList.of(key).toArray(new SkyKey[0])); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 456 | if (result.hasError()) { |
| 457 | throw result.getError().getException(); |
| 458 | } |
| 459 | return result.get(key); |
| 460 | } |
| 461 | |
| 462 | private void setGeneratingActions() { |
| 463 | if (evaluator.getExistingValueForTesting(OWNER_KEY) == null) { |
janakr | 93e3eea | 2017-03-30 22:09:37 +0000 | [diff] [blame] | 464 | differencer.inject( |
| 465 | ImmutableMap.of( |
| 466 | OWNER_KEY, |
| 467 | new ActionLookupValue(ImmutableList.<ActionAnalysisMetadata>copyOf(actions), false))); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 468 | } |
| 469 | } |
| 470 | |
| 471 | private <E extends SkyValue> EvaluationResult<E> evaluate(SkyKey... keys) |
| 472 | throws InterruptedException { |
| 473 | setGeneratingActions(); |
| 474 | return driver.evaluate( |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 475 | Arrays.asList(keys), |
| 476 | /*keepGoing=*/false, |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 477 | SkyframeExecutor.DEFAULT_THREAD_COUNT, |
| 478 | NullEventHandler.INSTANCE); |
| 479 | } |
| 480 | |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 481 | /** Value Builder for actions that just stats and stores the output file (which must exist). */ |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 482 | private static class SimpleActionExecutionFunction implements SkyFunction { |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 483 | @Override |
janakr | 93e3eea | 2017-03-30 22:09:37 +0000 | [diff] [blame] | 484 | public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException { |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 485 | Map<Artifact, FileValue> artifactData = new HashMap<>(); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 486 | Map<Artifact, TreeArtifactValue> treeArtifactData = new HashMap<>(); |
| 487 | Map<Artifact, FileArtifactValue> additionalOutputData = new HashMap<>(); |
janakr | 93e3eea | 2017-03-30 22:09:37 +0000 | [diff] [blame] | 488 | ActionLookupData actionLookupData = (ActionLookupData) skyKey.argument(); |
| 489 | ActionLookupValue actionLookupValue = |
| 490 | (ActionLookupValue) env.getValue(actionLookupData.getActionLookupNode()); |
| 491 | Action action = actionLookupValue.getAction(actionLookupData.getActionIndex()); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 492 | Artifact output = Iterables.getOnlyElement(action.getOutputs()); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 493 | |
| 494 | try { |
| 495 | if (output.isTreeArtifact()) { |
| 496 | TreeFileArtifact treeFileArtifact1 = ActionInputHelper.treeFileArtifact( |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 497 | output, PathFragment.create("child1")); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 498 | TreeFileArtifact treeFileArtifact2 = ActionInputHelper.treeFileArtifact( |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 499 | output, PathFragment.create("child2")); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 500 | TreeArtifactValue treeArtifactValue = TreeArtifactValue.create(ImmutableMap.of( |
| 501 | treeFileArtifact1, FileArtifactValue.create(treeFileArtifact1), |
| 502 | treeFileArtifact2, FileArtifactValue.create(treeFileArtifact2))); |
| 503 | treeArtifactData.put(output, treeArtifactValue); |
| 504 | } else if (action.getActionType() == MiddlemanType.NORMAL) { |
Rumou Duan | a77f32c | 2016-04-13 21:59:21 +0000 | [diff] [blame] | 505 | FileValue fileValue = ActionMetadataHandler.fileValueFromArtifact(output, null, null); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 506 | artifactData.put(output, fileValue); |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 507 | additionalOutputData.put(output, FileArtifactValue.create(output, fileValue)); |
| 508 | } else { |
| 509 | additionalOutputData.put(output, FileArtifactValue.DEFAULT_MIDDLEMAN); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 510 | } |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 511 | } catch (IOException e) { |
| 512 | throw new IllegalStateException(e); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 513 | } |
Michael Thvedt | 8d5a7bb | 2016-02-09 03:06:34 +0000 | [diff] [blame] | 514 | return new ActionExecutionValue( |
| 515 | artifactData, |
Rumou Duan | 7387620 | 2016-06-06 18:52:08 +0000 | [diff] [blame] | 516 | treeArtifactData, |
| 517 | additionalOutputData); |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 518 | } |
| 519 | |
| 520 | @Override |
| 521 | public String extractTag(SkyKey skyKey) { |
| 522 | return null; |
| 523 | } |
| 524 | } |
Han-Wen Nienhuys | 81b9083 | 2015-10-26 16:57:27 +0000 | [diff] [blame] | 525 | } |