blob: ad9354b30387fab201b010d49ed99a7632916568 [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;
michajlo660d17f2020-03-27 09:01:57 -070018import static org.junit.Assert.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;
janakrefb3f152019-06-05 17:42:34 -070026import com.google.devtools.build.lib.actions.ActionLookupData;
janakr93e3eea2017-03-30 22:09:37 +000027import com.google.devtools.build.lib.actions.ActionLookupValue.ActionLookupKey;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000028import com.google.devtools.build.lib.actions.Artifact;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000029import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
Rumou Duana77f32c2016-04-13 21:59:21 +000030import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
tomlu1cdcdf92018-01-16 11:07:51 -080031import com.google.devtools.build.lib.actions.ArtifactRoot;
shahan602cc852018-06-06 20:09:57 -070032import com.google.devtools.build.lib.actions.FileArtifactValue;
buchgr4992ae22019-03-20 04:23:32 -070033import com.google.devtools.build.lib.actions.FileArtifactValue.RemoteFileArtifactValue;
shahan602cc852018-06-06 20:09:57 -070034import com.google.devtools.build.lib.actions.FileStateValue;
35import com.google.devtools.build.lib.actions.FileValue;
janakraea05602019-05-22 15:41:29 -070036import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000037import com.google.devtools.build.lib.actions.util.TestAction;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000038import com.google.devtools.build.lib.analysis.BlazeDirectories;
janakr3b63a4e2017-09-14 09:55:40 +020039import com.google.devtools.build.lib.analysis.ServerDirectories;
lberki41cfcc72019-08-05 02:55:36 -070040import com.google.devtools.build.lib.clock.BlazeClock;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000041import com.google.devtools.build.lib.cmdline.PackageIdentifier;
ulfjack1e1a7752019-12-10 21:17:58 -080042import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
43import com.google.devtools.build.lib.collect.nestedset.Order;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000044import com.google.devtools.build.lib.events.NullEventHandler;
janakr15e15c22019-01-30 11:24:49 -080045import com.google.devtools.build.lib.packages.WorkspaceFileValue;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000046import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
47import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.BasicFilesystemDirtinessChecker;
Nathan Harmatad4f75942016-10-18 08:55:17 +000048import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
John Cater5e9ce942016-10-12 17:23:30 +000049import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +000050import com.google.devtools.build.lib.testutil.TestConstants;
michajlo8083e322020-03-20 13:32:52 -070051import com.google.devtools.build.lib.testutil.TestPackageFactoryBuilderFactory;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000052import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
laszlocsomor29bdf632018-03-13 02:50:46 -070053import com.google.devtools.build.lib.testutil.TimestampGranularityUtils;
Laszlo Csomora278aec2018-03-09 04:07:31 -080054import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000055import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
56import com.google.devtools.build.lib.vfs.BatchStat;
buchgr4992ae22019-03-20 04:23:32 -070057import com.google.devtools.build.lib.vfs.DigestHashFunction;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000058import com.google.devtools.build.lib.vfs.FileStatus;
59import com.google.devtools.build.lib.vfs.FileStatusWithDigest;
60import com.google.devtools.build.lib.vfs.FileStatusWithDigestAdapter;
61import com.google.devtools.build.lib.vfs.FileSystemUtils;
Eric Fellheimere6590722015-11-17 17:07:48 +000062import com.google.devtools.build.lib.vfs.ModifiedFileSet;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000063import com.google.devtools.build.lib.vfs.Path;
64import com.google.devtools.build.lib.vfs.PathFragment;
tomluee6a6862018-01-17 14:36:26 -080065import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000066import com.google.devtools.build.lib.vfs.RootedPath;
67import com.google.devtools.build.lib.vfs.Symlinks;
djasper7faa0ef2019-03-28 10:00:00 -070068import com.google.devtools.build.lib.vfs.UnixGlob;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000069import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
70import com.google.devtools.build.skyframe.Differencer.Diff;
Googler10028672018-10-25 12:14:34 -070071import com.google.devtools.build.skyframe.EvaluationContext;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000072import com.google.devtools.build.skyframe.EvaluationResult;
73import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
74import com.google.devtools.build.skyframe.MemoizingEvaluator;
75import com.google.devtools.build.skyframe.RecordingDifferencer;
janakr1cde8722017-10-10 03:22:21 +020076import com.google.devtools.build.skyframe.SequencedRecordingDifferencer;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000077import com.google.devtools.build.skyframe.SequentialBuildDriver;
78import com.google.devtools.build.skyframe.SkyFunction;
79import com.google.devtools.build.skyframe.SkyFunctionName;
80import com.google.devtools.build.skyframe.SkyKey;
81import com.google.devtools.build.skyframe.SkyValue;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000082import java.io.IOException;
83import java.util.ArrayList;
84import java.util.Arrays;
85import java.util.Collection;
buchgr4992ae22019-03-20 04:23:32 -070086import java.util.Collections;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000087import java.util.HashMap;
88import java.util.List;
89import java.util.Map;
90import java.util.UUID;
91import java.util.concurrent.atomic.AtomicReference;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000092import javax.annotation.Nullable;
Janak Ramakrishnancba16452016-07-29 02:17:02 +000093import org.junit.Before;
steinman7184cb32020-04-20 14:33:03 -070094import org.junit.Ignore;
Janak Ramakrishnancba16452016-07-29 02:17:02 +000095import org.junit.Test;
96import org.junit.runner.RunWith;
97import org.junit.runners.JUnit4;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000098
99/**
100 * Tests for {@link FilesystemValueChecker}.
101 */
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000102@RunWith(JUnit4.class)
103public class FilesystemValueCheckerTest {
Googler10028672018-10-25 12:14:34 -0700104 private static final EvaluationContext EVALUATION_OPTIONS =
105 EvaluationContext.newBuilder()
106 .setKeepGoing(false)
107 .setNumThreads(SkyframeExecutor.DEFAULT_THREAD_COUNT)
108 .setEventHander(NullEventHandler.INSTANCE)
109 .build();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000110
111 private RecordingDifferencer differencer;
112 private MemoizingEvaluator evaluator;
113 private SequentialBuildDriver driver;
114 private MockFileSystem fs;
115 private Path pkgRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000116
twerth1fa34df2020-04-09 03:21:02 -0700117 private static final int FSVC_THREADS_FOR_TEST = 200;
twerth5aaceb52020-04-07 06:31:56 -0700118
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000119 @Before
Florian Weikert92b22362015-12-03 10:17:18 +0000120 public final void setUp() throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000121 ImmutableMap.Builder<SkyFunctionName, SkyFunction> skyFunctions = ImmutableMap.builder();
122
123 fs = new MockFileSystem();
124 pkgRoot = fs.getPath("/testroot");
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000125 FileSystemUtils.createDirectoryAndParents(pkgRoot);
126 FileSystemUtils.createEmptyFile(pkgRoot.getRelative("WORKSPACE"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000127
John Catere0d1d0e2017-11-28 20:47:41 -0800128 AtomicReference<PathPackageLocator> pkgLocator =
129 new AtomicReference<>(
130 new PathPackageLocator(
131 fs.getPath("/output_base"),
tomluee6a6862018-01-17 14:36:26 -0800132 ImmutableList.of(Root.fromPath(pkgRoot)),
John Catere0d1d0e2017-11-28 20:47:41 -0800133 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
janakr3b63a4e2017-09-14 09:55:40 +0200134 BlazeDirectories directories =
135 new BlazeDirectories(
cushon849df362018-05-14 01:51:45 -0700136 new ServerDirectories(pkgRoot, pkgRoot, pkgRoot),
137 pkgRoot,
138 /* defaultSystemJavabase= */ null,
139 TestConstants.PRODUCT_NAME);
nharmata3fb7d342018-02-23 11:37:51 -0800140 ExternalFilesHelper externalFilesHelper = ExternalFilesHelper.createForTesting(
Nathan Harmatad4f75942016-10-18 08:55:17 +0000141 pkgLocator, ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS, directories);
shahan602cc852018-06-06 20:09:57 -0700142 skyFunctions.put(
143 FileStateValue.FILE_STATE,
144 new FileStateFunction(
djasper7faa0ef2019-03-28 10:00:00 -0700145 new AtomicReference<TimestampGranularityMonitor>(),
146 new AtomicReference<>(UnixGlob.DEFAULT_SYSCALLS),
147 externalFilesHelper));
shahan602cc852018-06-06 20:09:57 -0700148 skyFunctions.put(FileValue.FILE, new FileFunction(pkgLocator));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000149 skyFunctions.put(
150 SkyFunctions.FILE_SYMLINK_CYCLE_UNIQUENESS, new FileSymlinkCycleUniquenessFunction());
151 skyFunctions.put(
152 SkyFunctions.FILE_SYMLINK_INFINITE_EXPANSION_UNIQUENESS,
153 new FileSymlinkInfiniteExpansionUniquenessFunction());
ajurkowski2018b872020-04-14 09:59:00 -0700154 skyFunctions.put(
155 SkyFunctions.PACKAGE, new PackageFunction(null, null, null, null, null, null, null, null));
John Cater5e9ce942016-10-12 17:23:30 +0000156 skyFunctions.put(
157 SkyFunctions.PACKAGE_LOOKUP,
158 new PackageLookupFunction(
159 new AtomicReference<>(ImmutableSet.<PackageIdentifier>of()),
John Cater0c0735a2016-11-11 01:52:02 +0000160 CrossRepositoryLabelViolationStrategy.ERROR,
ajurkowski2018b872020-04-14 09:59:00 -0700161 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY,
162 BazelSkyframeExecutorConstants.EXTERNAL_PACKAGE_HELPER));
Damien Martin-Guillerez4ecfe512016-01-22 11:03:23 +0000163 skyFunctions.put(SkyFunctions.WORKSPACE_AST,
164 new WorkspaceASTFunction(TestRuleClassProvider.getRuleClassProvider()));
carmid6a98282018-03-13 19:19:16 -0700165 skyFunctions.put(
janakr15e15c22019-01-30 11:24:49 -0800166 WorkspaceFileValue.WORKSPACE_FILE,
carmid6a98282018-03-13 19:19:16 -0700167 new WorkspaceFileFunction(
168 TestRuleClassProvider.getRuleClassProvider(),
michajlo8083e322020-03-20 13:32:52 -0700169 TestPackageFactoryBuilderFactory.getInstance()
carmid6a98282018-03-13 19:19:16 -0700170 .builder(directories)
nharmatafde0bd2f2018-12-21 10:17:56 -0800171 .build(TestRuleClassProvider.getRuleClassProvider(), fs),
mjhalupkaf0e48112019-01-14 13:01:56 -0800172 directories,
brandjon771a0292020-05-26 12:04:16 -0700173 /*bzlLoadFunctionForInlining=*/ null));
ajurkowski2018b872020-04-14 09:59:00 -0700174 skyFunctions.put(
175 SkyFunctions.EXTERNAL_PACKAGE,
176 new ExternalPackageFunction(BazelSkyframeExecutorConstants.EXTERNAL_PACKAGE_HELPER));
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000177
janakr1cde8722017-10-10 03:22:21 +0200178 differencer = new SequencedRecordingDifferencer();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000179 evaluator = new InMemoryMemoizingEvaluator(skyFunctions.build(), differencer);
180 driver = new SequentialBuildDriver(evaluator);
181 PrecomputedValue.BUILD_ID.set(differencer, UUID.randomUUID());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000182 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000183 }
184
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000185 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000186 public void testEmpty() throws Exception {
twerth5aaceb52020-04-07 06:31:56 -0700187 FilesystemValueChecker checker =
188 new FilesystemValueChecker(
189 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000190 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000191 }
192
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000193 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000194 public void testSimple() throws Exception {
twerth5aaceb52020-04-07 06:31:56 -0700195 FilesystemValueChecker checker =
196 new FilesystemValueChecker(
197 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000198
199 Path path = fs.getPath("/foo");
200 FileSystemUtils.createEmptyFile(path);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000201 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000202
tomluee6a6862018-01-17 14:36:26 -0800203 SkyKey skyKey =
204 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800205 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000206 EvaluationResult<SkyValue> result =
Googler10028672018-10-25 12:14:34 -0700207 driver.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200208 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000209
Nathan Harmata8cd29782015-11-10 03:24:01 +0000210 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000211
212 FileSystemUtils.writeContentAsLatin1(path, "hello");
Nathan Harmata8cd29782015-11-10 03:24:01 +0000213 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000214
215 // The dirty bits are not reset until the FileValues are actually revalidated.
Nathan Harmata8cd29782015-11-10 03:24:01 +0000216 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000217
218 differencer.invalidate(ImmutableList.of(skyKey));
Googler10028672018-10-25 12:14:34 -0700219 result = driver.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200220 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000221 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000222 }
223
224 /**
225 * Tests that an already-invalidated value can still be marked changed: symlink points at sym1.
226 * Invalidate symlink by changing sym1 from pointing at path to point to sym2. This only dirties
227 * (rather than changes) symlink because sym2 still points at path, so all symlink stats remain
228 * the same. Then do a null build, change sym1 back to point at path, and change symlink to not be
229 * a symlink anymore. The fact that it is not a symlink should be detected.
230 */
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000231 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000232 public void testDirtySymlink() throws Exception {
twerth5aaceb52020-04-07 06:31:56 -0700233 FilesystemValueChecker checker =
234 new FilesystemValueChecker(
235 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000236
237 Path path = fs.getPath("/foo");
238 FileSystemUtils.writeContentAsLatin1(path, "foo contents");
239 // We need the intermediate sym1 and sym2 so that we can dirty a child of symlink without
240 // actually changing the FileValue calculated for symlink (if we changed the contents of foo,
Laszlo Csomora278aec2018-03-09 04:07:31 -0800241 // the FileValue created for symlink would notice, since it stats foo).
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000242 Path sym1 = fs.getPath("/sym1");
243 Path sym2 = fs.getPath("/sym2");
244 Path symlink = fs.getPath("/bar");
245 FileSystemUtils.ensureSymbolicLink(symlink, sym1);
246 FileSystemUtils.ensureSymbolicLink(sym1, path);
247 FileSystemUtils.ensureSymbolicLink(sym2, path);
248 SkyKey fooKey =
tomlu4c9fafd2018-01-18 10:29:11 -0800249 FileValue.key(RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000250 RootedPath symlinkRootedPath =
tomlu4c9fafd2018-01-18 10:29:11 -0800251 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/bar"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000252 SkyKey symlinkKey = FileValue.key(symlinkRootedPath);
253 SkyKey symlinkFileStateKey = FileStateValue.key(symlinkRootedPath);
254 RootedPath sym1RootedPath =
tomlu4c9fafd2018-01-18 10:29:11 -0800255 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/sym1"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000256 SkyKey sym1FileStateKey = FileStateValue.key(sym1RootedPath);
257 Iterable<SkyKey> allKeys = ImmutableList.of(symlinkKey, fooKey);
258
259 // First build -- prime the graph.
Googler10028672018-10-25 12:14:34 -0700260 EvaluationResult<FileValue> result = driver.evaluate(allKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200261 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000262 FileValue symlinkValue = result.get(symlinkKey);
263 FileValue fooValue = result.get(fooKey);
lberkiaea56b32017-05-30 12:35:33 +0200264 assertWithMessage(symlinkValue.toString()).that(symlinkValue.isSymlink()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000265 // Digest is not always available, so use size as a proxy for contents.
lberkiaea56b32017-05-30 12:35:33 +0200266 assertThat(symlinkValue.getSize()).isEqualTo(fooValue.getSize());
Nathan Harmata8cd29782015-11-10 03:24:01 +0000267 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000268
269 // Before second build, move sym1 to point to sym2.
lberkiaea56b32017-05-30 12:35:33 +0200270 assertThat(sym1.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000271 FileSystemUtils.ensureSymbolicLink(sym1, sym2);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000272 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000273
274 differencer.invalidate(ImmutableList.of(sym1FileStateKey));
Googler10028672018-10-25 12:14:34 -0700275 result = driver.evaluate(ImmutableList.<SkyKey>of(), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200276 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000277 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000278
279 // Before third build, move sym1 back to original (so change pruning will prevent signaling of
280 // its parents, but change symlink for real.
lberkiaea56b32017-05-30 12:35:33 +0200281 assertThat(sym1.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000282 FileSystemUtils.ensureSymbolicLink(sym1, path);
lberkiaea56b32017-05-30 12:35:33 +0200283 assertThat(symlink.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000284 FileSystemUtils.writeContentAsLatin1(symlink, "new symlink contents");
Nathan Harmata8cd29782015-11-10 03:24:01 +0000285 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), symlinkFileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000286 differencer.invalidate(ImmutableList.of(symlinkFileStateKey));
Googler10028672018-10-25 12:14:34 -0700287 result = driver.evaluate(allKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200288 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000289 symlinkValue = result.get(symlinkKey);
lberkiaea56b32017-05-30 12:35:33 +0200290 assertWithMessage(symlinkValue.toString()).that(symlinkValue.isSymlink()).isFalse();
291 assertThat(result.get(fooKey)).isEqualTo(fooValue);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000292 assertThat(symlinkValue.getSize()).isNotEqualTo(fooValue.getSize());
Nathan Harmata8cd29782015-11-10 03:24:01 +0000293 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000294 }
295
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000296 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000297 public void testExplicitFiles() throws Exception {
twerth5aaceb52020-04-07 06:31:56 -0700298 FilesystemValueChecker checker =
299 new FilesystemValueChecker(
300 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000301
302 Path path1 = fs.getPath("/foo1");
303 Path path2 = fs.getPath("/foo2");
304 FileSystemUtils.createEmptyFile(path1);
305 FileSystemUtils.createEmptyFile(path2);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000306 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000307
308 SkyKey key1 =
309 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800310 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo1")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000311 SkyKey key2 =
312 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800313 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo2")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000314 Iterable<SkyKey> skyKeys = ImmutableList.of(key1, key2);
Googler10028672018-10-25 12:14:34 -0700315 EvaluationResult<SkyValue> result = driver.evaluate(skyKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200316 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000317
Nathan Harmata8cd29782015-11-10 03:24:01 +0000318 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000319
Laszlo Csomora278aec2018-03-09 04:07:31 -0800320 // Wait for the timestamp granularity to elapse, so updating the files will observably advance
321 // their ctime.
laszlocsomor29bdf632018-03-13 02:50:46 -0700322 TimestampGranularityUtils.waitForTimestampGranularity(
323 System.currentTimeMillis(), OutErr.SYSTEM_OUT_ERR);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800324 // Update path1's contents and mtime. This will update the file's ctime.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000325 FileSystemUtils.writeContentAsLatin1(path1, "hello1");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000326 path1.setLastModifiedTime(27);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800327 // Update path2's mtime but not its contents. We expect that an mtime change suffices to update
328 // the ctime.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000329 path2.setLastModifiedTime(42);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800330 // Assert that both files changed. The change detection relies, among other things, on ctime
331 // change.
Nathan Harmata8cd29782015-11-10 03:24:01 +0000332 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), key1, key2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000333
334 differencer.invalidate(skyKeys);
Googler10028672018-10-25 12:14:34 -0700335 result = driver.evaluate(skyKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200336 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000337 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000338 }
339
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000340 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000341 public void testFileWithIOExceptionNotConsideredDirty() throws Exception {
342 Path path = fs.getPath("/testroot/foo");
343 path.getParentDirectory().createDirectory();
nharmatab4060b62017-04-04 17:11:39 +0000344 path.createSymbolicLink(PathFragment.create("bar"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000345
346 fs.readlinkThrowsIoException = true;
tomluee6a6862018-01-17 14:36:26 -0800347 SkyKey fileKey =
348 FileStateValue.key(
349 RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000350 EvaluationResult<SkyValue> result =
Googler10028672018-10-25 12:14:34 -0700351 driver.evaluate(ImmutableList.of(fileKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200352 assertThat(result.hasError()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000353
354 fs.readlinkThrowsIoException = false;
twerth5aaceb52020-04-07 06:31:56 -0700355 FilesystemValueChecker checker =
356 new FilesystemValueChecker(
357 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000358 Diff diff = getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000359 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
360 assertThat(diff.changedKeysWithNewValues()).isEmpty();
361 }
362
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000363 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000364 public void testFilesInCycleNotConsideredDirty() throws Exception {
365 Path path1 = pkgRoot.getRelative("foo1");
366 Path path2 = pkgRoot.getRelative("foo2");
367 Path path3 = pkgRoot.getRelative("foo3");
368 FileSystemUtils.ensureSymbolicLink(path1, path2);
369 FileSystemUtils.ensureSymbolicLink(path2, path3);
370 FileSystemUtils.ensureSymbolicLink(path3, path1);
tomluee6a6862018-01-17 14:36:26 -0800371 SkyKey fileKey1 = FileValue.key(RootedPath.toRootedPath(Root.fromPath(pkgRoot), path1));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000372
373 EvaluationResult<SkyValue> result =
Googler10028672018-10-25 12:14:34 -0700374 driver.evaluate(ImmutableList.of(fileKey1), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200375 assertThat(result.hasError()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000376
twerth5aaceb52020-04-07 06:31:56 -0700377 FilesystemValueChecker checker =
378 new FilesystemValueChecker(
379 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000380 Diff diff = getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000381 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
382 assertThat(diff.changedKeysWithNewValues()).isEmpty();
383 }
384
385 public void checkDirtyActions(BatchStat batchStatter, boolean forceDigests) throws Exception {
386 Artifact out1 = createDerivedArtifact("fiz");
387 Artifact out2 = createDerivedArtifact("pop");
388
389 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "hello");
390 FileSystemUtils.writeContentAsLatin1(out2.getPath(), "fizzlepop");
391
lberki41cfcc72019-08-05 02:55:36 -0700392 TimestampGranularityMonitor tsgm = new TimestampGranularityMonitor(BlazeClock.instance());
janakrbaf52ae2018-02-14 09:03:18 -0800393 ActionLookupKey actionLookupKey =
janakr573807d2018-01-11 14:02:35 -0800394 new ActionLookupKey() {
395 @Override
396 public SkyFunctionName functionName() {
397 return SkyFunctionName.FOR_TESTING;
398 }
399 };
janakrefb3f152019-06-05 17:42:34 -0700400 SkyKey actionKey1 = ActionLookupData.create(actionLookupKey, 0);
401 SkyKey actionKey2 = ActionLookupData.create(actionLookupKey, 1);
lberki41cfcc72019-08-05 02:55:36 -0700402
403 pretendBuildTwoArtifacts(out1, actionKey1, out2, actionKey2, batchStatter, tsgm);
404
405 // Change the file but not its size
406 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "hallo");
407 checkActionDirtiedByFile(out1, actionKey1, batchStatter, tsgm);
408 pretendBuildTwoArtifacts(out1, actionKey1, out2, actionKey2, batchStatter, tsgm);
409
410 // Now try with a different size
411 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "hallo2");
412 checkActionDirtiedByFile(out1, actionKey1, batchStatter, tsgm);
413 }
414
415 private void pretendBuildTwoArtifacts(
416 Artifact out1,
417 SkyKey actionKey1,
418 Artifact out2,
419 SkyKey actionKey2,
420 BatchStat batchStatter,
421 TimestampGranularityMonitor tsgm)
422 throws InterruptedException {
423 EvaluationContext evaluationContext =
424 EvaluationContext.newBuilder()
425 .setKeepGoing(false)
426 .setNumThreads(1)
427 .setEventHander(NullEventHandler.INSTANCE)
428 .build();
429
430 tsgm.setCommandStartTime();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000431 differencer.inject(
432 ImmutableMap.<SkyKey, SkyValue>of(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000433 actionKey1,
434 actionValue(
435 new TestAction(
ulfjack1e1a7752019-12-10 21:17:58 -0800436 Runnables.doNothing(),
437 NestedSetBuilder.emptySet(Order.STABLE_ORDER),
438 ImmutableSet.of(out1))),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000439 actionKey2,
440 actionValue(
441 new TestAction(
lberkic35878a2019-08-01 02:28:54 -0700442 Runnables.doNothing(),
ulfjack1e1a7752019-12-10 21:17:58 -0800443 NestedSetBuilder.emptySet(Order.STABLE_ORDER),
lberkic35878a2019-08-01 02:28:54 -0700444 ImmutableSet.of(out2)))));
Googler10028672018-10-25 12:14:34 -0700445 assertThat(driver.evaluate(ImmutableList.<SkyKey>of(), evaluationContext).hasError()).isFalse();
Eric Fellheimere6590722015-11-17 17:07:48 +0000446 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700447 new FilesystemValueChecker(
448 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000449 .getDirtyActionValues(
steinman39c00d22020-03-20 15:23:10 -0700450 evaluator.getValues(),
451 batchStatter,
452 ModifiedFileSet.EVERYTHING_MODIFIED,
453 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700454 .isEmpty();
455
456 tsgm.waitForTimestampGranularity(OutErr.SYSTEM_OUT_ERR);
457 }
458
459 private void checkActionDirtiedByFile(
460 Artifact file, SkyKey actionKey, BatchStat batchStatter, TimestampGranularityMonitor tsgm)
461 throws InterruptedException {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000462 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700463 new FilesystemValueChecker(
464 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
lberki41cfcc72019-08-05 02:55:36 -0700465 .getDirtyActionValues(
steinman39c00d22020-03-20 15:23:10 -0700466 evaluator.getValues(),
467 batchStatter,
468 ModifiedFileSet.EVERYTHING_MODIFIED,
469 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700470 .containsExactly(actionKey);
471 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700472 new FilesystemValueChecker(
473 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000474 .getDirtyActionValues(
475 evaluator.getValues(),
476 batchStatter,
steinman39c00d22020-03-20 15:23:10 -0700477 new ModifiedFileSet.Builder().modify(file.getExecPath()).build(),
478 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700479 .containsExactly(actionKey);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000480 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700481 new FilesystemValueChecker(
482 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
lberki41cfcc72019-08-05 02:55:36 -0700483 .getDirtyActionValues(
484 evaluator.getValues(),
485 batchStatter,
486 new ModifiedFileSet.Builder()
487 .modify(file.getExecPath().getParentDirectory())
steinman39c00d22020-03-20 15:23:10 -0700488 .build(),
489 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700490 .isEmpty();
Eric Fellheimere6590722015-11-17 17:07:48 +0000491 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700492 new FilesystemValueChecker(
493 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
lberki41cfcc72019-08-05 02:55:36 -0700494 .getDirtyActionValues(
steinman39c00d22020-03-20 15:23:10 -0700495 evaluator.getValues(),
496 batchStatter,
497 ModifiedFileSet.NOTHING_MODIFIED,
498 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700499 .isEmpty();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000500 }
501
janakrefb3f152019-06-05 17:42:34 -0700502 private void checkDirtyTreeArtifactActions(BatchStat batchStatter) throws Exception {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000503 // Normally, an Action specifies the contents of a TreeArtifact when it executes.
504 // To decouple FileSystemValueTester checking from Action execution, we inject TreeArtifact
505 // contents into ActionExecutionValues.
506
cpeyserac09f0a2018-02-05 09:33:15 -0800507 SpecialArtifact out1 = createTreeArtifact("one");
Googler1d8d1382020-05-18 12:10:49 -0700508 TreeFileArtifact file11 = TreeFileArtifact.createTreeOutput(out1, "fizz");
509 out1.getPath().createDirectoryAndParents();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000510 FileSystemUtils.writeContentAsLatin1(file11.getPath(), "buzz");
511
cpeyserac09f0a2018-02-05 09:33:15 -0800512 SpecialArtifact out2 = createTreeArtifact("two");
Googler1d8d1382020-05-18 12:10:49 -0700513 out2.getPath().getChild("subdir").createDirectoryAndParents();
514 TreeFileArtifact file21 = TreeFileArtifact.createTreeOutput(out2, "moony");
515 TreeFileArtifact file22 = TreeFileArtifact.createTreeOutput(out2, "subdir/wormtail");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000516 FileSystemUtils.writeContentAsLatin1(file21.getPath(), "padfoot");
517 FileSystemUtils.writeContentAsLatin1(file22.getPath(), "prongs");
518
cpeyserac09f0a2018-02-05 09:33:15 -0800519 SpecialArtifact outEmpty = createTreeArtifact("empty");
Googler1d8d1382020-05-18 12:10:49 -0700520 outEmpty.getPath().createDirectoryAndParents();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000521
cpeyserac09f0a2018-02-05 09:33:15 -0800522 SpecialArtifact outUnchanging = createTreeArtifact("untouched");
Googler1d8d1382020-05-18 12:10:49 -0700523 outUnchanging.getPath().createDirectoryAndParents();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000524
cpeyserac09f0a2018-02-05 09:33:15 -0800525 SpecialArtifact last = createTreeArtifact("zzzzzzzzzz");
Googler1d8d1382020-05-18 12:10:49 -0700526 last.getPath().createDirectoryAndParents();
Rumou Duan45e8e572016-06-17 16:43:44 +0000527
janakrbaf52ae2018-02-14 09:03:18 -0800528 ActionLookupKey actionLookupKey =
janakr573807d2018-01-11 14:02:35 -0800529 new ActionLookupKey() {
530 @Override
531 public SkyFunctionName functionName() {
532 return SkyFunctionName.FOR_TESTING;
533 }
534 };
janakrefb3f152019-06-05 17:42:34 -0700535 SkyKey actionKey1 = ActionLookupData.create(actionLookupKey, 0);
536 SkyKey actionKey2 = ActionLookupData.create(actionLookupKey, 1);
537 SkyKey actionKeyEmpty = ActionLookupData.create(actionLookupKey, 2);
538 SkyKey actionKeyUnchanging = ActionLookupData.create(actionLookupKey, 3);
539 SkyKey actionKeyLast = ActionLookupData.create(actionLookupKey, 4);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000540 differencer.inject(
541 ImmutableMap.<SkyKey, SkyValue>of(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000542 actionKey1,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000543 actionValueWithTreeArtifacts(ImmutableList.of(file11)),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000544 actionKey2,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000545 actionValueWithTreeArtifacts(ImmutableList.of(file21, file22)),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000546 actionKeyEmpty,
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000547 actionValueWithEmptyDirectory(outEmpty),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000548 actionKeyUnchanging,
Rumou Duan45e8e572016-06-17 16:43:44 +0000549 actionValueWithEmptyDirectory(outUnchanging),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000550 actionKeyLast,
Rumou Duan45e8e572016-06-17 16:43:44 +0000551 actionValueWithEmptyDirectory(last)));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000552
Googler10028672018-10-25 12:14:34 -0700553 EvaluationContext evaluationContext =
554 EvaluationContext.newBuilder()
555 .setKeepGoing(false)
556 .setNumThreads(1)
557 .setEventHander(NullEventHandler.INSTANCE)
558 .build();
Googler1d8d1382020-05-18 12:10:49 -0700559 assertThat(driver.evaluate(ImmutableList.of(), evaluationContext).hasError()).isFalse();
steinman39c00d22020-03-20 15:23:10 -0700560 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700561 new FilesystemValueChecker(
562 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
steinman39c00d22020-03-20 15:23:10 -0700563 .getDirtyActionValues(
564 evaluator.getValues(),
565 batchStatter,
566 ModifiedFileSet.EVERYTHING_MODIFIED,
567 /* trustRemoteArtifacts= */ false))
568 .isEmpty();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000569
570 // Touching the TreeArtifact directory should have no effect
571 FileSystemUtils.touchFile(out1.getPath());
572 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700573 new FilesystemValueChecker(
574 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
steinman39c00d22020-03-20 15:23:10 -0700575 .getDirtyActionValues(
576 evaluator.getValues(),
577 batchStatter,
578 ModifiedFileSet.EVERYTHING_MODIFIED,
579 /* trustRemoteArtifacts= */ false))
580 .isEmpty();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000581 // Neither should touching a subdirectory.
582 FileSystemUtils.touchFile(out2.getPath().getChild("subdir"));
583 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700584 new FilesystemValueChecker(
585 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
steinman39c00d22020-03-20 15:23:10 -0700586 .getDirtyActionValues(
587 evaluator.getValues(),
588 batchStatter,
589 ModifiedFileSet.EVERYTHING_MODIFIED,
590 /* trustRemoteArtifacts= */ false))
591 .isEmpty();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000592
593 /* **** Tests for directories **** */
594
595 // Removing a directory (even if empty) should have an effect
596 outEmpty.getPath().delete();
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000597 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700598 new FilesystemValueChecker(
599 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000600 .getDirtyActionValues(
601 evaluator.getValues(),
602 batchStatter,
steinman39c00d22020-03-20 15:23:10 -0700603 new ModifiedFileSet.Builder().modify(outEmpty.getExecPath()).build(),
604 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000605 .containsExactly(actionKeyEmpty);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000606 // Symbolic links should count as dirty
607 Path dummyEmptyDir = fs.getPath("/bin").getRelative("symlink");
608 FileSystemUtils.createDirectoryAndParents(dummyEmptyDir);
609 FileSystemUtils.ensureSymbolicLink(outEmpty.getPath(), dummyEmptyDir);
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000610 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700611 new FilesystemValueChecker(
612 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000613 .getDirtyActionValues(
614 evaluator.getValues(),
615 batchStatter,
steinman39c00d22020-03-20 15:23:10 -0700616 new ModifiedFileSet.Builder().modify(outEmpty.getExecPath()).build(),
617 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000618 .containsExactly(actionKeyEmpty);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000619
620 // We're done fiddling with this... restore the original state
621 outEmpty.getPath().delete();
jmmv5cc1f652019-03-20 09:34:08 -0700622 dummyEmptyDir.deleteTree();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000623 FileSystemUtils.createDirectoryAndParents(outEmpty.getPath());
624
625 /* **** Tests for files and directory contents ****/
626
627 // Test that file contents matter. This is covered by existing tests already,
628 // so it's just a sanity check.
629 FileSystemUtils.writeContentAsLatin1(file11.getPath(), "goodbye");
Ulf Adamsc73051c62016-03-23 09:18:13 +0000630 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700631 new FilesystemValueChecker(
632 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000633 .getDirtyActionValues(
634 evaluator.getValues(),
635 batchStatter,
steinman39c00d22020-03-20 15:23:10 -0700636 new ModifiedFileSet.Builder().modify(file11.getExecPath()).build(),
637 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000638 .containsExactly(actionKey1);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000639
640 // Test that directory contents (and nested contents) matter
Googler1d8d1382020-05-18 12:10:49 -0700641 Artifact out1new = TreeFileArtifact.createTreeOutput(out1, "julius/caesar");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000642 FileSystemUtils.createDirectoryAndParents(out1.getPath().getChild("julius"));
643 FileSystemUtils.writeContentAsLatin1(out1new.getPath(), "octavian");
644 // even for empty directories
Googler1d8d1382020-05-18 12:10:49 -0700645 Artifact outEmptyNew = TreeFileArtifact.createTreeOutput(outEmpty, "marcus");
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000646 FileSystemUtils.writeContentAsLatin1(outEmptyNew.getPath(), "aurelius");
647 // so does removing
648 file21.getPath().delete();
649 // now, let's test our changes are actually visible
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000650 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700651 new FilesystemValueChecker(
652 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000653 .getDirtyActionValues(
steinman39c00d22020-03-20 15:23:10 -0700654 evaluator.getValues(),
655 batchStatter,
656 ModifiedFileSet.EVERYTHING_MODIFIED,
657 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000658 .containsExactly(actionKey1, actionKey2, actionKeyEmpty);
659 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700660 new FilesystemValueChecker(
661 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000662 .getDirtyActionValues(
663 evaluator.getValues(),
664 batchStatter,
665 new ModifiedFileSet.Builder()
666 .modify(file21.getExecPath())
667 .modify(out1new.getExecPath())
668 .modify(outEmptyNew.getExecPath())
steinman39c00d22020-03-20 15:23:10 -0700669 .build(),
670 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000671 .containsExactly(actionKey1, actionKey2, actionKeyEmpty);
Rumou Duan45e8e572016-06-17 16:43:44 +0000672 // We also check that if the modified file set does not contain our modified files on disk,
673 // we are not going to check and return them.
674 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700675 new FilesystemValueChecker(
676 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000677 .getDirtyActionValues(
678 evaluator.getValues(),
679 batchStatter,
680 new ModifiedFileSet.Builder()
681 .modify(file21.getExecPath())
682 .modify(outEmptyNew.getExecPath())
steinman39c00d22020-03-20 15:23:10 -0700683 .build(),
684 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000685 .containsExactly(actionKey2, actionKeyEmpty);
Rumou Duan45e8e572016-06-17 16:43:44 +0000686 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700687 new FilesystemValueChecker(
688 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000689 .getDirtyActionValues(
690 evaluator.getValues(),
691 batchStatter,
692 new ModifiedFileSet.Builder()
693 .modify(file21.getExecPath())
694 .modify(out1new.getExecPath())
steinman39c00d22020-03-20 15:23:10 -0700695 .build(),
696 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000697 .containsExactly(actionKey1, actionKey2);
Rumou Duan45e8e572016-06-17 16:43:44 +0000698 // Check modifying the last (lexicographically) tree artifact.
699 last.getPath().delete();
700 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700701 new FilesystemValueChecker(
702 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000703 .getDirtyActionValues(
704 evaluator.getValues(),
705 batchStatter,
706 new ModifiedFileSet.Builder()
707 .modify(file21.getExecPath())
708 .modify(out1new.getExecPath())
709 .modify(last.getExecPath())
steinman39c00d22020-03-20 15:23:10 -0700710 .build(),
711 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000712 .containsExactly(actionKey1, actionKey2, actionKeyLast);
Rumou Duan45e8e572016-06-17 16:43:44 +0000713 // Check ModifiedFileSet without the last (lexicographically) tree artifact.
714 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700715 new FilesystemValueChecker(
716 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000717 .getDirtyActionValues(
718 evaluator.getValues(),
719 batchStatter,
720 new ModifiedFileSet.Builder()
721 .modify(file21.getExecPath())
722 .modify(out1new.getExecPath())
steinman39c00d22020-03-20 15:23:10 -0700723 .build(),
724 /* trustRemoteArtifacts= */ false))
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000725 .containsExactly(actionKey1, actionKey2);
Rumou Duan45e8e572016-06-17 16:43:44 +0000726 // Restore
727 last.getPath().delete();
728 FileSystemUtils.createDirectoryAndParents(last.getPath());
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000729 // We add a test for NOTHING_MODIFIED, because FileSystemValueChecker doesn't
730 // pay attention to file sets for TreeArtifact directory listings.
731 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700732 new FilesystemValueChecker(
733 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
steinman39c00d22020-03-20 15:23:10 -0700734 .getDirtyActionValues(
735 evaluator.getValues(),
736 batchStatter,
737 ModifiedFileSet.NOTHING_MODIFIED,
738 /* trustRemoteArtifacts= */ false))
739 .isEmpty();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000740 }
741
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000742 private Artifact createDerivedArtifact(String relPath) throws IOException {
janakr448f1cf2020-03-30 09:12:44 -0700743 String outSegment = "bin";
744 Path outputPath = fs.getPath("/" + outSegment);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000745 outputPath.createDirectory();
janakraea05602019-05-22 15:41:29 -0700746 return ActionsTestUtil.createArtifact(
janakr448f1cf2020-03-30 09:12:44 -0700747 ArtifactRoot.asDerivedRoot(fs.getPath("/"), outSegment), outputPath.getRelative(relPath));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000748 }
749
cpeyserac09f0a2018-02-05 09:33:15 -0800750 private SpecialArtifact createTreeArtifact(String relPath) throws IOException {
janakr448f1cf2020-03-30 09:12:44 -0700751 String outSegment = "bin";
752 Path outputDir = fs.getPath("/" + outSegment);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000753 Path outputPath = outputDir.getRelative(relPath);
754 outputDir.createDirectory();
janakr448f1cf2020-03-30 09:12:44 -0700755 ArtifactRoot derivedRoot = ArtifactRoot.asDerivedRoot(fs.getPath("/"), outSegment);
Googler1d8d1382020-05-18 12:10:49 -0700756 return ActionsTestUtil.createTreeArtifactWithGeneratingAction(
tomluee6a6862018-01-17 14:36:26 -0800757 derivedRoot,
Googler1d8d1382020-05-18 12:10:49 -0700758 derivedRoot.getExecPath().getRelative(derivedRoot.getRoot().relativize(outputPath)));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000759 }
760
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000761 @Test
steinman7184cb32020-04-20 14:33:03 -0700762 // TODO(b/154337187): Remove the following annotation to re-enable once this test is de-flaked.
763 @Ignore
764 public void testDirtyActions() throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000765 checkDirtyActions(null, false);
766 }
767
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000768 @Test
steinman7184cb32020-04-20 14:33:03 -0700769 // TODO(b/154337187): Remove the following annotation to re-enable once this test is de-flaked.
770 @Ignore
771 public void testDirtyActionsBatchStat() throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000772 checkDirtyActions(
773 new BatchStat() {
774 @Override
775 public List<FileStatusWithDigest> batchStat(
776 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
777 throws IOException {
778 List<FileStatusWithDigest> stats = new ArrayList<>();
779 for (PathFragment pathFrag : paths) {
780 stats.add(
781 FileStatusWithDigestAdapter.adapt(
tomlu6c919062018-01-11 17:32:09 -0800782 fs.getPath("/").getRelative(pathFrag).statIfFound(Symlinks.NOFOLLOW)));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000783 }
784 return stats;
785 }
786 },
787 false);
788 }
789
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000790 @Test
steinman68e78f52020-04-23 12:30:19 -0700791 // TODO(b/154337187): Remove the following annotation to re-enable once this test is de-flaked.
792 @Ignore
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000793 public void testDirtyActionsBatchStatWithDigest() throws Exception {
794 checkDirtyActions(
795 new BatchStat() {
796 @Override
797 public List<FileStatusWithDigest> batchStat(
798 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
799 throws IOException {
800 List<FileStatusWithDigest> stats = new ArrayList<>();
801 for (PathFragment pathFrag : paths) {
tomlu6c919062018-01-11 17:32:09 -0800802 final Path path = fs.getPath("/").getRelative(pathFrag);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000803 stats.add(statWithDigest(path, path.statIfFound(Symlinks.NOFOLLOW)));
804 }
805 return stats;
806 }
807 },
808 true);
809 }
810
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000811 @Test
steinman68e78f52020-04-23 12:30:19 -0700812 // TODO(b/154337187): Remove the following annotation to re-enable once this test is de-flaked.
813 @Ignore
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000814 public void testDirtyActionsBatchStatFallback() throws Exception {
815 checkDirtyActions(
816 new BatchStat() {
817 @Override
818 public List<FileStatusWithDigest> batchStat(
819 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
820 throws IOException {
821 throw new IOException("try again");
822 }
823 },
824 false);
825 }
826
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000827 @Test
828 public void testDirtyTreeArtifactActions() throws Exception {
829 checkDirtyTreeArtifactActions(null);
830 }
831
832 @Test
833 public void testDirtyTreeArtifactActionsBatchStat() throws Exception {
834 checkDirtyTreeArtifactActions(
835 new BatchStat() {
836 @Override
837 public List<FileStatusWithDigest> batchStat(
838 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
839 throws IOException {
840 List<FileStatusWithDigest> stats = new ArrayList<>();
841 for (PathFragment pathFrag : paths) {
842 stats.add(
843 FileStatusWithDigestAdapter.adapt(
tomlu6c919062018-01-11 17:32:09 -0800844 fs.getPath("/").getRelative(pathFrag).statIfFound(Symlinks.NOFOLLOW)));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000845 }
846 return stats;
847 }
848 });
849 }
850
851 // TODO(bazel-team): Add some tests for FileSystemValueChecker#changedKeys*() methods.
852 // Presently these appear to be untested.
853
ajurkowskie2982912020-04-09 10:32:08 -0700854 private static ActionExecutionValue actionValue(Action action) {
lberkif7eee1e2019-07-31 05:49:10 -0700855 Map<Artifact, FileArtifactValue> artifactData = new HashMap<>();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000856 for (Artifact output : action.getOutputs()) {
857 try {
858 Path path = output.getPath();
lberkic35878a2019-08-01 02:28:54 -0700859 FileArtifactValue noDigest =
860 ActionMetadataHandler.fileArtifactValueFromArtifact(
861 output,
862 FileStatusWithDigestAdapter.adapt(path.statIfFound(Symlinks.NOFOLLOW)),
863 null);
864 FileArtifactValue withDigest =
865 FileArtifactValue.createFromInjectedDigest(
866 noDigest, path.getDigest(), !output.isConstantMetadata());
867 artifactData.put(output, withDigest);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000868 } catch (IOException e) {
869 throw new IllegalStateException(e);
870 }
871 }
janakrb9d8d582018-06-13 21:57:19 -0700872 return ActionExecutionValue.create(
Michael Thvedt8d5a7bb2016-02-09 03:06:34 +0000873 artifactData,
Googler974879d2020-05-27 13:25:52 -0700874 /*treeArtifactData=*/ ImmutableMap.of(),
janakrb9d8d582018-06-13 21:57:19 -0700875 /*outputSymlinks=*/ null,
shahanef6f4cf2018-06-26 11:24:59 -0700876 /*discoveredModules=*/ null,
janakr9f496f32018-10-24 15:08:09 -0700877 /*actionDependsOnBuildId=*/ false);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000878 }
879
Googler974879d2020-05-27 13:25:52 -0700880 private static ActionExecutionValue actionValueWithEmptyDirectory(SpecialArtifact emptyDir) {
janakrb9d8d582018-06-13 21:57:19 -0700881 return ActionExecutionValue.create(
Googler974879d2020-05-27 13:25:52 -0700882 /*artifactData=*/ ImmutableMap.of(),
883 ImmutableMap.of(emptyDir, TreeArtifactValue.create(ImmutableMap.of())),
janakrb9d8d582018-06-13 21:57:19 -0700884 /*outputSymlinks=*/ null,
shahanef6f4cf2018-06-26 11:24:59 -0700885 /*discoveredModules=*/ null,
janakr9f496f32018-10-24 15:08:09 -0700886 /*actionDependsOnBuildId=*/ false);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000887 }
888
ajurkowskie2982912020-04-09 10:32:08 -0700889 private static ActionExecutionValue actionValueWithTreeArtifacts(
890 List<TreeFileArtifact> contents) {
Rumou Duana77f32c2016-04-13 21:59:21 +0000891 Map<Artifact, Map<TreeFileArtifact, FileArtifactValue>> directoryData = new HashMap<>();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000892
Rumou Duana77f32c2016-04-13 21:59:21 +0000893 for (TreeFileArtifact output : contents) {
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000894 try {
Rumou Duana77f32c2016-04-13 21:59:21 +0000895 Map<TreeFileArtifact, FileArtifactValue> dirDatum =
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000896 directoryData.get(output.getParent());
897 if (dirDatum == null) {
898 dirDatum = new HashMap<>();
899 directoryData.put(output.getParent(), dirDatum);
900 }
lberkic35878a2019-08-01 02:28:54 -0700901 Path path = output.getPath();
902 FileArtifactValue noDigest =
903 ActionMetadataHandler.fileArtifactValueFromArtifact(
904 output,
905 FileStatusWithDigestAdapter.adapt(path.statIfFound(Symlinks.NOFOLLOW)),
906 null);
907 FileArtifactValue withDigest =
908 FileArtifactValue.createFromInjectedDigest(
909 noDigest, path.getDigest(), !output.isConstantMetadata());
910 dirDatum.put(output, withDigest);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000911 } catch (IOException e) {
912 throw new IllegalStateException(e);
913 }
914 }
915
916 Map<Artifact, TreeArtifactValue> treeArtifactData = new HashMap<>();
Rumou Duana77f32c2016-04-13 21:59:21 +0000917 for (Map.Entry<Artifact, Map<TreeFileArtifact, FileArtifactValue>> dirDatum :
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000918 directoryData.entrySet()) {
Rumou Duana77f32c2016-04-13 21:59:21 +0000919 treeArtifactData.put(dirDatum.getKey(), TreeArtifactValue.create(dirDatum.getValue()));
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000920 }
921
janakrb9d8d582018-06-13 21:57:19 -0700922 return ActionExecutionValue.create(
Googler974879d2020-05-27 13:25:52 -0700923 /*artifactData=*/ ImmutableMap.of(),
kush2ce45a22018-05-02 14:15:37 -0700924 treeArtifactData,
janakrb9d8d582018-06-13 21:57:19 -0700925 /*outputSymlinks=*/ null,
shahanef6f4cf2018-06-26 11:24:59 -0700926 /*discoveredModules=*/ null,
janakr9f496f32018-10-24 15:08:09 -0700927 /*actionDependsOnBuildId=*/ false);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000928 }
929
ajurkowskie2982912020-04-09 10:32:08 -0700930 private static ActionExecutionValue actionValueWithRemoteTreeArtifact(
buchgr4992ae22019-03-20 04:23:32 -0700931 SpecialArtifact output, Map<PathFragment, RemoteFileArtifactValue> children) {
932 ImmutableMap.Builder<TreeFileArtifact, FileArtifactValue> childFileValues =
933 ImmutableMap.builder();
934 for (Map.Entry<PathFragment, RemoteFileArtifactValue> child : children.entrySet()) {
935 childFileValues.put(
Googler1d8d1382020-05-18 12:10:49 -0700936 TreeFileArtifact.createTreeOutput(output, child.getKey()), child.getValue());
buchgr4992ae22019-03-20 04:23:32 -0700937 }
938 TreeArtifactValue treeArtifactValue = TreeArtifactValue.create(childFileValues.build());
939 return ActionExecutionValue.create(
buchgr4992ae22019-03-20 04:23:32 -0700940 ImmutableMap.of(),
lberkic35878a2019-08-01 02:28:54 -0700941 Collections.singletonMap(output, treeArtifactValue),
buchgr4992ae22019-03-20 04:23:32 -0700942 /* outputSymlinks= */ null,
943 /* discoveredModules= */ null,
944 /* actionDependsOnBuildId= */ false);
945 }
946
ajurkowskie2982912020-04-09 10:32:08 -0700947 private static ActionExecutionValue actionValueWithRemoteArtifact(
buchgr4992ae22019-03-20 04:23:32 -0700948 Artifact output, RemoteFileArtifactValue value) {
949 return ActionExecutionValue.create(
buchgr4992ae22019-03-20 04:23:32 -0700950 Collections.singletonMap(output, value),
lberkic35878a2019-08-01 02:28:54 -0700951 ImmutableMap.of(),
buchgr4992ae22019-03-20 04:23:32 -0700952 /* outputSymlinks= */ null,
953 /* discoveredModules= */ null,
954 /* actionDependsOnBuildId= */ false);
955 }
956
957 private RemoteFileArtifactValue createRemoteFileArtifactValue(String contents) {
958 byte[] data = contents.getBytes();
959 DigestHashFunction hashFn = fs.getDigestFunction();
960 HashCode hash = hashFn.getHashFunction().hashBytes(data);
George Gensure3ef8fb92020-05-06 09:49:48 -0700961 return new RemoteFileArtifactValue(hash.asBytes(), data.length, -1, "action-id");
buchgr4992ae22019-03-20 04:23:32 -0700962 }
963
964 @Test
965 public void testRemoteAndLocalArtifacts() throws Exception {
966 // Test that injected remote artifacts are trusted by the FileSystemValueChecker
steinman39c00d22020-03-20 15:23:10 -0700967 // if it is configured to trust remote artifacts, and that local files always take precedence
968 // over remote files.
buchgr4992ae22019-03-20 04:23:32 -0700969 ActionLookupKey actionLookupKey =
970 new ActionLookupKey() {
971 @Override
972 public SkyFunctionName functionName() {
973 return SkyFunctionName.FOR_TESTING;
974 }
975 };
janakrefb3f152019-06-05 17:42:34 -0700976 SkyKey actionKey1 = ActionLookupData.create(actionLookupKey, 0);
977 SkyKey actionKey2 = ActionLookupData.create(actionLookupKey, 1);
buchgr4992ae22019-03-20 04:23:32 -0700978
979 Artifact out1 = createDerivedArtifact("foo");
980 Artifact out2 = createDerivedArtifact("bar");
981 Map<SkyKey, SkyValue> metadataToInject = new HashMap<>();
982 metadataToInject.put(
983 actionKey1,
984 actionValueWithRemoteArtifact(out1, createRemoteFileArtifactValue("foo-content")));
985 metadataToInject.put(
986 actionKey2,
987 actionValueWithRemoteArtifact(out2, createRemoteFileArtifactValue("bar-content")));
988 differencer.inject(metadataToInject);
989
990 EvaluationContext evaluationContext =
991 EvaluationContext.newBuilder()
992 .setKeepGoing(false)
993 .setNumThreads(1)
994 .setEventHander(NullEventHandler.INSTANCE)
995 .build();
996 assertThat(
997 driver.evaluate(ImmutableList.of(actionKey1, actionKey2), evaluationContext).hasError())
998 .isFalse();
999 assertThat(
twerth5aaceb52020-04-07 06:31:56 -07001000 new FilesystemValueChecker(
1001 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
buchgr4992ae22019-03-20 04:23:32 -07001002 .getDirtyActionValues(
1003 evaluator.getValues(),
1004 /* batchStatter= */ null,
steinman39c00d22020-03-20 15:23:10 -07001005 ModifiedFileSet.EVERYTHING_MODIFIED,
1006 /* trustRemoteArtifacts= */ true))
buchgr4992ae22019-03-20 04:23:32 -07001007 .isEmpty();
1008
1009 // Create the "out1" artifact on the filesystem and test that it invalidates the generating
1010 // action's SkyKey.
1011 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "new-foo-content");
1012 assertThat(
twerth5aaceb52020-04-07 06:31:56 -07001013 new FilesystemValueChecker(
1014 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
buchgr4992ae22019-03-20 04:23:32 -07001015 .getDirtyActionValues(
1016 evaluator.getValues(),
1017 /* batchStatter= */ null,
steinman39c00d22020-03-20 15:23:10 -07001018 ModifiedFileSet.EVERYTHING_MODIFIED,
1019 /* trustRemoteArtifacts= */ true))
buchgr4992ae22019-03-20 04:23:32 -07001020 .containsExactly(actionKey1);
1021 }
1022
1023 @Test
1024 public void testRemoteAndLocalTreeArtifacts() throws Exception {
1025 // Test that injected remote tree artifacts are trusted by the FileSystemValueChecker
1026 // and that local files always takes preference over remote files.
1027 ActionLookupKey actionLookupKey =
1028 new ActionLookupKey() {
1029 @Override
1030 public SkyFunctionName functionName() {
1031 return SkyFunctionName.FOR_TESTING;
1032 }
1033 };
janakrefb3f152019-06-05 17:42:34 -07001034 SkyKey actionKey = ActionLookupData.create(actionLookupKey, 0);
buchgr4992ae22019-03-20 04:23:32 -07001035
1036 SpecialArtifact treeArtifact = createTreeArtifact("dir");
1037 treeArtifact.getPath().createDirectoryAndParents();
1038 Map<PathFragment, RemoteFileArtifactValue> treeArtifactMetadata = new HashMap<>();
1039 treeArtifactMetadata.put(
1040 PathFragment.create("foo"), createRemoteFileArtifactValue("foo-content"));
1041 treeArtifactMetadata.put(
1042 PathFragment.create("bar"), createRemoteFileArtifactValue("bar-content"));
1043
1044 Map<SkyKey, SkyValue> metadataToInject = new HashMap<>();
1045 metadataToInject.put(
1046 actionKey, actionValueWithRemoteTreeArtifact(treeArtifact, treeArtifactMetadata));
1047 differencer.inject(metadataToInject);
1048
1049 EvaluationContext evaluationContext =
1050 EvaluationContext.newBuilder()
1051 .setKeepGoing(false)
1052 .setNumThreads(1)
1053 .setEventHander(NullEventHandler.INSTANCE)
1054 .build();
1055 assertThat(driver.evaluate(ImmutableList.of(actionKey), evaluationContext).hasError())
1056 .isFalse();
1057 assertThat(
twerth5aaceb52020-04-07 06:31:56 -07001058 new FilesystemValueChecker(
1059 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
buchgr4992ae22019-03-20 04:23:32 -07001060 .getDirtyActionValues(
1061 evaluator.getValues(),
1062 /* batchStatter= */ null,
steinman39c00d22020-03-20 15:23:10 -07001063 ModifiedFileSet.EVERYTHING_MODIFIED,
1064 /* trustRemoteArtifacts= */ false))
buchgr4992ae22019-03-20 04:23:32 -07001065 .isEmpty();
1066
1067 // Create dir/foo on the local disk and test that it invalidates the associated sky key.
Googler1d8d1382020-05-18 12:10:49 -07001068 TreeFileArtifact fooArtifact = TreeFileArtifact.createTreeOutput(treeArtifact, "foo");
buchgr4992ae22019-03-20 04:23:32 -07001069 FileSystemUtils.writeContentAsLatin1(fooArtifact.getPath(), "new-foo-content");
1070 assertThat(
twerth5aaceb52020-04-07 06:31:56 -07001071 new FilesystemValueChecker(
1072 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
buchgr4992ae22019-03-20 04:23:32 -07001073 .getDirtyActionValues(
1074 evaluator.getValues(),
1075 /* batchStatter= */ null,
steinman39c00d22020-03-20 15:23:10 -07001076 ModifiedFileSet.EVERYTHING_MODIFIED,
1077 /* trustRemoteArtifacts= */ false))
buchgr4992ae22019-03-20 04:23:32 -07001078 .containsExactly(actionKey);
1079 }
1080
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001081 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001082 public void testPropagatesRuntimeExceptions() throws Exception {
tomluee6a6862018-01-17 14:36:26 -08001083 Collection<SkyKey> values =
1084 ImmutableList.of(
1085 FileValue.key(
1086 RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo"))));
Googler10028672018-10-25 12:14:34 -07001087 driver.evaluate(values, EVALUATION_OPTIONS);
twerth5aaceb52020-04-07 06:31:56 -07001088 FilesystemValueChecker checker =
1089 new FilesystemValueChecker(
1090 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001091
Nathan Harmata8cd29782015-11-10 03:24:01 +00001092 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001093
1094 fs.statThrowsRuntimeException = true;
jcater83130f42019-04-30 14:29:28 -07001095 RuntimeException e =
1096 assertThrows(RuntimeException.class, () -> getDirtyFilesystemKeys(evaluator, checker));
1097 assertThat(e).hasMessageThat().isEqualTo("bork");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001098 }
1099
1100 private static void assertEmptyDiff(Diff diff) {
1101 assertDiffWithNewValues(diff);
1102 }
1103
1104 private static void assertDiffWithNewValues(Diff diff, SkyKey... keysWithNewValues) {
1105 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
1106 assertThat(diff.changedKeysWithNewValues().keySet())
1107 .containsExactlyElementsIn(Arrays.asList(keysWithNewValues));
1108 }
1109
1110 private class MockFileSystem extends InMemoryFileSystem {
1111
1112 boolean statThrowsRuntimeException;
1113 boolean readlinkThrowsIoException;
1114
1115 MockFileSystem() {
ccalvarinc9efd062018-07-27 12:46:46 -07001116 super();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001117 }
1118
1119 @Override
fellya205ed82018-09-10 11:52:34 -07001120 public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001121 if (statThrowsRuntimeException) {
1122 throw new RuntimeException("bork");
1123 }
fellya205ed82018-09-10 11:52:34 -07001124 return super.statIfFound(path, followSymlinks);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001125 }
1126
1127 @Override
aehligc801c392017-12-19 07:12:25 -08001128 protected PathFragment readSymbolicLink(Path path) throws IOException {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001129 if (readlinkThrowsIoException) {
1130 throw new IOException("readlink failed");
1131 }
1132 return super.readSymbolicLink(path);
1133 }
1134 }
1135
1136 private static FileStatusWithDigest statWithDigest(final Path path, final FileStatus stat) {
1137 return new FileStatusWithDigest() {
1138 @Nullable
1139 @Override
1140 public byte[] getDigest() throws IOException {
olaolabfd1d332017-06-19 16:55:24 -04001141 return path.getDigest();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001142 }
1143
1144 @Override
1145 public boolean isFile() {
1146 return stat.isFile();
1147 }
1148
1149 @Override
1150 public boolean isSpecialFile() {
1151 return stat.isSpecialFile();
1152 }
1153
1154 @Override
1155 public boolean isDirectory() {
1156 return stat.isDirectory();
1157 }
1158
1159 @Override
1160 public boolean isSymbolicLink() {
1161 return stat.isSymbolicLink();
1162 }
1163
1164 @Override
1165 public long getSize() throws IOException {
1166 return stat.getSize();
1167 }
1168
1169 @Override
1170 public long getLastModifiedTime() throws IOException {
1171 return stat.getLastModifiedTime();
1172 }
1173
1174 @Override
1175 public long getLastChangeTime() throws IOException {
1176 return stat.getLastChangeTime();
1177 }
1178
1179 @Override
1180 public long getNodeId() throws IOException {
1181 return stat.getNodeId();
1182 }
1183 };
1184 }
1185
Nathan Harmata8cd29782015-11-10 03:24:01 +00001186 private static Diff getDirtyFilesystemKeys(MemoizingEvaluator evaluator,
1187 FilesystemValueChecker checker) throws InterruptedException {
1188 return checker.getDirtyKeys(evaluator.getValues(), new BasicFilesystemDirtinessChecker());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001189 }
1190}