blob: f0112d4c9f3ec084548208cc58f6ef4536bb259e [file] [log] [blame]
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001// 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.
14package com.google.devtools.build.lib.skyframe;
15
16import static com.google.common.truth.Truth.assertThat;
lberkiaea56b32017-05-30 12:35:33 +020017import static com.google.common.truth.Truth.assertWithMessage;
Rumou Duana77f32c2016-04-13 21:59:21 +000018import static com.google.devtools.build.lib.actions.ActionInputHelper.treeFileArtifact;
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +000019import static org.junit.Assert.fail;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000020
21import com.google.common.collect.ImmutableList;
22import com.google.common.collect.ImmutableMap;
23import com.google.common.collect.ImmutableSet;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000024import com.google.common.util.concurrent.Runnables;
25import com.google.devtools.build.lib.actions.Action;
janakr93e3eea2017-03-30 22:09:37 +000026import com.google.devtools.build.lib.actions.ActionLookupValue.ActionLookupKey;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000027import com.google.devtools.build.lib.actions.Artifact;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000028import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
29import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType;
Rumou Duana77f32c2016-04-13 21:59:21 +000030import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000031import com.google.devtools.build.lib.actions.ArtifactOwner;
tomlu1cdcdf92018-01-16 11:07:51 -080032import com.google.devtools.build.lib.actions.ArtifactRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000033import com.google.devtools.build.lib.actions.util.TestAction;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000034import com.google.devtools.build.lib.analysis.BlazeDirectories;
janakr3b63a4e2017-09-14 09:55:40 +020035import com.google.devtools.build.lib.analysis.ServerDirectories;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000036import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000037import com.google.devtools.build.lib.events.NullEventHandler;
38import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
39import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.BasicFilesystemDirtinessChecker;
Nathan Harmatad4f75942016-10-18 08:55:17 +000040import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
John Cater5e9ce942016-10-12 17:23:30 +000041import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +000042import com.google.devtools.build.lib.testutil.TestConstants;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000043import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
laszlocsomor29bdf632018-03-13 02:50:46 -070044import com.google.devtools.build.lib.testutil.TimestampGranularityUtils;
Laszlo Csomora278aec2018-03-09 04:07:31 -080045import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000046import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
47import com.google.devtools.build.lib.vfs.BatchStat;
48import com.google.devtools.build.lib.vfs.FileStatus;
49import com.google.devtools.build.lib.vfs.FileStatusWithDigest;
50import com.google.devtools.build.lib.vfs.FileStatusWithDigestAdapter;
51import com.google.devtools.build.lib.vfs.FileSystemUtils;
Eric Fellheimere6590722015-11-17 17:07:48 +000052import com.google.devtools.build.lib.vfs.ModifiedFileSet;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000053import com.google.devtools.build.lib.vfs.Path;
54import com.google.devtools.build.lib.vfs.PathFragment;
tomluee6a6862018-01-17 14:36:26 -080055import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000056import com.google.devtools.build.lib.vfs.RootedPath;
57import com.google.devtools.build.lib.vfs.Symlinks;
58import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
59import com.google.devtools.build.skyframe.Differencer.Diff;
60import com.google.devtools.build.skyframe.EvaluationResult;
61import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
62import com.google.devtools.build.skyframe.MemoizingEvaluator;
63import com.google.devtools.build.skyframe.RecordingDifferencer;
janakr1cde8722017-10-10 03:22:21 +020064import com.google.devtools.build.skyframe.SequencedRecordingDifferencer;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000065import com.google.devtools.build.skyframe.SequentialBuildDriver;
66import com.google.devtools.build.skyframe.SkyFunction;
67import com.google.devtools.build.skyframe.SkyFunctionName;
68import com.google.devtools.build.skyframe.SkyKey;
69import com.google.devtools.build.skyframe.SkyValue;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000070import java.io.IOException;
71import java.util.ArrayList;
72import java.util.Arrays;
73import java.util.Collection;
74import java.util.HashMap;
75import java.util.List;
76import java.util.Map;
77import java.util.UUID;
78import java.util.concurrent.atomic.AtomicReference;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000079import javax.annotation.Nullable;
Janak Ramakrishnancba16452016-07-29 02:17:02 +000080import org.junit.Before;
81import org.junit.Test;
82import org.junit.runner.RunWith;
83import org.junit.runners.JUnit4;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000084
85/**
86 * Tests for {@link FilesystemValueChecker}.
87 */
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +000088@RunWith(JUnit4.class)
89public class FilesystemValueCheckerTest {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000090
91 private RecordingDifferencer differencer;
92 private MemoizingEvaluator evaluator;
93 private SequentialBuildDriver driver;
94 private MockFileSystem fs;
95 private Path pkgRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000096
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +000097 @Before
Florian Weikert92b22362015-12-03 10:17:18 +000098 public final void setUp() throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000099 ImmutableMap.Builder<SkyFunctionName, SkyFunction> skyFunctions = ImmutableMap.builder();
100
101 fs = new MockFileSystem();
102 pkgRoot = fs.getPath("/testroot");
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000103 FileSystemUtils.createDirectoryAndParents(pkgRoot);
104 FileSystemUtils.createEmptyFile(pkgRoot.getRelative("WORKSPACE"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000105
John Catere0d1d0e2017-11-28 20:47:41 -0800106 AtomicReference<PathPackageLocator> pkgLocator =
107 new AtomicReference<>(
108 new PathPackageLocator(
109 fs.getPath("/output_base"),
tomluee6a6862018-01-17 14:36:26 -0800110 ImmutableList.of(Root.fromPath(pkgRoot)),
John Catere0d1d0e2017-11-28 20:47:41 -0800111 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
janakr3b63a4e2017-09-14 09:55:40 +0200112 BlazeDirectories directories =
113 new BlazeDirectories(
cushon849df362018-05-14 01:51:45 -0700114 new ServerDirectories(pkgRoot, pkgRoot, pkgRoot),
115 pkgRoot,
116 /* defaultSystemJavabase= */ null,
117 TestConstants.PRODUCT_NAME);
nharmata3fb7d342018-02-23 11:37:51 -0800118 ExternalFilesHelper externalFilesHelper = ExternalFilesHelper.createForTesting(
Nathan Harmatad4f75942016-10-18 08:55:17 +0000119 pkgLocator, ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS, directories);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000120 skyFunctions.put(SkyFunctions.FILE_STATE, new FileStateFunction(
121 new AtomicReference<TimestampGranularityMonitor>(), externalFilesHelper));
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000122 skyFunctions.put(SkyFunctions.FILE, new FileFunction(pkgLocator));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000123 skyFunctions.put(
124 SkyFunctions.FILE_SYMLINK_CYCLE_UNIQUENESS, new FileSymlinkCycleUniquenessFunction());
125 skyFunctions.put(
126 SkyFunctions.FILE_SYMLINK_INFINITE_EXPANSION_UNIQUENESS,
127 new FileSymlinkInfiniteExpansionUniquenessFunction());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000128 skyFunctions.put(SkyFunctions.PACKAGE,
129 new PackageFunction(null, null, null, null, null, null, null));
John Cater5e9ce942016-10-12 17:23:30 +0000130 skyFunctions.put(
131 SkyFunctions.PACKAGE_LOOKUP,
132 new PackageLookupFunction(
133 new AtomicReference<>(ImmutableSet.<PackageIdentifier>of()),
John Cater0c0735a2016-11-11 01:52:02 +0000134 CrossRepositoryLabelViolationStrategy.ERROR,
John Catere0d1d0e2017-11-28 20:47:41 -0800135 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
Damien Martin-Guillerez4ecfe512016-01-22 11:03:23 +0000136 skyFunctions.put(SkyFunctions.WORKSPACE_AST,
137 new WorkspaceASTFunction(TestRuleClassProvider.getRuleClassProvider()));
carmid6a98282018-03-13 19:19:16 -0700138 skyFunctions.put(
139 SkyFunctions.WORKSPACE_FILE,
140 new WorkspaceFileFunction(
141 TestRuleClassProvider.getRuleClassProvider(),
142 TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING
143 .builder(directories)
nharmatae57e9a32018-04-02 15:10:24 -0700144 .build(TestRuleClassProvider.getRuleClassProvider()),
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000145 directories));
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000146 skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000147
janakr1cde8722017-10-10 03:22:21 +0200148 differencer = new SequencedRecordingDifferencer();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000149 evaluator = new InMemoryMemoizingEvaluator(skyFunctions.build(), differencer);
150 driver = new SequentialBuildDriver(evaluator);
151 PrecomputedValue.BUILD_ID.set(differencer, UUID.randomUUID());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000152 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000153 }
154
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000155 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000156 public void testEmpty() throws Exception {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000157 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000158 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000159 }
160
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000161 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000162 public void testSimple() throws Exception {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000163 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000164
165 Path path = fs.getPath("/foo");
166 FileSystemUtils.createEmptyFile(path);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000167 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000168
tomluee6a6862018-01-17 14:36:26 -0800169 SkyKey skyKey =
170 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800171 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000172 EvaluationResult<SkyValue> result =
173 driver.evaluate(
174 ImmutableList.of(skyKey),
175 false,
176 SkyframeExecutor.DEFAULT_THREAD_COUNT,
177 NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200178 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000179
Nathan Harmata8cd29782015-11-10 03:24:01 +0000180 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000181
182 FileSystemUtils.writeContentAsLatin1(path, "hello");
Nathan Harmata8cd29782015-11-10 03:24:01 +0000183 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000184
185 // The dirty bits are not reset until the FileValues are actually revalidated.
Nathan Harmata8cd29782015-11-10 03:24:01 +0000186 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000187
188 differencer.invalidate(ImmutableList.of(skyKey));
189 result =
190 driver.evaluate(
191 ImmutableList.of(skyKey),
192 false,
193 SkyframeExecutor.DEFAULT_THREAD_COUNT,
194 NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200195 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000196 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000197 }
198
199 /**
200 * Tests that an already-invalidated value can still be marked changed: symlink points at sym1.
201 * Invalidate symlink by changing sym1 from pointing at path to point to sym2. This only dirties
202 * (rather than changes) symlink because sym2 still points at path, so all symlink stats remain
203 * the same. Then do a null build, change sym1 back to point at path, and change symlink to not be
204 * a symlink anymore. The fact that it is not a symlink should be detected.
205 */
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000206 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000207 public void testDirtySymlink() throws Exception {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000208 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000209
210 Path path = fs.getPath("/foo");
211 FileSystemUtils.writeContentAsLatin1(path, "foo contents");
212 // We need the intermediate sym1 and sym2 so that we can dirty a child of symlink without
213 // actually changing the FileValue calculated for symlink (if we changed the contents of foo,
Laszlo Csomora278aec2018-03-09 04:07:31 -0800214 // the FileValue created for symlink would notice, since it stats foo).
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000215 Path sym1 = fs.getPath("/sym1");
216 Path sym2 = fs.getPath("/sym2");
217 Path symlink = fs.getPath("/bar");
218 FileSystemUtils.ensureSymbolicLink(symlink, sym1);
219 FileSystemUtils.ensureSymbolicLink(sym1, path);
220 FileSystemUtils.ensureSymbolicLink(sym2, path);
221 SkyKey fooKey =
tomlu4c9fafd2018-01-18 10:29:11 -0800222 FileValue.key(RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000223 RootedPath symlinkRootedPath =
tomlu4c9fafd2018-01-18 10:29:11 -0800224 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/bar"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000225 SkyKey symlinkKey = FileValue.key(symlinkRootedPath);
226 SkyKey symlinkFileStateKey = FileStateValue.key(symlinkRootedPath);
227 RootedPath sym1RootedPath =
tomlu4c9fafd2018-01-18 10:29:11 -0800228 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/sym1"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000229 SkyKey sym1FileStateKey = FileStateValue.key(sym1RootedPath);
230 Iterable<SkyKey> allKeys = ImmutableList.of(symlinkKey, fooKey);
231
232 // First build -- prime the graph.
233 EvaluationResult<FileValue> result =
234 driver.evaluate(
235 allKeys, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200236 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000237 FileValue symlinkValue = result.get(symlinkKey);
238 FileValue fooValue = result.get(fooKey);
lberkiaea56b32017-05-30 12:35:33 +0200239 assertWithMessage(symlinkValue.toString()).that(symlinkValue.isSymlink()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000240 // Digest is not always available, so use size as a proxy for contents.
lberkiaea56b32017-05-30 12:35:33 +0200241 assertThat(symlinkValue.getSize()).isEqualTo(fooValue.getSize());
Nathan Harmata8cd29782015-11-10 03:24:01 +0000242 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000243
244 // Before second build, move sym1 to point to sym2.
lberkiaea56b32017-05-30 12:35:33 +0200245 assertThat(sym1.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000246 FileSystemUtils.ensureSymbolicLink(sym1, sym2);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000247 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000248
249 differencer.invalidate(ImmutableList.of(sym1FileStateKey));
250 result =
251 driver.evaluate(
252 ImmutableList.<SkyKey>of(),
253 false,
254 SkyframeExecutor.DEFAULT_THREAD_COUNT,
255 NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200256 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000257 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000258
259 // Before third build, move sym1 back to original (so change pruning will prevent signaling of
260 // its parents, but change symlink for real.
lberkiaea56b32017-05-30 12:35:33 +0200261 assertThat(sym1.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000262 FileSystemUtils.ensureSymbolicLink(sym1, path);
lberkiaea56b32017-05-30 12:35:33 +0200263 assertThat(symlink.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000264 FileSystemUtils.writeContentAsLatin1(symlink, "new symlink contents");
Nathan Harmata8cd29782015-11-10 03:24:01 +0000265 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), symlinkFileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000266 differencer.invalidate(ImmutableList.of(symlinkFileStateKey));
267 result =
268 driver.evaluate(
269 allKeys, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200270 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000271 symlinkValue = result.get(symlinkKey);
lberkiaea56b32017-05-30 12:35:33 +0200272 assertWithMessage(symlinkValue.toString()).that(symlinkValue.isSymlink()).isFalse();
273 assertThat(result.get(fooKey)).isEqualTo(fooValue);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000274 assertThat(symlinkValue.getSize()).isNotEqualTo(fooValue.getSize());
Nathan Harmata8cd29782015-11-10 03:24:01 +0000275 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000276 }
277
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000278 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000279 public void testExplicitFiles() throws Exception {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000280 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000281
282 Path path1 = fs.getPath("/foo1");
283 Path path2 = fs.getPath("/foo2");
284 FileSystemUtils.createEmptyFile(path1);
285 FileSystemUtils.createEmptyFile(path2);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000286 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000287
288 SkyKey key1 =
289 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800290 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo1")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000291 SkyKey key2 =
292 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800293 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo2")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000294 Iterable<SkyKey> skyKeys = ImmutableList.of(key1, key2);
295 EvaluationResult<SkyValue> result =
296 driver.evaluate(
297 skyKeys, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200298 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000299
Nathan Harmata8cd29782015-11-10 03:24:01 +0000300 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000301
Laszlo Csomora278aec2018-03-09 04:07:31 -0800302 // Wait for the timestamp granularity to elapse, so updating the files will observably advance
303 // their ctime.
laszlocsomor29bdf632018-03-13 02:50:46 -0700304 TimestampGranularityUtils.waitForTimestampGranularity(
305 System.currentTimeMillis(), OutErr.SYSTEM_OUT_ERR);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800306 // Update path1's contents and mtime. This will update the file's ctime.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000307 FileSystemUtils.writeContentAsLatin1(path1, "hello1");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000308 path1.setLastModifiedTime(27);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800309 // Update path2's mtime but not its contents. We expect that an mtime change suffices to update
310 // the ctime.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000311 path2.setLastModifiedTime(42);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800312 // Assert that both files changed. The change detection relies, among other things, on ctime
313 // change.
Nathan Harmata8cd29782015-11-10 03:24:01 +0000314 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), key1, key2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000315
316 differencer.invalidate(skyKeys);
317 result =
318 driver.evaluate(
319 skyKeys, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200320 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000321 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000322 }
323
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000324 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000325 public void testFileWithIOExceptionNotConsideredDirty() throws Exception {
326 Path path = fs.getPath("/testroot/foo");
327 path.getParentDirectory().createDirectory();
nharmatab4060b62017-04-04 17:11:39 +0000328 path.createSymbolicLink(PathFragment.create("bar"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000329
330 fs.readlinkThrowsIoException = true;
tomluee6a6862018-01-17 14:36:26 -0800331 SkyKey fileKey =
332 FileStateValue.key(
333 RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000334 EvaluationResult<SkyValue> result =
335 driver.evaluate(
336 ImmutableList.of(fileKey),
337 false,
338 SkyframeExecutor.DEFAULT_THREAD_COUNT,
339 NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200340 assertThat(result.hasError()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000341
342 fs.readlinkThrowsIoException = false;
Ulf Adamsc73051c62016-03-23 09:18:13 +0000343 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000344 Diff diff = getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000345 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
346 assertThat(diff.changedKeysWithNewValues()).isEmpty();
347 }
348
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000349 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000350 public void testFilesInCycleNotConsideredDirty() throws Exception {
351 Path path1 = pkgRoot.getRelative("foo1");
352 Path path2 = pkgRoot.getRelative("foo2");
353 Path path3 = pkgRoot.getRelative("foo3");
354 FileSystemUtils.ensureSymbolicLink(path1, path2);
355 FileSystemUtils.ensureSymbolicLink(path2, path3);
356 FileSystemUtils.ensureSymbolicLink(path3, path1);
tomluee6a6862018-01-17 14:36:26 -0800357 SkyKey fileKey1 = FileValue.key(RootedPath.toRootedPath(Root.fromPath(pkgRoot), path1));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000358
359 EvaluationResult<SkyValue> result =
360 driver.evaluate(
361 ImmutableList.of(fileKey1),
362 false,
363 SkyframeExecutor.DEFAULT_THREAD_COUNT,
364 NullEventHandler.INSTANCE);
lberkiaea56b32017-05-30 12:35:33 +0200365 assertThat(result.hasError()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000366
Ulf Adamsc73051c62016-03-23 09:18:13 +0000367 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000368 Diff diff = getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000369 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
370 assertThat(diff.changedKeysWithNewValues()).isEmpty();
371 }
372
373 public void checkDirtyActions(BatchStat batchStatter, boolean forceDigests) throws Exception {
374 Artifact out1 = createDerivedArtifact("fiz");
375 Artifact out2 = createDerivedArtifact("pop");
376
377 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "hello");
378 FileSystemUtils.writeContentAsLatin1(out2.getPath(), "fizzlepop");
379
janakrbaf52ae2018-02-14 09:03:18 -0800380 ActionLookupKey actionLookupKey =
janakr573807d2018-01-11 14:02:35 -0800381 new ActionLookupKey() {
382 @Override
383 public SkyFunctionName functionName() {
384 return SkyFunctionName.FOR_TESTING;
385 }
386 };
janakr93e3eea2017-03-30 22:09:37 +0000387 SkyKey actionKey1 = ActionExecutionValue.key(actionLookupKey, 0);
388 SkyKey actionKey2 = ActionExecutionValue.key(actionLookupKey, 1);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000389 differencer.inject(
390 ImmutableMap.<SkyKey, SkyValue>of(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000391 actionKey1,
392 actionValue(
393 new TestAction(
394 Runnables.doNothing(), ImmutableSet.<Artifact>of(), ImmutableSet.of(out1)),
395 forceDigests),
396 actionKey2,
397 actionValue(
398 new TestAction(
399 Runnables.doNothing(), ImmutableSet.<Artifact>of(), ImmutableSet.of(out2)),
400 forceDigests)));
lberkiaea56b32017-05-30 12:35:33 +0200401 assertThat(
402 driver
403 .evaluate(ImmutableList.<SkyKey>of(), false, 1, NullEventHandler.INSTANCE)
404 .hasError())
405 .isFalse();
Ulf Adamsc73051c62016-03-23 09:18:13 +0000406 assertThat(new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Eric Fellheimere6590722015-11-17 17:07:48 +0000407 batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED)).isEmpty();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000408
409 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "goodbye");
Eric Fellheimere6590722015-11-17 17:07:48 +0000410 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000411 new FilesystemValueChecker(null, null)
412 .getDirtyActionValues(
413 evaluator.getValues(), batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED))
414 .containsExactly(actionKey1);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000415 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000416 new FilesystemValueChecker(null, null)
417 .getDirtyActionValues(
418 evaluator.getValues(),
419 batchStatter,
420 new ModifiedFileSet.Builder().modify(out1.getExecPath()).build()))
421 .containsExactly(actionKey1);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000422 assertThat(
423 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Eric Fellheimere6590722015-11-17 17:07:48 +0000424 batchStatter,
425 new ModifiedFileSet.Builder().modify(
426 out1.getExecPath().getParentDirectory()).build())).isEmpty();
427 assertThat(
Ulf Adamsc73051c62016-03-23 09:18:13 +0000428 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Eric Fellheimere6590722015-11-17 17:07:48 +0000429 batchStatter, ModifiedFileSet.NOTHING_MODIFIED)).isEmpty();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000430 }
431
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000432 public void checkDirtyTreeArtifactActions(BatchStat batchStatter)
433 throws Exception {
434 // Normally, an Action specifies the contents of a TreeArtifact when it executes.
435 // To decouple FileSystemValueTester checking from Action execution, we inject TreeArtifact
436 // contents into ActionExecutionValues.
437
cpeyserac09f0a2018-02-05 09:33:15 -0800438 SpecialArtifact out1 = createTreeArtifact("one");
Rumou Duana77f32c2016-04-13 21:59:21 +0000439 TreeFileArtifact file11 = treeFileArtifact(out1, "fizz");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000440 FileSystemUtils.createDirectoryAndParents(out1.getPath());
441 FileSystemUtils.writeContentAsLatin1(file11.getPath(), "buzz");
442
cpeyserac09f0a2018-02-05 09:33:15 -0800443 SpecialArtifact out2 = createTreeArtifact("two");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000444 FileSystemUtils.createDirectoryAndParents(out2.getPath().getChild("subdir"));
Rumou Duana77f32c2016-04-13 21:59:21 +0000445 TreeFileArtifact file21 = treeFileArtifact(out2, "moony");
446 TreeFileArtifact file22 = treeFileArtifact(out2, "subdir/wormtail");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000447 FileSystemUtils.writeContentAsLatin1(file21.getPath(), "padfoot");
448 FileSystemUtils.writeContentAsLatin1(file22.getPath(), "prongs");
449
cpeyserac09f0a2018-02-05 09:33:15 -0800450 SpecialArtifact outEmpty = createTreeArtifact("empty");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000451 FileSystemUtils.createDirectoryAndParents(outEmpty.getPath());
452
cpeyserac09f0a2018-02-05 09:33:15 -0800453 SpecialArtifact outUnchanging = createTreeArtifact("untouched");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000454 FileSystemUtils.createDirectoryAndParents(outUnchanging.getPath());
455
cpeyserac09f0a2018-02-05 09:33:15 -0800456 SpecialArtifact last = createTreeArtifact("zzzzzzzzzz");
Rumou Duan45e8e572016-06-17 16:43:44 +0000457 FileSystemUtils.createDirectoryAndParents(last.getPath());
458
janakrbaf52ae2018-02-14 09:03:18 -0800459 ActionLookupKey actionLookupKey =
janakr573807d2018-01-11 14:02:35 -0800460 new ActionLookupKey() {
461 @Override
462 public SkyFunctionName functionName() {
463 return SkyFunctionName.FOR_TESTING;
464 }
465 };
janakr93e3eea2017-03-30 22:09:37 +0000466 SkyKey actionKey1 = ActionExecutionValue.key(actionLookupKey, 0);
467 SkyKey actionKey2 = ActionExecutionValue.key(actionLookupKey, 1);
468 SkyKey actionKeyEmpty = ActionExecutionValue.key(actionLookupKey, 2);
469 SkyKey actionKeyUnchanging = ActionExecutionValue.key(actionLookupKey, 3);
470 SkyKey actionKeyLast = ActionExecutionValue.key(actionLookupKey, 4);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000471 differencer.inject(
472 ImmutableMap.<SkyKey, SkyValue>of(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000473 actionKey1,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000474 actionValueWithTreeArtifacts(ImmutableList.of(file11)),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000475 actionKey2,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000476 actionValueWithTreeArtifacts(ImmutableList.of(file21, file22)),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000477 actionKeyEmpty,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000478 actionValueWithEmptyDirectory(outEmpty),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000479 actionKeyUnchanging,
Rumou Duan45e8e572016-06-17 16:43:44 +0000480 actionValueWithEmptyDirectory(outUnchanging),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000481 actionKeyLast,
Rumou Duan45e8e572016-06-17 16:43:44 +0000482 actionValueWithEmptyDirectory(last)));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000483
lberkiaea56b32017-05-30 12:35:33 +0200484 assertThat(
485 driver
486 .evaluate(ImmutableList.<SkyKey>of(), false, 1, NullEventHandler.INSTANCE)
487 .hasError())
488 .isFalse();
Ulf Adamsc73051c62016-03-23 09:18:13 +0000489 assertThat(new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000490 batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED)).isEmpty();
491
492 // Touching the TreeArtifact directory should have no effect
493 FileSystemUtils.touchFile(out1.getPath());
494 assertThat(
Ulf Adamsc73051c62016-03-23 09:18:13 +0000495 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000496 batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED)).isEmpty();
497 // Neither should touching a subdirectory.
498 FileSystemUtils.touchFile(out2.getPath().getChild("subdir"));
499 assertThat(
Ulf Adamsc73051c62016-03-23 09:18:13 +0000500 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000501 batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED)).isEmpty();
502
503 /* **** Tests for directories **** */
504
505 // Removing a directory (even if empty) should have an effect
506 outEmpty.getPath().delete();
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000507 assertThat(
508 new FilesystemValueChecker(null, null)
509 .getDirtyActionValues(
510 evaluator.getValues(),
511 batchStatter,
512 new ModifiedFileSet.Builder().modify(outEmpty.getExecPath()).build()))
513 .containsExactly(actionKeyEmpty);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000514 // Symbolic links should count as dirty
515 Path dummyEmptyDir = fs.getPath("/bin").getRelative("symlink");
516 FileSystemUtils.createDirectoryAndParents(dummyEmptyDir);
517 FileSystemUtils.ensureSymbolicLink(outEmpty.getPath(), dummyEmptyDir);
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000518 assertThat(
519 new FilesystemValueChecker(null, null)
520 .getDirtyActionValues(
521 evaluator.getValues(),
522 batchStatter,
523 new ModifiedFileSet.Builder().modify(outEmpty.getExecPath()).build()))
524 .containsExactly(actionKeyEmpty);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000525
526 // We're done fiddling with this... restore the original state
527 outEmpty.getPath().delete();
528 FileSystemUtils.deleteTree(dummyEmptyDir);
529 FileSystemUtils.createDirectoryAndParents(outEmpty.getPath());
530
531 /* **** Tests for files and directory contents ****/
532
533 // Test that file contents matter. This is covered by existing tests already,
534 // so it's just a sanity check.
535 FileSystemUtils.writeContentAsLatin1(file11.getPath(), "goodbye");
Ulf Adamsc73051c62016-03-23 09:18:13 +0000536 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000537 new FilesystemValueChecker(null, null)
538 .getDirtyActionValues(
539 evaluator.getValues(),
540 batchStatter,
541 new ModifiedFileSet.Builder().modify(file11.getExecPath()).build()))
542 .containsExactly(actionKey1);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000543
544 // Test that directory contents (and nested contents) matter
Rumou Duana77f32c2016-04-13 21:59:21 +0000545 Artifact out1new = treeFileArtifact(out1, "julius/caesar");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000546 FileSystemUtils.createDirectoryAndParents(out1.getPath().getChild("julius"));
547 FileSystemUtils.writeContentAsLatin1(out1new.getPath(), "octavian");
548 // even for empty directories
Rumou Duana77f32c2016-04-13 21:59:21 +0000549 Artifact outEmptyNew = treeFileArtifact(outEmpty, "marcus");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000550 FileSystemUtils.writeContentAsLatin1(outEmptyNew.getPath(), "aurelius");
551 // so does removing
552 file21.getPath().delete();
553 // now, let's test our changes are actually visible
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000554 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000555 new FilesystemValueChecker(null, null)
556 .getDirtyActionValues(
557 evaluator.getValues(), batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED))
558 .containsExactly(actionKey1, actionKey2, actionKeyEmpty);
559 assertThat(
560 new FilesystemValueChecker(null, null)
561 .getDirtyActionValues(
562 evaluator.getValues(),
563 batchStatter,
564 new ModifiedFileSet.Builder()
565 .modify(file21.getExecPath())
566 .modify(out1new.getExecPath())
567 .modify(outEmptyNew.getExecPath())
568 .build()))
569 .containsExactly(actionKey1, actionKey2, actionKeyEmpty);
Rumou Duan45e8e572016-06-17 16:43:44 +0000570 // We also check that if the modified file set does not contain our modified files on disk,
571 // we are not going to check and return them.
572 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000573 new FilesystemValueChecker(null, null)
574 .getDirtyActionValues(
575 evaluator.getValues(),
576 batchStatter,
577 new ModifiedFileSet.Builder()
578 .modify(file21.getExecPath())
579 .modify(outEmptyNew.getExecPath())
580 .build()))
581 .containsExactly(actionKey2, actionKeyEmpty);
Rumou Duan45e8e572016-06-17 16:43:44 +0000582 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000583 new FilesystemValueChecker(null, null)
584 .getDirtyActionValues(
585 evaluator.getValues(),
586 batchStatter,
587 new ModifiedFileSet.Builder()
588 .modify(file21.getExecPath())
589 .modify(out1new.getExecPath())
590 .build()))
591 .containsExactly(actionKey1, actionKey2);
Rumou Duan45e8e572016-06-17 16:43:44 +0000592 // Check modifying the last (lexicographically) tree artifact.
593 last.getPath().delete();
594 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000595 new FilesystemValueChecker(null, null)
596 .getDirtyActionValues(
597 evaluator.getValues(),
598 batchStatter,
599 new ModifiedFileSet.Builder()
600 .modify(file21.getExecPath())
601 .modify(out1new.getExecPath())
602 .modify(last.getExecPath())
603 .build()))
604 .containsExactly(actionKey1, actionKey2, actionKeyLast);
Rumou Duan45e8e572016-06-17 16:43:44 +0000605 // Check ModifiedFileSet without the last (lexicographically) tree artifact.
606 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000607 new FilesystemValueChecker(null, null)
608 .getDirtyActionValues(
609 evaluator.getValues(),
610 batchStatter,
611 new ModifiedFileSet.Builder()
612 .modify(file21.getExecPath())
613 .modify(out1new.getExecPath())
614 .build()))
615 .containsExactly(actionKey1, actionKey2);
Rumou Duan45e8e572016-06-17 16:43:44 +0000616 // Restore
617 last.getPath().delete();
618 FileSystemUtils.createDirectoryAndParents(last.getPath());
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000619 // We add a test for NOTHING_MODIFIED, because FileSystemValueChecker doesn't
620 // pay attention to file sets for TreeArtifact directory listings.
621 assertThat(
Ulf Adamsc73051c62016-03-23 09:18:13 +0000622 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000623 batchStatter, ModifiedFileSet.NOTHING_MODIFIED)).isEmpty();
624 }
625
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000626 private Artifact createDerivedArtifact(String relPath) throws IOException {
627 Path outputPath = fs.getPath("/bin");
628 outputPath.createDirectory();
629 return new Artifact(
tomlu1cdcdf92018-01-16 11:07:51 -0800630 outputPath.getRelative(relPath), ArtifactRoot.asDerivedRoot(fs.getPath("/"), outputPath));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000631 }
632
cpeyserac09f0a2018-02-05 09:33:15 -0800633 private SpecialArtifact createTreeArtifact(String relPath) throws IOException {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000634 Path outputDir = fs.getPath("/bin");
635 Path outputPath = outputDir.getRelative(relPath);
636 outputDir.createDirectory();
tomlu1cdcdf92018-01-16 11:07:51 -0800637 ArtifactRoot derivedRoot = ArtifactRoot.asDerivedRoot(fs.getPath("/"), outputDir);
tomluee6a6862018-01-17 14:36:26 -0800638 return new SpecialArtifact(
tomluee6a6862018-01-17 14:36:26 -0800639 derivedRoot,
640 derivedRoot.getExecPath().getRelative(derivedRoot.getRoot().relativize(outputPath)),
janakr192c8902018-01-19 22:29:42 -0800641 ArtifactOwner.NullArtifactOwner.INSTANCE,
tomluee6a6862018-01-17 14:36:26 -0800642 SpecialArtifactType.TREE);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000643 }
644
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000645 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000646 public void testDirtyActions() throws Exception {
647 checkDirtyActions(null, false);
648 }
649
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000650 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000651 public void testDirtyActionsBatchStat() throws Exception {
652 checkDirtyActions(
653 new BatchStat() {
654 @Override
655 public List<FileStatusWithDigest> batchStat(
656 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
657 throws IOException {
658 List<FileStatusWithDigest> stats = new ArrayList<>();
659 for (PathFragment pathFrag : paths) {
660 stats.add(
661 FileStatusWithDigestAdapter.adapt(
tomlu6c919062018-01-11 17:32:09 -0800662 fs.getPath("/").getRelative(pathFrag).statIfFound(Symlinks.NOFOLLOW)));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000663 }
664 return stats;
665 }
666 },
667 false);
668 }
669
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000670 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000671 public void testDirtyActionsBatchStatWithDigest() throws Exception {
672 checkDirtyActions(
673 new BatchStat() {
674 @Override
675 public List<FileStatusWithDigest> batchStat(
676 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
677 throws IOException {
678 List<FileStatusWithDigest> stats = new ArrayList<>();
679 for (PathFragment pathFrag : paths) {
tomlu6c919062018-01-11 17:32:09 -0800680 final Path path = fs.getPath("/").getRelative(pathFrag);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000681 stats.add(statWithDigest(path, path.statIfFound(Symlinks.NOFOLLOW)));
682 }
683 return stats;
684 }
685 },
686 true);
687 }
688
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000689 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000690 public void testDirtyActionsBatchStatFallback() throws Exception {
691 checkDirtyActions(
692 new BatchStat() {
693 @Override
694 public List<FileStatusWithDigest> batchStat(
695 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
696 throws IOException {
697 throw new IOException("try again");
698 }
699 },
700 false);
701 }
702
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000703 @Test
704 public void testDirtyTreeArtifactActions() throws Exception {
705 checkDirtyTreeArtifactActions(null);
706 }
707
708 @Test
709 public void testDirtyTreeArtifactActionsBatchStat() throws Exception {
710 checkDirtyTreeArtifactActions(
711 new BatchStat() {
712 @Override
713 public List<FileStatusWithDigest> batchStat(
714 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
715 throws IOException {
716 List<FileStatusWithDigest> stats = new ArrayList<>();
717 for (PathFragment pathFrag : paths) {
718 stats.add(
719 FileStatusWithDigestAdapter.adapt(
tomlu6c919062018-01-11 17:32:09 -0800720 fs.getPath("/").getRelative(pathFrag).statIfFound(Symlinks.NOFOLLOW)));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000721 }
722 return stats;
723 }
724 });
725 }
726
727 // TODO(bazel-team): Add some tests for FileSystemValueChecker#changedKeys*() methods.
728 // Presently these appear to be untested.
729
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000730 private ActionExecutionValue actionValue(Action action, boolean forceDigest) {
731 Map<Artifact, FileValue> artifactData = new HashMap<>();
732 for (Artifact output : action.getOutputs()) {
733 try {
734 Path path = output.getPath();
735 FileStatusWithDigest stat =
736 forceDigest ? statWithDigest(path, path.statIfFound(Symlinks.NOFOLLOW)) : null;
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000737 artifactData.put(output,
Rumou Duana77f32c2016-04-13 21:59:21 +0000738 ActionMetadataHandler.fileValueFromArtifact(output, stat, null));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000739 } catch (IOException e) {
740 throw new IllegalStateException(e);
741 }
742 }
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000743 return new ActionExecutionValue(
744 artifactData,
745 ImmutableMap.<Artifact, TreeArtifactValue>of(),
kush2ce45a22018-05-02 14:15:37 -0700746 ImmutableMap.<Artifact, FileArtifactValue>of(),
747 /*outputSymlinks=*/ null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000748 }
749
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000750 private ActionExecutionValue actionValueWithEmptyDirectory(Artifact emptyDir) {
751 TreeArtifactValue emptyValue = TreeArtifactValue.create
Rumou Duana77f32c2016-04-13 21:59:21 +0000752 (ImmutableMap.<TreeFileArtifact, FileArtifactValue>of());
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000753
754 return new ActionExecutionValue(
Rumou Duana77f32c2016-04-13 21:59:21 +0000755 ImmutableMap.<Artifact, FileValue>of(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000756 ImmutableMap.of(emptyDir, emptyValue),
kush2ce45a22018-05-02 14:15:37 -0700757 ImmutableMap.<Artifact, FileArtifactValue>of(),
758 /*outputSymlinks=*/ null);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000759 }
760
Rumou Duana77f32c2016-04-13 21:59:21 +0000761 private ActionExecutionValue actionValueWithTreeArtifacts(List<TreeFileArtifact> contents) {
762 Map<Artifact, FileValue> fileData = new HashMap<>();
763 Map<Artifact, Map<TreeFileArtifact, FileArtifactValue>> directoryData = new HashMap<>();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000764
Rumou Duana77f32c2016-04-13 21:59:21 +0000765 for (TreeFileArtifact output : contents) {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000766 try {
Rumou Duana77f32c2016-04-13 21:59:21 +0000767 Map<TreeFileArtifact, FileArtifactValue> dirDatum =
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000768 directoryData.get(output.getParent());
769 if (dirDatum == null) {
770 dirDatum = new HashMap<>();
771 directoryData.put(output.getParent(), dirDatum);
772 }
Rumou Duana77f32c2016-04-13 21:59:21 +0000773 FileValue fileValue = ActionMetadataHandler.fileValueFromArtifact(output, null, null);
Janak Ramakrishnancba16452016-07-29 02:17:02 +0000774 dirDatum.put(output, FileArtifactValue.create(output, fileValue));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000775 fileData.put(output, fileValue);
776 } catch (IOException e) {
777 throw new IllegalStateException(e);
778 }
779 }
780
781 Map<Artifact, TreeArtifactValue> treeArtifactData = new HashMap<>();
Rumou Duana77f32c2016-04-13 21:59:21 +0000782 for (Map.Entry<Artifact, Map<TreeFileArtifact, FileArtifactValue>> dirDatum :
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000783 directoryData.entrySet()) {
Rumou Duana77f32c2016-04-13 21:59:21 +0000784 treeArtifactData.put(dirDatum.getKey(), TreeArtifactValue.create(dirDatum.getValue()));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000785 }
786
kush2ce45a22018-05-02 14:15:37 -0700787 return new ActionExecutionValue(
788 fileData,
789 treeArtifactData,
790 ImmutableMap.<Artifact, FileArtifactValue>of(),
791 /*outputSymlinks=*/ null);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000792 }
793
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000794 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000795 public void testPropagatesRuntimeExceptions() throws Exception {
tomluee6a6862018-01-17 14:36:26 -0800796 Collection<SkyKey> values =
797 ImmutableList.of(
798 FileValue.key(
799 RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo"))));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000800 driver.evaluate(
801 values, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000802 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000803
Nathan Harmata8cd29782015-11-10 03:24:01 +0000804 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000805
806 fs.statThrowsRuntimeException = true;
807 try {
Nathan Harmata8cd29782015-11-10 03:24:01 +0000808 getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000809 fail();
810 } catch (RuntimeException e) {
811 assertThat(e).hasMessage("bork");
812 }
813 }
814
815 private static void assertEmptyDiff(Diff diff) {
816 assertDiffWithNewValues(diff);
817 }
818
819 private static void assertDiffWithNewValues(Diff diff, SkyKey... keysWithNewValues) {
820 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
821 assertThat(diff.changedKeysWithNewValues().keySet())
822 .containsExactlyElementsIn(Arrays.asList(keysWithNewValues));
823 }
824
825 private class MockFileSystem extends InMemoryFileSystem {
826
827 boolean statThrowsRuntimeException;
828 boolean readlinkThrowsIoException;
829
830 MockFileSystem() {
831 super();
832 }
833
834 @Override
aehligc801c392017-12-19 07:12:25 -0800835 public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000836 if (statThrowsRuntimeException) {
837 throw new RuntimeException("bork");
838 }
839 return super.stat(path, followSymlinks);
840 }
841
842 @Override
aehligc801c392017-12-19 07:12:25 -0800843 protected PathFragment readSymbolicLink(Path path) throws IOException {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000844 if (readlinkThrowsIoException) {
845 throw new IOException("readlink failed");
846 }
847 return super.readSymbolicLink(path);
848 }
849 }
850
851 private static FileStatusWithDigest statWithDigest(final Path path, final FileStatus stat) {
852 return new FileStatusWithDigest() {
853 @Nullable
854 @Override
855 public byte[] getDigest() throws IOException {
olaolabfd1d332017-06-19 16:55:24 -0400856 return path.getDigest();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000857 }
858
859 @Override
860 public boolean isFile() {
861 return stat.isFile();
862 }
863
864 @Override
865 public boolean isSpecialFile() {
866 return stat.isSpecialFile();
867 }
868
869 @Override
870 public boolean isDirectory() {
871 return stat.isDirectory();
872 }
873
874 @Override
875 public boolean isSymbolicLink() {
876 return stat.isSymbolicLink();
877 }
878
879 @Override
880 public long getSize() throws IOException {
881 return stat.getSize();
882 }
883
884 @Override
885 public long getLastModifiedTime() throws IOException {
886 return stat.getLastModifiedTime();
887 }
888
889 @Override
890 public long getLastChangeTime() throws IOException {
891 return stat.getLastChangeTime();
892 }
893
894 @Override
895 public long getNodeId() throws IOException {
896 return stat.getNodeId();
897 }
898 };
899 }
900
Nathan Harmata8cd29782015-11-10 03:24:01 +0000901 private static Diff getDirtyFilesystemKeys(MemoizingEvaluator evaluator,
902 FilesystemValueChecker checker) throws InterruptedException {
903 return checker.getDirtyKeys(evaluator.getValues(), new BasicFilesystemDirtinessChecker());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000904 }
905}