blob: cb06db2752e9ff1aa48ed3cc8465fd0e64309199 [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;
jcater83130f42019-04-30 14:29:28 -070018import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000019
20import com.google.common.collect.ImmutableList;
21import com.google.common.collect.ImmutableMap;
22import com.google.common.collect.ImmutableSet;
buchgr4992ae22019-03-20 04:23:32 -070023import com.google.common.hash.HashCode;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000024import com.google.common.util.concurrent.Runnables;
25import com.google.devtools.build.lib.actions.Action;
buchgr4992ae22019-03-20 04:23:32 -070026import com.google.devtools.build.lib.actions.ActionInputHelper;
janakrefb3f152019-06-05 17:42:34 -070027import com.google.devtools.build.lib.actions.ActionLookupData;
janakr93e3eea2017-03-30 22:09:37 +000028import com.google.devtools.build.lib.actions.ActionLookupValue.ActionLookupKey;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000029import com.google.devtools.build.lib.actions.Artifact;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000030import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
31import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType;
Rumou Duana77f32c2016-04-13 21:59:21 +000032import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
tomlu1cdcdf92018-01-16 11:07:51 -080033import com.google.devtools.build.lib.actions.ArtifactRoot;
shahan602cc852018-06-06 20:09:57 -070034import com.google.devtools.build.lib.actions.FileArtifactValue;
buchgr4992ae22019-03-20 04:23:32 -070035import com.google.devtools.build.lib.actions.FileArtifactValue.RemoteFileArtifactValue;
shahan602cc852018-06-06 20:09:57 -070036import com.google.devtools.build.lib.actions.FileStateValue;
37import com.google.devtools.build.lib.actions.FileValue;
janakraea05602019-05-22 15:41:29 -070038import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000039import com.google.devtools.build.lib.actions.util.TestAction;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000040import com.google.devtools.build.lib.analysis.BlazeDirectories;
janakr3b63a4e2017-09-14 09:55:40 +020041import com.google.devtools.build.lib.analysis.ServerDirectories;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000042import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000043import com.google.devtools.build.lib.events.NullEventHandler;
janakr15e15c22019-01-30 11:24:49 -080044import com.google.devtools.build.lib.packages.WorkspaceFileValue;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000045import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
46import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.BasicFilesystemDirtinessChecker;
Nathan Harmatad4f75942016-10-18 08:55:17 +000047import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
John Cater5e9ce942016-10-12 17:23:30 +000048import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +000049import com.google.devtools.build.lib.testutil.TestConstants;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000050import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
laszlocsomor29bdf632018-03-13 02:50:46 -070051import com.google.devtools.build.lib.testutil.TimestampGranularityUtils;
Laszlo Csomora278aec2018-03-09 04:07:31 -080052import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000053import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
54import com.google.devtools.build.lib.vfs.BatchStat;
buchgr4992ae22019-03-20 04:23:32 -070055import com.google.devtools.build.lib.vfs.DigestHashFunction;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000056import com.google.devtools.build.lib.vfs.FileStatus;
57import com.google.devtools.build.lib.vfs.FileStatusWithDigest;
58import com.google.devtools.build.lib.vfs.FileStatusWithDigestAdapter;
59import com.google.devtools.build.lib.vfs.FileSystemUtils;
Eric Fellheimere6590722015-11-17 17:07:48 +000060import com.google.devtools.build.lib.vfs.ModifiedFileSet;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000061import com.google.devtools.build.lib.vfs.Path;
62import com.google.devtools.build.lib.vfs.PathFragment;
tomluee6a6862018-01-17 14:36:26 -080063import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000064import com.google.devtools.build.lib.vfs.RootedPath;
65import com.google.devtools.build.lib.vfs.Symlinks;
djasper7faa0ef2019-03-28 10:00:00 -070066import com.google.devtools.build.lib.vfs.UnixGlob;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000067import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
68import com.google.devtools.build.skyframe.Differencer.Diff;
Googler10028672018-10-25 12:14:34 -070069import com.google.devtools.build.skyframe.EvaluationContext;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000070import com.google.devtools.build.skyframe.EvaluationResult;
71import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
72import com.google.devtools.build.skyframe.MemoizingEvaluator;
73import com.google.devtools.build.skyframe.RecordingDifferencer;
janakr1cde8722017-10-10 03:22:21 +020074import com.google.devtools.build.skyframe.SequencedRecordingDifferencer;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000075import com.google.devtools.build.skyframe.SequentialBuildDriver;
76import com.google.devtools.build.skyframe.SkyFunction;
77import com.google.devtools.build.skyframe.SkyFunctionName;
78import com.google.devtools.build.skyframe.SkyKey;
79import com.google.devtools.build.skyframe.SkyValue;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000080import java.io.IOException;
81import java.util.ArrayList;
82import java.util.Arrays;
83import java.util.Collection;
buchgr4992ae22019-03-20 04:23:32 -070084import java.util.Collections;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000085import java.util.HashMap;
86import java.util.List;
87import java.util.Map;
88import java.util.UUID;
89import java.util.concurrent.atomic.AtomicReference;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000090import javax.annotation.Nullable;
Janak Ramakrishnancba16452016-07-29 02:17:02 +000091import org.junit.Before;
92import org.junit.Test;
93import org.junit.runner.RunWith;
94import org.junit.runners.JUnit4;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000095
96/**
97 * Tests for {@link FilesystemValueChecker}.
98 */
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +000099@RunWith(JUnit4.class)
100public class FilesystemValueCheckerTest {
Googler10028672018-10-25 12:14:34 -0700101 private static final EvaluationContext EVALUATION_OPTIONS =
102 EvaluationContext.newBuilder()
103 .setKeepGoing(false)
104 .setNumThreads(SkyframeExecutor.DEFAULT_THREAD_COUNT)
105 .setEventHander(NullEventHandler.INSTANCE)
106 .build();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000107
108 private RecordingDifferencer differencer;
109 private MemoizingEvaluator evaluator;
110 private SequentialBuildDriver driver;
111 private MockFileSystem fs;
112 private Path pkgRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000113
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000114 @Before
Florian Weikert92b22362015-12-03 10:17:18 +0000115 public final void setUp() throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000116 ImmutableMap.Builder<SkyFunctionName, SkyFunction> skyFunctions = ImmutableMap.builder();
117
118 fs = new MockFileSystem();
119 pkgRoot = fs.getPath("/testroot");
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000120 FileSystemUtils.createDirectoryAndParents(pkgRoot);
121 FileSystemUtils.createEmptyFile(pkgRoot.getRelative("WORKSPACE"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000122
John Catere0d1d0e2017-11-28 20:47:41 -0800123 AtomicReference<PathPackageLocator> pkgLocator =
124 new AtomicReference<>(
125 new PathPackageLocator(
126 fs.getPath("/output_base"),
tomluee6a6862018-01-17 14:36:26 -0800127 ImmutableList.of(Root.fromPath(pkgRoot)),
John Catere0d1d0e2017-11-28 20:47:41 -0800128 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
janakr3b63a4e2017-09-14 09:55:40 +0200129 BlazeDirectories directories =
130 new BlazeDirectories(
cushon849df362018-05-14 01:51:45 -0700131 new ServerDirectories(pkgRoot, pkgRoot, pkgRoot),
132 pkgRoot,
133 /* defaultSystemJavabase= */ null,
134 TestConstants.PRODUCT_NAME);
nharmata3fb7d342018-02-23 11:37:51 -0800135 ExternalFilesHelper externalFilesHelper = ExternalFilesHelper.createForTesting(
Nathan Harmatad4f75942016-10-18 08:55:17 +0000136 pkgLocator, ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS, directories);
shahan602cc852018-06-06 20:09:57 -0700137 skyFunctions.put(
138 FileStateValue.FILE_STATE,
139 new FileStateFunction(
djasper7faa0ef2019-03-28 10:00:00 -0700140 new AtomicReference<TimestampGranularityMonitor>(),
141 new AtomicReference<>(UnixGlob.DEFAULT_SYSCALLS),
142 externalFilesHelper));
shahan602cc852018-06-06 20:09:57 -0700143 skyFunctions.put(FileValue.FILE, new FileFunction(pkgLocator));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000144 skyFunctions.put(
145 SkyFunctions.FILE_SYMLINK_CYCLE_UNIQUENESS, new FileSymlinkCycleUniquenessFunction());
146 skyFunctions.put(
147 SkyFunctions.FILE_SYMLINK_INFINITE_EXPANSION_UNIQUENESS,
148 new FileSymlinkInfiniteExpansionUniquenessFunction());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000149 skyFunctions.put(SkyFunctions.PACKAGE,
150 new PackageFunction(null, null, null, null, null, null, null));
John Cater5e9ce942016-10-12 17:23:30 +0000151 skyFunctions.put(
152 SkyFunctions.PACKAGE_LOOKUP,
153 new PackageLookupFunction(
154 new AtomicReference<>(ImmutableSet.<PackageIdentifier>of()),
John Cater0c0735a2016-11-11 01:52:02 +0000155 CrossRepositoryLabelViolationStrategy.ERROR,
John Catere0d1d0e2017-11-28 20:47:41 -0800156 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
Damien Martin-Guillerez4ecfe512016-01-22 11:03:23 +0000157 skyFunctions.put(SkyFunctions.WORKSPACE_AST,
158 new WorkspaceASTFunction(TestRuleClassProvider.getRuleClassProvider()));
carmid6a98282018-03-13 19:19:16 -0700159 skyFunctions.put(
janakr15e15c22019-01-30 11:24:49 -0800160 WorkspaceFileValue.WORKSPACE_FILE,
carmid6a98282018-03-13 19:19:16 -0700161 new WorkspaceFileFunction(
162 TestRuleClassProvider.getRuleClassProvider(),
163 TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING
164 .builder(directories)
nharmatafde0bd2f2018-12-21 10:17:56 -0800165 .build(TestRuleClassProvider.getRuleClassProvider(), fs),
mjhalupkaf0e48112019-01-14 13:01:56 -0800166 directories,
167 /*skylarkImportLookupFunctionForInlining=*/ null));
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000168 skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000169
janakr1cde8722017-10-10 03:22:21 +0200170 differencer = new SequencedRecordingDifferencer();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000171 evaluator = new InMemoryMemoizingEvaluator(skyFunctions.build(), differencer);
172 driver = new SequentialBuildDriver(evaluator);
173 PrecomputedValue.BUILD_ID.set(differencer, UUID.randomUUID());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000174 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000175 }
176
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000177 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000178 public void testEmpty() throws Exception {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000179 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000180 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000181 }
182
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000183 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000184 public void testSimple() throws Exception {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000185 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000186
187 Path path = fs.getPath("/foo");
188 FileSystemUtils.createEmptyFile(path);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000189 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000190
tomluee6a6862018-01-17 14:36:26 -0800191 SkyKey skyKey =
192 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800193 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000194 EvaluationResult<SkyValue> result =
Googler10028672018-10-25 12:14:34 -0700195 driver.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200196 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000197
Nathan Harmata8cd29782015-11-10 03:24:01 +0000198 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000199
200 FileSystemUtils.writeContentAsLatin1(path, "hello");
Nathan Harmata8cd29782015-11-10 03:24:01 +0000201 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000202
203 // The dirty bits are not reset until the FileValues are actually revalidated.
Nathan Harmata8cd29782015-11-10 03:24:01 +0000204 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000205
206 differencer.invalidate(ImmutableList.of(skyKey));
Googler10028672018-10-25 12:14:34 -0700207 result = driver.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200208 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000209 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000210 }
211
212 /**
213 * Tests that an already-invalidated value can still be marked changed: symlink points at sym1.
214 * Invalidate symlink by changing sym1 from pointing at path to point to sym2. This only dirties
215 * (rather than changes) symlink because sym2 still points at path, so all symlink stats remain
216 * the same. Then do a null build, change sym1 back to point at path, and change symlink to not be
217 * a symlink anymore. The fact that it is not a symlink should be detected.
218 */
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000219 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000220 public void testDirtySymlink() throws Exception {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000221 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000222
223 Path path = fs.getPath("/foo");
224 FileSystemUtils.writeContentAsLatin1(path, "foo contents");
225 // We need the intermediate sym1 and sym2 so that we can dirty a child of symlink without
226 // actually changing the FileValue calculated for symlink (if we changed the contents of foo,
Laszlo Csomora278aec2018-03-09 04:07:31 -0800227 // the FileValue created for symlink would notice, since it stats foo).
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000228 Path sym1 = fs.getPath("/sym1");
229 Path sym2 = fs.getPath("/sym2");
230 Path symlink = fs.getPath("/bar");
231 FileSystemUtils.ensureSymbolicLink(symlink, sym1);
232 FileSystemUtils.ensureSymbolicLink(sym1, path);
233 FileSystemUtils.ensureSymbolicLink(sym2, path);
234 SkyKey fooKey =
tomlu4c9fafd2018-01-18 10:29:11 -0800235 FileValue.key(RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000236 RootedPath symlinkRootedPath =
tomlu4c9fafd2018-01-18 10:29:11 -0800237 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/bar"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000238 SkyKey symlinkKey = FileValue.key(symlinkRootedPath);
239 SkyKey symlinkFileStateKey = FileStateValue.key(symlinkRootedPath);
240 RootedPath sym1RootedPath =
tomlu4c9fafd2018-01-18 10:29:11 -0800241 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/sym1"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000242 SkyKey sym1FileStateKey = FileStateValue.key(sym1RootedPath);
243 Iterable<SkyKey> allKeys = ImmutableList.of(symlinkKey, fooKey);
244
245 // First build -- prime the graph.
Googler10028672018-10-25 12:14:34 -0700246 EvaluationResult<FileValue> result = driver.evaluate(allKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200247 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000248 FileValue symlinkValue = result.get(symlinkKey);
249 FileValue fooValue = result.get(fooKey);
lberkiaea56b32017-05-30 12:35:33 +0200250 assertWithMessage(symlinkValue.toString()).that(symlinkValue.isSymlink()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000251 // Digest is not always available, so use size as a proxy for contents.
lberkiaea56b32017-05-30 12:35:33 +0200252 assertThat(symlinkValue.getSize()).isEqualTo(fooValue.getSize());
Nathan Harmata8cd29782015-11-10 03:24:01 +0000253 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000254
255 // Before second build, move sym1 to point to sym2.
lberkiaea56b32017-05-30 12:35:33 +0200256 assertThat(sym1.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000257 FileSystemUtils.ensureSymbolicLink(sym1, sym2);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000258 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000259
260 differencer.invalidate(ImmutableList.of(sym1FileStateKey));
Googler10028672018-10-25 12:14:34 -0700261 result = driver.evaluate(ImmutableList.<SkyKey>of(), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200262 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000263 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000264
265 // Before third build, move sym1 back to original (so change pruning will prevent signaling of
266 // its parents, but change symlink for real.
lberkiaea56b32017-05-30 12:35:33 +0200267 assertThat(sym1.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000268 FileSystemUtils.ensureSymbolicLink(sym1, path);
lberkiaea56b32017-05-30 12:35:33 +0200269 assertThat(symlink.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000270 FileSystemUtils.writeContentAsLatin1(symlink, "new symlink contents");
Nathan Harmata8cd29782015-11-10 03:24:01 +0000271 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), symlinkFileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000272 differencer.invalidate(ImmutableList.of(symlinkFileStateKey));
Googler10028672018-10-25 12:14:34 -0700273 result = driver.evaluate(allKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200274 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000275 symlinkValue = result.get(symlinkKey);
lberkiaea56b32017-05-30 12:35:33 +0200276 assertWithMessage(symlinkValue.toString()).that(symlinkValue.isSymlink()).isFalse();
277 assertThat(result.get(fooKey)).isEqualTo(fooValue);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000278 assertThat(symlinkValue.getSize()).isNotEqualTo(fooValue.getSize());
Nathan Harmata8cd29782015-11-10 03:24:01 +0000279 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000280 }
281
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000282 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000283 public void testExplicitFiles() throws Exception {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000284 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000285
286 Path path1 = fs.getPath("/foo1");
287 Path path2 = fs.getPath("/foo2");
288 FileSystemUtils.createEmptyFile(path1);
289 FileSystemUtils.createEmptyFile(path2);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000290 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000291
292 SkyKey key1 =
293 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800294 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo1")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000295 SkyKey key2 =
296 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800297 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo2")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000298 Iterable<SkyKey> skyKeys = ImmutableList.of(key1, key2);
Googler10028672018-10-25 12:14:34 -0700299 EvaluationResult<SkyValue> result = driver.evaluate(skyKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200300 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000301
Nathan Harmata8cd29782015-11-10 03:24:01 +0000302 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000303
Laszlo Csomora278aec2018-03-09 04:07:31 -0800304 // Wait for the timestamp granularity to elapse, so updating the files will observably advance
305 // their ctime.
laszlocsomor29bdf632018-03-13 02:50:46 -0700306 TimestampGranularityUtils.waitForTimestampGranularity(
307 System.currentTimeMillis(), OutErr.SYSTEM_OUT_ERR);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800308 // Update path1's contents and mtime. This will update the file's ctime.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000309 FileSystemUtils.writeContentAsLatin1(path1, "hello1");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000310 path1.setLastModifiedTime(27);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800311 // Update path2's mtime but not its contents. We expect that an mtime change suffices to update
312 // the ctime.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000313 path2.setLastModifiedTime(42);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800314 // Assert that both files changed. The change detection relies, among other things, on ctime
315 // change.
Nathan Harmata8cd29782015-11-10 03:24:01 +0000316 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), key1, key2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000317
318 differencer.invalidate(skyKeys);
Googler10028672018-10-25 12:14:34 -0700319 result = driver.evaluate(skyKeys, EVALUATION_OPTIONS);
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 =
Googler10028672018-10-25 12:14:34 -0700335 driver.evaluate(ImmutableList.of(fileKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200336 assertThat(result.hasError()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000337
338 fs.readlinkThrowsIoException = false;
Ulf Adamsc73051c62016-03-23 09:18:13 +0000339 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000340 Diff diff = getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000341 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
342 assertThat(diff.changedKeysWithNewValues()).isEmpty();
343 }
344
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000345 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000346 public void testFilesInCycleNotConsideredDirty() throws Exception {
347 Path path1 = pkgRoot.getRelative("foo1");
348 Path path2 = pkgRoot.getRelative("foo2");
349 Path path3 = pkgRoot.getRelative("foo3");
350 FileSystemUtils.ensureSymbolicLink(path1, path2);
351 FileSystemUtils.ensureSymbolicLink(path2, path3);
352 FileSystemUtils.ensureSymbolicLink(path3, path1);
tomluee6a6862018-01-17 14:36:26 -0800353 SkyKey fileKey1 = FileValue.key(RootedPath.toRootedPath(Root.fromPath(pkgRoot), path1));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000354
355 EvaluationResult<SkyValue> result =
Googler10028672018-10-25 12:14:34 -0700356 driver.evaluate(ImmutableList.of(fileKey1), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200357 assertThat(result.hasError()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000358
Ulf Adamsc73051c62016-03-23 09:18:13 +0000359 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000360 Diff diff = getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000361 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
362 assertThat(diff.changedKeysWithNewValues()).isEmpty();
363 }
364
365 public void checkDirtyActions(BatchStat batchStatter, boolean forceDigests) throws Exception {
366 Artifact out1 = createDerivedArtifact("fiz");
367 Artifact out2 = createDerivedArtifact("pop");
368
369 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "hello");
370 FileSystemUtils.writeContentAsLatin1(out2.getPath(), "fizzlepop");
371
janakrbaf52ae2018-02-14 09:03:18 -0800372 ActionLookupKey actionLookupKey =
janakr573807d2018-01-11 14:02:35 -0800373 new ActionLookupKey() {
374 @Override
375 public SkyFunctionName functionName() {
376 return SkyFunctionName.FOR_TESTING;
377 }
378 };
janakrefb3f152019-06-05 17:42:34 -0700379 SkyKey actionKey1 = ActionLookupData.create(actionLookupKey, 0);
380 SkyKey actionKey2 = ActionLookupData.create(actionLookupKey, 1);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000381 differencer.inject(
382 ImmutableMap.<SkyKey, SkyValue>of(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000383 actionKey1,
384 actionValue(
385 new TestAction(
lberkic35878a2019-08-01 02:28:54 -0700386 Runnables.doNothing(), ImmutableSet.<Artifact>of(), ImmutableSet.of(out1))),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000387 actionKey2,
388 actionValue(
389 new TestAction(
lberkic35878a2019-08-01 02:28:54 -0700390 Runnables.doNothing(),
391 ImmutableSet.<Artifact>of(),
392 ImmutableSet.of(out2)))));
Googler10028672018-10-25 12:14:34 -0700393 EvaluationContext evaluationContext =
394 EvaluationContext.newBuilder()
395 .setKeepGoing(false)
396 .setNumThreads(1)
397 .setEventHander(NullEventHandler.INSTANCE)
398 .build();
399 assertThat(driver.evaluate(ImmutableList.<SkyKey>of(), evaluationContext).hasError()).isFalse();
Ulf Adamsc73051c62016-03-23 09:18:13 +0000400 assertThat(new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Eric Fellheimere6590722015-11-17 17:07:48 +0000401 batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED)).isEmpty();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000402
403 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "goodbye");
Eric Fellheimere6590722015-11-17 17:07:48 +0000404 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000405 new FilesystemValueChecker(null, null)
406 .getDirtyActionValues(
407 evaluator.getValues(), batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED))
408 .containsExactly(actionKey1);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000409 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000410 new FilesystemValueChecker(null, null)
411 .getDirtyActionValues(
412 evaluator.getValues(),
413 batchStatter,
414 new ModifiedFileSet.Builder().modify(out1.getExecPath()).build()))
415 .containsExactly(actionKey1);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000416 assertThat(
417 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Eric Fellheimere6590722015-11-17 17:07:48 +0000418 batchStatter,
419 new ModifiedFileSet.Builder().modify(
420 out1.getExecPath().getParentDirectory()).build())).isEmpty();
421 assertThat(
Ulf Adamsc73051c62016-03-23 09:18:13 +0000422 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Eric Fellheimere6590722015-11-17 17:07:48 +0000423 batchStatter, ModifiedFileSet.NOTHING_MODIFIED)).isEmpty();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000424 }
425
janakrefb3f152019-06-05 17:42:34 -0700426 private void checkDirtyTreeArtifactActions(BatchStat batchStatter) throws Exception {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000427 // Normally, an Action specifies the contents of a TreeArtifact when it executes.
428 // To decouple FileSystemValueTester checking from Action execution, we inject TreeArtifact
429 // contents into ActionExecutionValues.
430
cpeyserac09f0a2018-02-05 09:33:15 -0800431 SpecialArtifact out1 = createTreeArtifact("one");
janakrefb3f152019-06-05 17:42:34 -0700432 TreeFileArtifact file11 =
433 ActionsTestUtil.createTreeFileArtifactWithNoGeneratingAction(out1, "fizz");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000434 FileSystemUtils.createDirectoryAndParents(out1.getPath());
435 FileSystemUtils.writeContentAsLatin1(file11.getPath(), "buzz");
436
cpeyserac09f0a2018-02-05 09:33:15 -0800437 SpecialArtifact out2 = createTreeArtifact("two");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000438 FileSystemUtils.createDirectoryAndParents(out2.getPath().getChild("subdir"));
janakrefb3f152019-06-05 17:42:34 -0700439 TreeFileArtifact file21 =
440 ActionsTestUtil.createTreeFileArtifactWithNoGeneratingAction(out2, "moony");
441 TreeFileArtifact file22 =
442 ActionsTestUtil.createTreeFileArtifactWithNoGeneratingAction(out2, "subdir/wormtail");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000443 FileSystemUtils.writeContentAsLatin1(file21.getPath(), "padfoot");
444 FileSystemUtils.writeContentAsLatin1(file22.getPath(), "prongs");
445
cpeyserac09f0a2018-02-05 09:33:15 -0800446 SpecialArtifact outEmpty = createTreeArtifact("empty");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000447 FileSystemUtils.createDirectoryAndParents(outEmpty.getPath());
448
cpeyserac09f0a2018-02-05 09:33:15 -0800449 SpecialArtifact outUnchanging = createTreeArtifact("untouched");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000450 FileSystemUtils.createDirectoryAndParents(outUnchanging.getPath());
451
cpeyserac09f0a2018-02-05 09:33:15 -0800452 SpecialArtifact last = createTreeArtifact("zzzzzzzzzz");
Rumou Duan45e8e572016-06-17 16:43:44 +0000453 FileSystemUtils.createDirectoryAndParents(last.getPath());
454
janakrbaf52ae2018-02-14 09:03:18 -0800455 ActionLookupKey actionLookupKey =
janakr573807d2018-01-11 14:02:35 -0800456 new ActionLookupKey() {
457 @Override
458 public SkyFunctionName functionName() {
459 return SkyFunctionName.FOR_TESTING;
460 }
461 };
janakrefb3f152019-06-05 17:42:34 -0700462 SkyKey actionKey1 = ActionLookupData.create(actionLookupKey, 0);
463 SkyKey actionKey2 = ActionLookupData.create(actionLookupKey, 1);
464 SkyKey actionKeyEmpty = ActionLookupData.create(actionLookupKey, 2);
465 SkyKey actionKeyUnchanging = ActionLookupData.create(actionLookupKey, 3);
466 SkyKey actionKeyLast = ActionLookupData.create(actionLookupKey, 4);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000467 differencer.inject(
468 ImmutableMap.<SkyKey, SkyValue>of(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000469 actionKey1,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000470 actionValueWithTreeArtifacts(ImmutableList.of(file11)),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000471 actionKey2,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000472 actionValueWithTreeArtifacts(ImmutableList.of(file21, file22)),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000473 actionKeyEmpty,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000474 actionValueWithEmptyDirectory(outEmpty),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000475 actionKeyUnchanging,
Rumou Duan45e8e572016-06-17 16:43:44 +0000476 actionValueWithEmptyDirectory(outUnchanging),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000477 actionKeyLast,
Rumou Duan45e8e572016-06-17 16:43:44 +0000478 actionValueWithEmptyDirectory(last)));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000479
Googler10028672018-10-25 12:14:34 -0700480 EvaluationContext evaluationContext =
481 EvaluationContext.newBuilder()
482 .setKeepGoing(false)
483 .setNumThreads(1)
484 .setEventHander(NullEventHandler.INSTANCE)
485 .build();
486 assertThat(driver.evaluate(ImmutableList.<SkyKey>of(), evaluationContext).hasError()).isFalse();
Ulf Adamsc73051c62016-03-23 09:18:13 +0000487 assertThat(new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000488 batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED)).isEmpty();
489
490 // Touching the TreeArtifact directory should have no effect
491 FileSystemUtils.touchFile(out1.getPath());
492 assertThat(
Ulf Adamsc73051c62016-03-23 09:18:13 +0000493 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000494 batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED)).isEmpty();
495 // Neither should touching a subdirectory.
496 FileSystemUtils.touchFile(out2.getPath().getChild("subdir"));
497 assertThat(
Ulf Adamsc73051c62016-03-23 09:18:13 +0000498 new FilesystemValueChecker(null, null).getDirtyActionValues(evaluator.getValues(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000499 batchStatter, ModifiedFileSet.EVERYTHING_MODIFIED)).isEmpty();
500
501 /* **** Tests for directories **** */
502
503 // Removing a directory (even if empty) should have an effect
504 outEmpty.getPath().delete();
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000505 assertThat(
506 new FilesystemValueChecker(null, null)
507 .getDirtyActionValues(
508 evaluator.getValues(),
509 batchStatter,
510 new ModifiedFileSet.Builder().modify(outEmpty.getExecPath()).build()))
511 .containsExactly(actionKeyEmpty);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000512 // Symbolic links should count as dirty
513 Path dummyEmptyDir = fs.getPath("/bin").getRelative("symlink");
514 FileSystemUtils.createDirectoryAndParents(dummyEmptyDir);
515 FileSystemUtils.ensureSymbolicLink(outEmpty.getPath(), dummyEmptyDir);
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000516 assertThat(
517 new FilesystemValueChecker(null, null)
518 .getDirtyActionValues(
519 evaluator.getValues(),
520 batchStatter,
521 new ModifiedFileSet.Builder().modify(outEmpty.getExecPath()).build()))
522 .containsExactly(actionKeyEmpty);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000523
524 // We're done fiddling with this... restore the original state
525 outEmpty.getPath().delete();
jmmv5cc1f652019-03-20 09:34:08 -0700526 dummyEmptyDir.deleteTree();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000527 FileSystemUtils.createDirectoryAndParents(outEmpty.getPath());
528
529 /* **** Tests for files and directory contents ****/
530
531 // Test that file contents matter. This is covered by existing tests already,
532 // so it's just a sanity check.
533 FileSystemUtils.writeContentAsLatin1(file11.getPath(), "goodbye");
Ulf Adamsc73051c62016-03-23 09:18:13 +0000534 assertThat(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000535 new FilesystemValueChecker(null, null)
536 .getDirtyActionValues(
537 evaluator.getValues(),
538 batchStatter,
539 new ModifiedFileSet.Builder().modify(file11.getExecPath()).build()))
540 .containsExactly(actionKey1);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000541
542 // Test that directory contents (and nested contents) matter
janakrefb3f152019-06-05 17:42:34 -0700543 Artifact out1new =
544 ActionsTestUtil.createTreeFileArtifactWithNoGeneratingAction(out1, "julius/caesar");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000545 FileSystemUtils.createDirectoryAndParents(out1.getPath().getChild("julius"));
546 FileSystemUtils.writeContentAsLatin1(out1new.getPath(), "octavian");
547 // even for empty directories
janakrefb3f152019-06-05 17:42:34 -0700548 Artifact outEmptyNew =
549 ActionsTestUtil.createTreeFileArtifactWithNoGeneratingAction(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();
janakraea05602019-05-22 15:41:29 -0700629 return ActionsTestUtil.createArtifact(
630 ArtifactRoot.asDerivedRoot(fs.getPath("/"), outputPath), outputPath.getRelative(relPath));
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)),
janakrefb3f152019-06-05 17:42:34 -0700641 ActionsTestUtil.NULL_ARTIFACT_OWNER,
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
lberkic35878a2019-08-01 02:28:54 -0700730 private ActionExecutionValue actionValue(Action action) {
lberkif7eee1e2019-07-31 05:49:10 -0700731 Map<Artifact, FileArtifactValue> artifactData = new HashMap<>();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000732 for (Artifact output : action.getOutputs()) {
733 try {
734 Path path = output.getPath();
lberkic35878a2019-08-01 02:28:54 -0700735 FileArtifactValue noDigest =
736 ActionMetadataHandler.fileArtifactValueFromArtifact(
737 output,
738 FileStatusWithDigestAdapter.adapt(path.statIfFound(Symlinks.NOFOLLOW)),
739 null);
740 FileArtifactValue withDigest =
741 FileArtifactValue.createFromInjectedDigest(
742 noDigest, path.getDigest(), !output.isConstantMetadata());
743 artifactData.put(output, withDigest);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000744 } catch (IOException e) {
745 throw new IllegalStateException(e);
746 }
747 }
janakrb9d8d582018-06-13 21:57:19 -0700748 return ActionExecutionValue.create(
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000749 artifactData,
750 ImmutableMap.<Artifact, TreeArtifactValue>of(),
janakrb9d8d582018-06-13 21:57:19 -0700751 /*outputSymlinks=*/ null,
shahanef6f4cf2018-06-26 11:24:59 -0700752 /*discoveredModules=*/ null,
janakr9f496f32018-10-24 15:08:09 -0700753 /*actionDependsOnBuildId=*/ false);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000754 }
755
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000756 private ActionExecutionValue actionValueWithEmptyDirectory(Artifact emptyDir) {
757 TreeArtifactValue emptyValue = TreeArtifactValue.create
Rumou Duana77f32c2016-04-13 21:59:21 +0000758 (ImmutableMap.<TreeFileArtifact, FileArtifactValue>of());
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000759
janakrb9d8d582018-06-13 21:57:19 -0700760 return ActionExecutionValue.create(
janakr0c42fc82018-09-14 10:37:25 -0700761 ImmutableMap.of(),
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000762 ImmutableMap.of(emptyDir, emptyValue),
janakrb9d8d582018-06-13 21:57:19 -0700763 /*outputSymlinks=*/ null,
shahanef6f4cf2018-06-26 11:24:59 -0700764 /*discoveredModules=*/ null,
janakr9f496f32018-10-24 15:08:09 -0700765 /*actionDependsOnBuildId=*/ false);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000766 }
767
Rumou Duana77f32c2016-04-13 21:59:21 +0000768 private ActionExecutionValue actionValueWithTreeArtifacts(List<TreeFileArtifact> contents) {
lberkif7eee1e2019-07-31 05:49:10 -0700769 Map<Artifact, FileArtifactValue> fileData = new HashMap<>();
Rumou Duana77f32c2016-04-13 21:59:21 +0000770 Map<Artifact, Map<TreeFileArtifact, FileArtifactValue>> directoryData = new HashMap<>();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000771
Rumou Duana77f32c2016-04-13 21:59:21 +0000772 for (TreeFileArtifact output : contents) {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000773 try {
Rumou Duana77f32c2016-04-13 21:59:21 +0000774 Map<TreeFileArtifact, FileArtifactValue> dirDatum =
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000775 directoryData.get(output.getParent());
776 if (dirDatum == null) {
777 dirDatum = new HashMap<>();
778 directoryData.put(output.getParent(), dirDatum);
779 }
lberkic35878a2019-08-01 02:28:54 -0700780 Path path = output.getPath();
781 FileArtifactValue noDigest =
782 ActionMetadataHandler.fileArtifactValueFromArtifact(
783 output,
784 FileStatusWithDigestAdapter.adapt(path.statIfFound(Symlinks.NOFOLLOW)),
785 null);
786 FileArtifactValue withDigest =
787 FileArtifactValue.createFromInjectedDigest(
788 noDigest, path.getDigest(), !output.isConstantMetadata());
789 dirDatum.put(output, withDigest);
790 fileData.put(output, withDigest);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000791 } catch (IOException e) {
792 throw new IllegalStateException(e);
793 }
794 }
795
796 Map<Artifact, TreeArtifactValue> treeArtifactData = new HashMap<>();
Rumou Duana77f32c2016-04-13 21:59:21 +0000797 for (Map.Entry<Artifact, Map<TreeFileArtifact, FileArtifactValue>> dirDatum :
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000798 directoryData.entrySet()) {
Rumou Duana77f32c2016-04-13 21:59:21 +0000799 treeArtifactData.put(dirDatum.getKey(), TreeArtifactValue.create(dirDatum.getValue()));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000800 }
801
janakrb9d8d582018-06-13 21:57:19 -0700802 return ActionExecutionValue.create(
kush2ce45a22018-05-02 14:15:37 -0700803 fileData,
804 treeArtifactData,
janakrb9d8d582018-06-13 21:57:19 -0700805 /*outputSymlinks=*/ null,
shahanef6f4cf2018-06-26 11:24:59 -0700806 /*discoveredModules=*/ null,
janakr9f496f32018-10-24 15:08:09 -0700807 /*actionDependsOnBuildId=*/ false);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000808 }
809
buchgr4992ae22019-03-20 04:23:32 -0700810 private ActionExecutionValue actionValueWithRemoteTreeArtifact(
811 SpecialArtifact output, Map<PathFragment, RemoteFileArtifactValue> children) {
812 ImmutableMap.Builder<TreeFileArtifact, FileArtifactValue> childFileValues =
813 ImmutableMap.builder();
814 for (Map.Entry<PathFragment, RemoteFileArtifactValue> child : children.entrySet()) {
815 childFileValues.put(
janakrefb3f152019-06-05 17:42:34 -0700816 ActionInputHelper.treeFileArtifactWithNoGeneratingActionSet(
817 output, child.getKey(), output.getArtifactOwner()),
818 child.getValue());
buchgr4992ae22019-03-20 04:23:32 -0700819 }
820 TreeArtifactValue treeArtifactValue = TreeArtifactValue.create(childFileValues.build());
821 return ActionExecutionValue.create(
buchgr4992ae22019-03-20 04:23:32 -0700822 ImmutableMap.of(),
lberkic35878a2019-08-01 02:28:54 -0700823 Collections.singletonMap(output, treeArtifactValue),
buchgr4992ae22019-03-20 04:23:32 -0700824 /* outputSymlinks= */ null,
825 /* discoveredModules= */ null,
826 /* actionDependsOnBuildId= */ false);
827 }
828
829 private ActionExecutionValue actionValueWithRemoteArtifact(
830 Artifact output, RemoteFileArtifactValue value) {
831 return ActionExecutionValue.create(
buchgr4992ae22019-03-20 04:23:32 -0700832 Collections.singletonMap(output, value),
lberkic35878a2019-08-01 02:28:54 -0700833 ImmutableMap.of(),
buchgr4992ae22019-03-20 04:23:32 -0700834 /* outputSymlinks= */ null,
835 /* discoveredModules= */ null,
836 /* actionDependsOnBuildId= */ false);
837 }
838
839 private RemoteFileArtifactValue createRemoteFileArtifactValue(String contents) {
840 byte[] data = contents.getBytes();
841 DigestHashFunction hashFn = fs.getDigestFunction();
842 HashCode hash = hashFn.getHashFunction().hashBytes(data);
843 return new RemoteFileArtifactValue(hash.asBytes(), data.length, -1);
844 }
845
846 @Test
847 public void testRemoteAndLocalArtifacts() throws Exception {
848 // Test that injected remote artifacts are trusted by the FileSystemValueChecker
849 // and that local files always takes preference over remote files.
850 ActionLookupKey actionLookupKey =
851 new ActionLookupKey() {
852 @Override
853 public SkyFunctionName functionName() {
854 return SkyFunctionName.FOR_TESTING;
855 }
856 };
janakrefb3f152019-06-05 17:42:34 -0700857 SkyKey actionKey1 = ActionLookupData.create(actionLookupKey, 0);
858 SkyKey actionKey2 = ActionLookupData.create(actionLookupKey, 1);
buchgr4992ae22019-03-20 04:23:32 -0700859
860 Artifact out1 = createDerivedArtifact("foo");
861 Artifact out2 = createDerivedArtifact("bar");
862 Map<SkyKey, SkyValue> metadataToInject = new HashMap<>();
863 metadataToInject.put(
864 actionKey1,
865 actionValueWithRemoteArtifact(out1, createRemoteFileArtifactValue("foo-content")));
866 metadataToInject.put(
867 actionKey2,
868 actionValueWithRemoteArtifact(out2, createRemoteFileArtifactValue("bar-content")));
869 differencer.inject(metadataToInject);
870
871 EvaluationContext evaluationContext =
872 EvaluationContext.newBuilder()
873 .setKeepGoing(false)
874 .setNumThreads(1)
875 .setEventHander(NullEventHandler.INSTANCE)
876 .build();
877 assertThat(
878 driver.evaluate(ImmutableList.of(actionKey1, actionKey2), evaluationContext).hasError())
879 .isFalse();
880 assertThat(
881 new FilesystemValueChecker(/* tsgm= */ null, /* lastExecutionTimeRange= */ null)
882 .getDirtyActionValues(
883 evaluator.getValues(),
884 /* batchStatter= */ null,
885 ModifiedFileSet.EVERYTHING_MODIFIED))
886 .isEmpty();
887
888 // Create the "out1" artifact on the filesystem and test that it invalidates the generating
889 // action's SkyKey.
890 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "new-foo-content");
891 assertThat(
892 new FilesystemValueChecker(/* tsgm= */ null, /* lastExecutionTimeRange= */ null)
893 .getDirtyActionValues(
894 evaluator.getValues(),
895 /* batchStatter= */ null,
896 ModifiedFileSet.EVERYTHING_MODIFIED))
897 .containsExactly(actionKey1);
898 }
899
900 @Test
901 public void testRemoteAndLocalTreeArtifacts() throws Exception {
902 // Test that injected remote tree artifacts are trusted by the FileSystemValueChecker
903 // and that local files always takes preference over remote files.
904 ActionLookupKey actionLookupKey =
905 new ActionLookupKey() {
906 @Override
907 public SkyFunctionName functionName() {
908 return SkyFunctionName.FOR_TESTING;
909 }
910 };
janakrefb3f152019-06-05 17:42:34 -0700911 SkyKey actionKey = ActionLookupData.create(actionLookupKey, 0);
buchgr4992ae22019-03-20 04:23:32 -0700912
913 SpecialArtifact treeArtifact = createTreeArtifact("dir");
914 treeArtifact.getPath().createDirectoryAndParents();
915 Map<PathFragment, RemoteFileArtifactValue> treeArtifactMetadata = new HashMap<>();
916 treeArtifactMetadata.put(
917 PathFragment.create("foo"), createRemoteFileArtifactValue("foo-content"));
918 treeArtifactMetadata.put(
919 PathFragment.create("bar"), createRemoteFileArtifactValue("bar-content"));
920
921 Map<SkyKey, SkyValue> metadataToInject = new HashMap<>();
922 metadataToInject.put(
923 actionKey, actionValueWithRemoteTreeArtifact(treeArtifact, treeArtifactMetadata));
924 differencer.inject(metadataToInject);
925
926 EvaluationContext evaluationContext =
927 EvaluationContext.newBuilder()
928 .setKeepGoing(false)
929 .setNumThreads(1)
930 .setEventHander(NullEventHandler.INSTANCE)
931 .build();
932 assertThat(driver.evaluate(ImmutableList.of(actionKey), evaluationContext).hasError())
933 .isFalse();
934 assertThat(
935 new FilesystemValueChecker(/* tsgm= */ null, /* lastExecutionTimeRange= */ null)
936 .getDirtyActionValues(
937 evaluator.getValues(),
938 /* batchStatter= */ null,
939 ModifiedFileSet.EVERYTHING_MODIFIED))
940 .isEmpty();
941
942 // Create dir/foo on the local disk and test that it invalidates the associated sky key.
janakrefb3f152019-06-05 17:42:34 -0700943 TreeFileArtifact fooArtifact =
944 ActionsTestUtil.createTreeFileArtifactWithNoGeneratingAction(treeArtifact, "foo");
buchgr4992ae22019-03-20 04:23:32 -0700945 FileSystemUtils.writeContentAsLatin1(fooArtifact.getPath(), "new-foo-content");
946 assertThat(
947 new FilesystemValueChecker(/* tsgm= */ null, /* lastExecutionTimeRange= */ null)
948 .getDirtyActionValues(
949 evaluator.getValues(),
950 /* batchStatter= */ null,
951 ModifiedFileSet.EVERYTHING_MODIFIED))
952 .containsExactly(actionKey);
953 }
954
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000955 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000956 public void testPropagatesRuntimeExceptions() throws Exception {
tomluee6a6862018-01-17 14:36:26 -0800957 Collection<SkyKey> values =
958 ImmutableList.of(
959 FileValue.key(
960 RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo"))));
Googler10028672018-10-25 12:14:34 -0700961 driver.evaluate(values, EVALUATION_OPTIONS);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000962 FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000963
Nathan Harmata8cd29782015-11-10 03:24:01 +0000964 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000965
966 fs.statThrowsRuntimeException = true;
jcater83130f42019-04-30 14:29:28 -0700967 RuntimeException e =
968 assertThrows(RuntimeException.class, () -> getDirtyFilesystemKeys(evaluator, checker));
969 assertThat(e).hasMessageThat().isEqualTo("bork");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000970 }
971
972 private static void assertEmptyDiff(Diff diff) {
973 assertDiffWithNewValues(diff);
974 }
975
976 private static void assertDiffWithNewValues(Diff diff, SkyKey... keysWithNewValues) {
977 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
978 assertThat(diff.changedKeysWithNewValues().keySet())
979 .containsExactlyElementsIn(Arrays.asList(keysWithNewValues));
980 }
981
982 private class MockFileSystem extends InMemoryFileSystem {
983
984 boolean statThrowsRuntimeException;
985 boolean readlinkThrowsIoException;
986
987 MockFileSystem() {
ccalvarinc9efd062018-07-27 12:46:46 -0700988 super();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000989 }
990
991 @Override
fellya205ed82018-09-10 11:52:34 -0700992 public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000993 if (statThrowsRuntimeException) {
994 throw new RuntimeException("bork");
995 }
fellya205ed82018-09-10 11:52:34 -0700996 return super.statIfFound(path, followSymlinks);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000997 }
998
999 @Override
aehligc801c392017-12-19 07:12:25 -08001000 protected PathFragment readSymbolicLink(Path path) throws IOException {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001001 if (readlinkThrowsIoException) {
1002 throw new IOException("readlink failed");
1003 }
1004 return super.readSymbolicLink(path);
1005 }
1006 }
1007
1008 private static FileStatusWithDigest statWithDigest(final Path path, final FileStatus stat) {
1009 return new FileStatusWithDigest() {
1010 @Nullable
1011 @Override
1012 public byte[] getDigest() throws IOException {
olaolabfd1d332017-06-19 16:55:24 -04001013 return path.getDigest();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001014 }
1015
1016 @Override
1017 public boolean isFile() {
1018 return stat.isFile();
1019 }
1020
1021 @Override
1022 public boolean isSpecialFile() {
1023 return stat.isSpecialFile();
1024 }
1025
1026 @Override
1027 public boolean isDirectory() {
1028 return stat.isDirectory();
1029 }
1030
1031 @Override
1032 public boolean isSymbolicLink() {
1033 return stat.isSymbolicLink();
1034 }
1035
1036 @Override
1037 public long getSize() throws IOException {
1038 return stat.getSize();
1039 }
1040
1041 @Override
1042 public long getLastModifiedTime() throws IOException {
1043 return stat.getLastModifiedTime();
1044 }
1045
1046 @Override
1047 public long getLastChangeTime() throws IOException {
1048 return stat.getLastChangeTime();
1049 }
1050
1051 @Override
1052 public long getNodeId() throws IOException {
1053 return stat.getNodeId();
1054 }
1055 };
1056 }
1057
Nathan Harmata8cd29782015-11-10 03:24:01 +00001058 private static Diff getDirtyFilesystemKeys(MemoizingEvaluator evaluator,
1059 FilesystemValueChecker checker) throws InterruptedException {
1060 return checker.getDirtyKeys(evaluator.getValues(), new BasicFilesystemDirtinessChecker());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001061 }
1062}