blob: 856a10cc577c34c5e9f5fbd168528c929c760804 [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;
janakr933c3012021-07-13 07:44:15 -070018import static java.util.concurrent.TimeUnit.SECONDS;
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;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000027import com.google.devtools.build.lib.actions.Artifact;
Michael Thvedte4a7b0792016-02-09 12:15:53 +000028import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
Rumou Duana77f32c2016-04-13 21:59:21 +000029import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
tomlu1cdcdf92018-01-16 11:07:51 -080030import com.google.devtools.build.lib.actions.ArtifactRoot;
Googlerf0b0c392021-01-27 17:56:52 -080031import com.google.devtools.build.lib.actions.ArtifactRoot.RootType;
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;
janakr13b737a2021-07-02 14:24:25 -070036import com.google.devtools.build.lib.actions.ThreadStateReceiver;
janakraea05602019-05-22 15:41:29 -070037import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000038import com.google.devtools.build.lib.actions.util.TestAction;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000039import com.google.devtools.build.lib.analysis.BlazeDirectories;
janakr3b63a4e2017-09-14 09:55:40 +020040import com.google.devtools.build.lib.analysis.ServerDirectories;
lberki41cfcc72019-08-05 02:55:36 -070041import com.google.devtools.build.lib.clock.BlazeClock;
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;
janakre2af68f2021-03-18 15:11:30 -070045import com.google.devtools.build.lib.io.FileSymlinkCycleUniquenessFunction;
46import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionUniquenessFunction;
janakr15e15c22019-01-30 11:24:49 -080047import com.google.devtools.build.lib.packages.WorkspaceFileValue;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000048import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
49import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.BasicFilesystemDirtinessChecker;
Nathan Harmatad4f75942016-10-18 08:55:17 +000050import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
John Cater5e9ce942016-10-12 17:23:30 +000051import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +000052import com.google.devtools.build.lib.testutil.TestConstants;
michajlo8083e322020-03-20 13:32:52 -070053import com.google.devtools.build.lib.testutil.TestPackageFactoryBuilderFactory;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000054import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
janakr933c3012021-07-13 07:44:15 -070055import com.google.devtools.build.lib.testutil.TestUtils;
laszlocsomor29bdf632018-03-13 02:50:46 -070056import com.google.devtools.build.lib.testutil.TimestampGranularityUtils;
Laszlo Csomora278aec2018-03-09 04:07:31 -080057import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000058import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
59import com.google.devtools.build.lib.vfs.BatchStat;
buchgr4992ae22019-03-20 04:23:32 -070060import com.google.devtools.build.lib.vfs.DigestHashFunction;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000061import com.google.devtools.build.lib.vfs.FileStatus;
62import com.google.devtools.build.lib.vfs.FileStatusWithDigest;
63import com.google.devtools.build.lib.vfs.FileStatusWithDigestAdapter;
64import com.google.devtools.build.lib.vfs.FileSystemUtils;
Eric Fellheimere6590722015-11-17 17:07:48 +000065import com.google.devtools.build.lib.vfs.ModifiedFileSet;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000066import com.google.devtools.build.lib.vfs.Path;
67import com.google.devtools.build.lib.vfs.PathFragment;
tomluee6a6862018-01-17 14:36:26 -080068import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000069import com.google.devtools.build.lib.vfs.RootedPath;
70import com.google.devtools.build.lib.vfs.Symlinks;
djasper7faa0ef2019-03-28 10:00:00 -070071import com.google.devtools.build.lib.vfs.UnixGlob;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000072import com.google.devtools.build.skyframe.Differencer.Diff;
Googler10028672018-10-25 12:14:34 -070073import com.google.devtools.build.skyframe.EvaluationContext;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000074import com.google.devtools.build.skyframe.EvaluationResult;
75import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
76import com.google.devtools.build.skyframe.MemoizingEvaluator;
77import com.google.devtools.build.skyframe.RecordingDifferencer;
janakr1cde8722017-10-10 03:22:21 +020078import com.google.devtools.build.skyframe.SequencedRecordingDifferencer;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000079import com.google.devtools.build.skyframe.SequentialBuildDriver;
80import com.google.devtools.build.skyframe.SkyFunction;
81import com.google.devtools.build.skyframe.SkyFunctionName;
82import com.google.devtools.build.skyframe.SkyKey;
83import com.google.devtools.build.skyframe.SkyValue;
ajurkowskibdc60b02021-11-24 08:47:05 -080084import com.google.testing.junit.testparameterinjector.TestParameter;
85import com.google.testing.junit.testparameterinjector.TestParameterInjector;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000086import java.io.IOException;
87import java.util.ArrayList;
88import java.util.Arrays;
89import java.util.Collection;
90import java.util.HashMap;
91import java.util.List;
92import java.util.Map;
93import java.util.UUID;
janakr933c3012021-07-13 07:44:15 -070094import java.util.concurrent.CountDownLatch;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000095import java.util.concurrent.atomic.AtomicReference;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000096import javax.annotation.Nullable;
Janak Ramakrishnancba16452016-07-29 02:17:02 +000097import org.junit.Before;
steinman7184cb32020-04-20 14:33:03 -070098import org.junit.Ignore;
Janak Ramakrishnancba16452016-07-29 02:17:02 +000099import org.junit.Test;
100import org.junit.runner.RunWith;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000101
jhorvitz3daedc32020-07-22 18:33:55 -0700102/** Tests for {@link FilesystemValueChecker}. */
ajurkowskibdc60b02021-11-24 08:47:05 -0800103@RunWith(TestParameterInjector.class)
ajurkowski280bbe22020-08-19 11:26:20 -0700104public final class FilesystemValueCheckerTest extends FilesystemValueCheckerTestBase {
Googler10028672018-10-25 12:14:34 -0700105 private static final EvaluationContext EVALUATION_OPTIONS =
106 EvaluationContext.newBuilder()
107 .setKeepGoing(false)
108 .setNumThreads(SkyframeExecutor.DEFAULT_THREAD_COUNT)
michajlo7a485be2020-07-30 11:08:46 -0700109 .setEventHandler(NullEventHandler.INSTANCE)
Googler10028672018-10-25 12:14:34 -0700110 .build();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000111
112 private RecordingDifferencer differencer;
113 private MemoizingEvaluator evaluator;
114 private SequentialBuildDriver driver;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000115 private Path pkgRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000116
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000117 @Before
ajurkowskifbd44272021-12-02 16:05:49 -0800118 public void setUp() throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000119 ImmutableMap.Builder<SkyFunctionName, SkyFunction> skyFunctions = ImmutableMap.builder();
120
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000121 pkgRoot = fs.getPath("/testroot");
jhorvitz3daedc32020-07-22 18:33:55 -0700122 pkgRoot.createDirectoryAndParents();
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000123 FileSystemUtils.createEmptyFile(pkgRoot.getRelative("WORKSPACE"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000124
John Catere0d1d0e2017-11-28 20:47:41 -0800125 AtomicReference<PathPackageLocator> pkgLocator =
126 new AtomicReference<>(
127 new PathPackageLocator(
128 fs.getPath("/output_base"),
tomluee6a6862018-01-17 14:36:26 -0800129 ImmutableList.of(Root.fromPath(pkgRoot)),
John Catere0d1d0e2017-11-28 20:47:41 -0800130 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
janakr3b63a4e2017-09-14 09:55:40 +0200131 BlazeDirectories directories =
132 new BlazeDirectories(
cushon849df362018-05-14 01:51:45 -0700133 new ServerDirectories(pkgRoot, pkgRoot, pkgRoot),
134 pkgRoot,
135 /* defaultSystemJavabase= */ null,
136 TestConstants.PRODUCT_NAME);
nharmata3fb7d342018-02-23 11:37:51 -0800137 ExternalFilesHelper externalFilesHelper = ExternalFilesHelper.createForTesting(
Nathan Harmatad4f75942016-10-18 08:55:17 +0000138 pkgLocator, ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS, directories);
shahan602cc852018-06-06 20:09:57 -0700139 skyFunctions.put(
140 FileStateValue.FILE_STATE,
141 new FileStateFunction(
jhorvitz3daedc32020-07-22 18:33:55 -0700142 new AtomicReference<>(),
djasper7faa0ef2019-03-28 10:00:00 -0700143 new AtomicReference<>(UnixGlob.DEFAULT_SYSCALLS),
144 externalFilesHelper));
shahan602cc852018-06-06 20:09:57 -0700145 skyFunctions.put(FileValue.FILE, new FileFunction(pkgLocator));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000146 skyFunctions.put(
janakre2af68f2021-03-18 15:11:30 -0700147 FileSymlinkCycleUniquenessFunction.NAME, new FileSymlinkCycleUniquenessFunction());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000148 skyFunctions.put(
janakre2af68f2021-03-18 15:11:30 -0700149 FileSymlinkInfiniteExpansionUniquenessFunction.NAME,
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000150 new FileSymlinkInfiniteExpansionUniquenessFunction());
ajurkowski2018b872020-04-14 09:59:00 -0700151 skyFunctions.put(
janakre4fcd472021-05-26 08:47:39 -0700152 SkyFunctions.PACKAGE,
153 new PackageFunction(
154 null,
155 null,
156 null,
157 null,
158 null,
janakre4fcd472021-05-26 08:47:39 -0700159 /*packageProgress=*/ null,
160 PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException.INSTANCE,
janakr13b737a2021-07-02 14:24:25 -0700161 PackageFunction.IncrementalityIntent.INCREMENTAL,
162 k -> ThreadStateReceiver.NULL_INSTANCE));
John Cater5e9ce942016-10-12 17:23:30 +0000163 skyFunctions.put(
164 SkyFunctions.PACKAGE_LOOKUP,
165 new PackageLookupFunction(
jhorvitz3daedc32020-07-22 18:33:55 -0700166 new AtomicReference<>(ImmutableSet.of()),
John Cater0c0735a2016-11-11 01:52:02 +0000167 CrossRepositoryLabelViolationStrategy.ERROR,
ajurkowski2018b872020-04-14 09:59:00 -0700168 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY,
169 BazelSkyframeExecutorConstants.EXTERNAL_PACKAGE_HELPER));
carmid6a98282018-03-13 19:19:16 -0700170 skyFunctions.put(
janakr15e15c22019-01-30 11:24:49 -0800171 WorkspaceFileValue.WORKSPACE_FILE,
carmid6a98282018-03-13 19:19:16 -0700172 new WorkspaceFileFunction(
173 TestRuleClassProvider.getRuleClassProvider(),
michajlo8083e322020-03-20 13:32:52 -0700174 TestPackageFactoryBuilderFactory.getInstance()
carmid6a98282018-03-13 19:19:16 -0700175 .builder(directories)
nharmatafde0bd2f2018-12-21 10:17:56 -0800176 .build(TestRuleClassProvider.getRuleClassProvider(), fs),
mjhalupkaf0e48112019-01-14 13:01:56 -0800177 directories,
brandjon771a0292020-05-26 12:04:16 -0700178 /*bzlLoadFunctionForInlining=*/ null));
ajurkowski2018b872020-04-14 09:59:00 -0700179 skyFunctions.put(
180 SkyFunctions.EXTERNAL_PACKAGE,
181 new ExternalPackageFunction(BazelSkyframeExecutorConstants.EXTERNAL_PACKAGE_HELPER));
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000182
janakr1cde8722017-10-10 03:22:21 +0200183 differencer = new SequencedRecordingDifferencer();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000184 evaluator = new InMemoryMemoizingEvaluator(skyFunctions.build(), differencer);
185 driver = new SequentialBuildDriver(evaluator);
186 PrecomputedValue.BUILD_ID.set(differencer, UUID.randomUUID());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000187 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000188 }
189
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000190 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000191 public void testEmpty() throws Exception {
twerth5aaceb52020-04-07 06:31:56 -0700192 FilesystemValueChecker checker =
193 new FilesystemValueChecker(
194 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000195 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000196 }
197
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000198 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000199 public void testSimple() throws Exception {
twerth5aaceb52020-04-07 06:31:56 -0700200 FilesystemValueChecker checker =
201 new FilesystemValueChecker(
202 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000203
204 Path path = fs.getPath("/foo");
205 FileSystemUtils.createEmptyFile(path);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000206 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000207
tomluee6a6862018-01-17 14:36:26 -0800208 SkyKey skyKey =
209 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800210 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000211 EvaluationResult<SkyValue> result =
Googler10028672018-10-25 12:14:34 -0700212 driver.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200213 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000214
Nathan Harmata8cd29782015-11-10 03:24:01 +0000215 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000216
217 FileSystemUtils.writeContentAsLatin1(path, "hello");
Nathan Harmata8cd29782015-11-10 03:24:01 +0000218 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000219
220 // The dirty bits are not reset until the FileValues are actually revalidated.
Nathan Harmata8cd29782015-11-10 03:24:01 +0000221 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000222
223 differencer.invalidate(ImmutableList.of(skyKey));
Googler10028672018-10-25 12:14:34 -0700224 result = driver.evaluate(ImmutableList.of(skyKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200225 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000226 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000227 }
228
229 /**
230 * Tests that an already-invalidated value can still be marked changed: symlink points at sym1.
231 * Invalidate symlink by changing sym1 from pointing at path to point to sym2. This only dirties
232 * (rather than changes) symlink because sym2 still points at path, so all symlink stats remain
233 * the same. Then do a null build, change sym1 back to point at path, and change symlink to not be
234 * a symlink anymore. The fact that it is not a symlink should be detected.
235 */
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000236 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000237 public void testDirtySymlink() throws Exception {
twerth5aaceb52020-04-07 06:31:56 -0700238 FilesystemValueChecker checker =
239 new FilesystemValueChecker(
240 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000241
242 Path path = fs.getPath("/foo");
243 FileSystemUtils.writeContentAsLatin1(path, "foo contents");
244 // We need the intermediate sym1 and sym2 so that we can dirty a child of symlink without
245 // actually changing the FileValue calculated for symlink (if we changed the contents of foo,
Laszlo Csomora278aec2018-03-09 04:07:31 -0800246 // the FileValue created for symlink would notice, since it stats foo).
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000247 Path sym1 = fs.getPath("/sym1");
248 Path sym2 = fs.getPath("/sym2");
249 Path symlink = fs.getPath("/bar");
250 FileSystemUtils.ensureSymbolicLink(symlink, sym1);
251 FileSystemUtils.ensureSymbolicLink(sym1, path);
252 FileSystemUtils.ensureSymbolicLink(sym2, path);
253 SkyKey fooKey =
tomlu4c9fafd2018-01-18 10:29:11 -0800254 FileValue.key(RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000255 RootedPath symlinkRootedPath =
tomlu4c9fafd2018-01-18 10:29:11 -0800256 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/bar"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000257 SkyKey symlinkKey = FileValue.key(symlinkRootedPath);
258 SkyKey symlinkFileStateKey = FileStateValue.key(symlinkRootedPath);
259 RootedPath sym1RootedPath =
tomlu4c9fafd2018-01-18 10:29:11 -0800260 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/sym1"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000261 SkyKey sym1FileStateKey = FileStateValue.key(sym1RootedPath);
262 Iterable<SkyKey> allKeys = ImmutableList.of(symlinkKey, fooKey);
263
264 // First build -- prime the graph.
Googler10028672018-10-25 12:14:34 -0700265 EvaluationResult<FileValue> result = driver.evaluate(allKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200266 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000267 FileValue symlinkValue = result.get(symlinkKey);
268 FileValue fooValue = result.get(fooKey);
lberkiaea56b32017-05-30 12:35:33 +0200269 assertWithMessage(symlinkValue.toString()).that(symlinkValue.isSymlink()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000270 // Digest is not always available, so use size as a proxy for contents.
lberkiaea56b32017-05-30 12:35:33 +0200271 assertThat(symlinkValue.getSize()).isEqualTo(fooValue.getSize());
Nathan Harmata8cd29782015-11-10 03:24:01 +0000272 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000273
274 // Before second build, move sym1 to point to sym2.
lberkiaea56b32017-05-30 12:35:33 +0200275 assertThat(sym1.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000276 FileSystemUtils.ensureSymbolicLink(sym1, sym2);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000277 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000278
279 differencer.invalidate(ImmutableList.of(sym1FileStateKey));
jhorvitz3daedc32020-07-22 18:33:55 -0700280 result = driver.evaluate(ImmutableList.of(), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200281 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000282 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000283
284 // Before third build, move sym1 back to original (so change pruning will prevent signaling of
285 // its parents, but change symlink for real.
lberkiaea56b32017-05-30 12:35:33 +0200286 assertThat(sym1.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000287 FileSystemUtils.ensureSymbolicLink(sym1, path);
lberkiaea56b32017-05-30 12:35:33 +0200288 assertThat(symlink.delete()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000289 FileSystemUtils.writeContentAsLatin1(symlink, "new symlink contents");
Nathan Harmata8cd29782015-11-10 03:24:01 +0000290 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), symlinkFileStateKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000291 differencer.invalidate(ImmutableList.of(symlinkFileStateKey));
Googler10028672018-10-25 12:14:34 -0700292 result = driver.evaluate(allKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200293 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000294 symlinkValue = result.get(symlinkKey);
lberkiaea56b32017-05-30 12:35:33 +0200295 assertWithMessage(symlinkValue.toString()).that(symlinkValue.isSymlink()).isFalse();
296 assertThat(result.get(fooKey)).isEqualTo(fooValue);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000297 assertThat(symlinkValue.getSize()).isNotEqualTo(fooValue.getSize());
Nathan Harmata8cd29782015-11-10 03:24:01 +0000298 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000299 }
300
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000301 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000302 public void testExplicitFiles() throws Exception {
twerth5aaceb52020-04-07 06:31:56 -0700303 FilesystemValueChecker checker =
304 new FilesystemValueChecker(
305 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000306
307 Path path1 = fs.getPath("/foo1");
308 Path path2 = fs.getPath("/foo2");
309 FileSystemUtils.createEmptyFile(path1);
310 FileSystemUtils.createEmptyFile(path2);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000311 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000312
313 SkyKey key1 =
314 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800315 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo1")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000316 SkyKey key2 =
317 FileStateValue.key(
tomlu4c9fafd2018-01-18 10:29:11 -0800318 RootedPath.toRootedPath(Root.absoluteRoot(fs), PathFragment.create("/foo2")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000319 Iterable<SkyKey> skyKeys = ImmutableList.of(key1, key2);
Googler10028672018-10-25 12:14:34 -0700320 EvaluationResult<SkyValue> result = driver.evaluate(skyKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200321 assertThat(result.hasError()).isFalse();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000322
Nathan Harmata8cd29782015-11-10 03:24:01 +0000323 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000324
Laszlo Csomora278aec2018-03-09 04:07:31 -0800325 // Wait for the timestamp granularity to elapse, so updating the files will observably advance
326 // their ctime.
laszlocsomor29bdf632018-03-13 02:50:46 -0700327 TimestampGranularityUtils.waitForTimestampGranularity(
328 System.currentTimeMillis(), OutErr.SYSTEM_OUT_ERR);
ajurkowski280bbe22020-08-19 11:26:20 -0700329 // Update path1's contents. This will update the file's ctime with current time indicated by the
330 // clock.
331 fs.advanceClockMillis(1);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000332 FileSystemUtils.writeContentAsLatin1(path1, "hello1");
Laszlo Csomora278aec2018-03-09 04:07:31 -0800333 // Update path2's mtime but not its contents. We expect that an mtime change suffices to update
334 // the ctime.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000335 path2.setLastModifiedTime(42);
Laszlo Csomora278aec2018-03-09 04:07:31 -0800336 // Assert that both files changed. The change detection relies, among other things, on ctime
337 // change.
Nathan Harmata8cd29782015-11-10 03:24:01 +0000338 assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), key1, key2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000339
340 differencer.invalidate(skyKeys);
Googler10028672018-10-25 12:14:34 -0700341 result = driver.evaluate(skyKeys, EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200342 assertThat(result.hasError()).isFalse();
Nathan Harmata8cd29782015-11-10 03:24:01 +0000343 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000344 }
345
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000346 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000347 public void testFileWithIOExceptionNotConsideredDirty() throws Exception {
348 Path path = fs.getPath("/testroot/foo");
349 path.getParentDirectory().createDirectory();
nharmatab4060b62017-04-04 17:11:39 +0000350 path.createSymbolicLink(PathFragment.create("bar"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000351
352 fs.readlinkThrowsIoException = true;
tomluee6a6862018-01-17 14:36:26 -0800353 SkyKey fileKey =
354 FileStateValue.key(
355 RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000356 EvaluationResult<SkyValue> result =
Googler10028672018-10-25 12:14:34 -0700357 driver.evaluate(ImmutableList.of(fileKey), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200358 assertThat(result.hasError()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000359
360 fs.readlinkThrowsIoException = false;
twerth5aaceb52020-04-07 06:31:56 -0700361 FilesystemValueChecker checker =
362 new FilesystemValueChecker(
363 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000364 Diff diff = getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000365 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
366 assertThat(diff.changedKeysWithNewValues()).isEmpty();
367 }
368
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000369 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000370 public void testFilesInCycleNotConsideredDirty() throws Exception {
371 Path path1 = pkgRoot.getRelative("foo1");
372 Path path2 = pkgRoot.getRelative("foo2");
373 Path path3 = pkgRoot.getRelative("foo3");
374 FileSystemUtils.ensureSymbolicLink(path1, path2);
375 FileSystemUtils.ensureSymbolicLink(path2, path3);
376 FileSystemUtils.ensureSymbolicLink(path3, path1);
tomluee6a6862018-01-17 14:36:26 -0800377 SkyKey fileKey1 = FileValue.key(RootedPath.toRootedPath(Root.fromPath(pkgRoot), path1));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000378
379 EvaluationResult<SkyValue> result =
Googler10028672018-10-25 12:14:34 -0700380 driver.evaluate(ImmutableList.of(fileKey1), EVALUATION_OPTIONS);
lberkiaea56b32017-05-30 12:35:33 +0200381 assertThat(result.hasError()).isTrue();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000382
twerth5aaceb52020-04-07 06:31:56 -0700383 FilesystemValueChecker checker =
384 new FilesystemValueChecker(
385 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST);
Nathan Harmata8cd29782015-11-10 03:24:01 +0000386 Diff diff = getDirtyFilesystemKeys(evaluator, checker);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000387 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
388 assertThat(diff.changedKeysWithNewValues()).isEmpty();
389 }
390
jhorvitz3daedc32020-07-22 18:33:55 -0700391 public void checkDirtyActions(BatchStat batchStatter) throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000392 Artifact out1 = createDerivedArtifact("fiz");
393 Artifact out2 = createDerivedArtifact("pop");
394
395 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "hello");
396 FileSystemUtils.writeContentAsLatin1(out2.getPath(), "fizzlepop");
397
lberki41cfcc72019-08-05 02:55:36 -0700398 TimestampGranularityMonitor tsgm = new TimestampGranularityMonitor(BlazeClock.instance());
ajurkowski280bbe22020-08-19 11:26:20 -0700399 SkyKey actionKey1 = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
400 SkyKey actionKey2 = ActionLookupData.create(ACTION_LOOKUP_KEY, 1);
lberki41cfcc72019-08-05 02:55:36 -0700401
402 pretendBuildTwoArtifacts(out1, actionKey1, out2, actionKey2, batchStatter, tsgm);
403
404 // Change the file but not its size
405 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "hallo");
406 checkActionDirtiedByFile(out1, actionKey1, batchStatter, tsgm);
407 pretendBuildTwoArtifacts(out1, actionKey1, out2, actionKey2, batchStatter, tsgm);
408
409 // Now try with a different size
410 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "hallo2");
411 checkActionDirtiedByFile(out1, actionKey1, batchStatter, tsgm);
412 }
413
414 private void pretendBuildTwoArtifacts(
415 Artifact out1,
416 SkyKey actionKey1,
417 Artifact out2,
418 SkyKey actionKey2,
419 BatchStat batchStatter,
420 TimestampGranularityMonitor tsgm)
421 throws InterruptedException {
422 EvaluationContext evaluationContext =
423 EvaluationContext.newBuilder()
424 .setKeepGoing(false)
425 .setNumThreads(1)
michajlo7a485be2020-07-30 11:08:46 -0700426 .setEventHandler(NullEventHandler.INSTANCE)
lberki41cfcc72019-08-05 02:55:36 -0700427 .build();
428
429 tsgm.setCommandStartTime();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000430 differencer.inject(
431 ImmutableMap.<SkyKey, SkyValue>of(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000432 actionKey1,
433 actionValue(
434 new TestAction(
ulfjack1e1a7752019-12-10 21:17:58 -0800435 Runnables.doNothing(),
436 NestedSetBuilder.emptySet(Order.STABLE_ORDER),
437 ImmutableSet.of(out1))),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000438 actionKey2,
439 actionValue(
440 new TestAction(
lberkic35878a2019-08-01 02:28:54 -0700441 Runnables.doNothing(),
ulfjack1e1a7752019-12-10 21:17:58 -0800442 NestedSetBuilder.emptySet(Order.STABLE_ORDER),
lberkic35878a2019-08-01 02:28:54 -0700443 ImmutableSet.of(out2)))));
jhorvitz3daedc32020-07-22 18:33:55 -0700444 assertThat(driver.evaluate(ImmutableList.of(), evaluationContext).hasError()).isFalse();
Eric Fellheimere6590722015-11-17 17:07:48 +0000445 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700446 new FilesystemValueChecker(
447 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000448 .getDirtyActionValues(
steinman39c00d22020-03-20 15:23:10 -0700449 evaluator.getValues(),
450 batchStatter,
451 ModifiedFileSet.EVERYTHING_MODIFIED,
452 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700453 .isEmpty();
454
455 tsgm.waitForTimestampGranularity(OutErr.SYSTEM_OUT_ERR);
456 }
457
458 private void checkActionDirtiedByFile(
459 Artifact file, SkyKey actionKey, BatchStat batchStatter, TimestampGranularityMonitor tsgm)
460 throws InterruptedException {
Ulf Adamsc73051c62016-03-23 09:18:13 +0000461 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700462 new FilesystemValueChecker(
463 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
lberki41cfcc72019-08-05 02:55:36 -0700464 .getDirtyActionValues(
steinman39c00d22020-03-20 15:23:10 -0700465 evaluator.getValues(),
466 batchStatter,
467 ModifiedFileSet.EVERYTHING_MODIFIED,
468 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700469 .containsExactly(actionKey);
470 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700471 new FilesystemValueChecker(
472 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000473 .getDirtyActionValues(
474 evaluator.getValues(),
475 batchStatter,
ajurkowskifbd44272021-12-02 16:05:49 -0800476 ModifiedFileSet.EVERYTHING_DELETED,
477 /* trustRemoteArtifacts= */ false))
478 .containsExactly(actionKey);
479 assertThat(
480 new FilesystemValueChecker(
481 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
482 .getDirtyActionValues(
483 evaluator.getValues(),
484 batchStatter,
steinman39c00d22020-03-20 15:23:10 -0700485 new ModifiedFileSet.Builder().modify(file.getExecPath()).build(),
486 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700487 .containsExactly(actionKey);
Ulf Adamsc73051c62016-03-23 09:18:13 +0000488 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700489 new FilesystemValueChecker(
490 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
lberki41cfcc72019-08-05 02:55:36 -0700491 .getDirtyActionValues(
492 evaluator.getValues(),
493 batchStatter,
494 new ModifiedFileSet.Builder()
495 .modify(file.getExecPath().getParentDirectory())
steinman39c00d22020-03-20 15:23:10 -0700496 .build(),
497 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700498 .isEmpty();
Eric Fellheimere6590722015-11-17 17:07:48 +0000499 assertThat(
twerth5aaceb52020-04-07 06:31:56 -0700500 new FilesystemValueChecker(
501 tsgm, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
lberki41cfcc72019-08-05 02:55:36 -0700502 .getDirtyActionValues(
steinman39c00d22020-03-20 15:23:10 -0700503 evaluator.getValues(),
504 batchStatter,
505 ModifiedFileSet.NOTHING_MODIFIED,
506 /* trustRemoteArtifacts= */ false))
lberki41cfcc72019-08-05 02:55:36 -0700507 .isEmpty();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000508 }
509
ajurkowskifbd44272021-12-02 16:05:49 -0800510 enum ModifiedSetReporting {
511 EVERYTHING_MODIFIED {
512 @Override
513 ModifiedFileSet getModifiedFileSet(PathFragment path) {
514 return ModifiedFileSet.EVERYTHING_MODIFIED;
515 }
516 },
517 EVERYTHING_DELETED {
518 @Override
519 ModifiedFileSet getModifiedFileSet(PathFragment path) {
520 return ModifiedFileSet.EVERYTHING_DELETED;
521 }
522 },
523 SINGLE_PATH {
524 @Override
525 ModifiedFileSet getModifiedFileSet(PathFragment path) {
526 return ModifiedFileSet.builder().modify(path).build();
527 }
528 };
529
530 abstract ModifiedFileSet getModifiedFileSet(PathFragment path);
531 }
532
ajurkowskibdc60b02021-11-24 08:47:05 -0800533 @Test
534 public void getDirtyActionValues_touchedTreeDirectory_returnsEmptyDiff(
535 @TestParameter BatchStatMode batchStatMode,
536 @TestParameter({"", "subdir"}) String touchedTreePath,
ajurkowskifbd44272021-12-02 16:05:49 -0800537 @TestParameter ModifiedSetReporting modifiedSet)
ajurkowskibdc60b02021-11-24 08:47:05 -0800538 throws Exception {
539 SpecialArtifact tree = createTreeArtifact("tree");
540 TreeFileArtifact treeFile = TreeFileArtifact.createTreeOutput(tree, "subdir/file");
541 FileSystemUtils.writeIsoLatin1(treeFile.getPath(), "text");
542 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
543 differencer.inject(
544 ImmutableMap.of(actionKey, actionValueWithTreeArtifacts(ImmutableList.of(treeFile))));
545 evaluate();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000546
ajurkowskibdc60b02021-11-24 08:47:05 -0800547 FileSystemUtils.touchFile(tree.getPath().getRelative(touchedTreePath));
548 assertThat(
549 new FilesystemValueChecker(
550 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
551 .getDirtyActionValues(
552 evaluator.getValues(),
553 batchStatMode.getBatchStat(fs),
ajurkowskifbd44272021-12-02 16:05:49 -0800554 modifiedSet.getModifiedFileSet(tree.getExecPath()),
ajurkowskibdc60b02021-11-24 08:47:05 -0800555 /*trustRemoteArtifacts=*/ false))
556 .isEmpty();
557 }
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000558
ajurkowskibdc60b02021-11-24 08:47:05 -0800559 @Test
560 public void getDirtyActionValues_deleteEmptyTreeDirectory_returnsTreeKey(
ajurkowskifbd44272021-12-02 16:05:49 -0800561 @TestParameter BatchStatMode batchStatMode, @TestParameter ModifiedSetReporting modifiedSet)
ajurkowskibdc60b02021-11-24 08:47:05 -0800562 throws Exception {
563 SpecialArtifact tree = createTreeArtifact("tree");
564 tree.getPath().createDirectoryAndParents();
565 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
566 differencer.inject(
567 ImmutableMap.of(actionKey, actionValueWithTreeArtifact(tree, TreeArtifactValue.empty())));
568 evaluate();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000569
ajurkowskibdc60b02021-11-24 08:47:05 -0800570 assertThat(tree.getPath().delete()).isTrue();
571 assertThat(
572 new FilesystemValueChecker(
573 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
574 .getDirtyActionValues(
575 evaluator.getValues(),
576 batchStatMode.getBatchStat(fs),
ajurkowskifbd44272021-12-02 16:05:49 -0800577 modifiedSet.getModifiedFileSet(tree.getExecPath()),
ajurkowskibdc60b02021-11-24 08:47:05 -0800578 /*trustRemoteArtifacts=*/ false))
579 .containsExactly(actionKey);
580 }
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000581
ajurkowskibdc60b02021-11-24 08:47:05 -0800582 @Test
583 public void getDirtyActionValues_treeDirectoryReplacedWithSymlink_returnsTreeKey(
584 @TestParameter BatchStatMode batchStatMode) throws Exception {
585 SpecialArtifact tree = createTreeArtifact("tree");
586 tree.getPath().createDirectoryAndParents();
587 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
588 differencer.inject(
589 ImmutableMap.of(actionKey, actionValueWithTreeArtifact(tree, TreeArtifactValue.empty())));
590 evaluate();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000591
ajurkowskibdc60b02021-11-24 08:47:05 -0800592 Path dummyEmptyDir = fs.getPath("/bin").getRelative("dir");
593 dummyEmptyDir.createDirectoryAndParents();
594 assertThat(tree.getPath().delete()).isTrue();
595 tree.getPath().createSymbolicLink(dummyEmptyDir);
Rumou Duan45e8e572016-06-17 16:43:44 +0000596
ajurkowskibdc60b02021-11-24 08:47:05 -0800597 assertThat(
598 new FilesystemValueChecker(
599 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
600 .getDirtyActionValues(
601 evaluator.getValues(),
602 batchStatMode.getBatchStat(fs),
603 ModifiedFileSet.EVERYTHING_MODIFIED,
604 /* trustRemoteArtifacts= */ false))
605 .containsExactly(actionKey); // Symbolic links should count as dirty
606 }
607
608 @Test
609 public void getDirtyActionValues_modifiedTreeFile_returnsTreeKey(
ajurkowskifbd44272021-12-02 16:05:49 -0800610 @TestParameter BatchStatMode batchStatMode, @TestParameter ModifiedSetReporting modifiedSet)
ajurkowskibdc60b02021-11-24 08:47:05 -0800611 throws Exception {
612 SpecialArtifact tree = createTreeArtifact("tree");
613 TreeFileArtifact treeFile = TreeFileArtifact.createTreeOutput(tree, "file");
614 FileSystemUtils.writeIsoLatin1(treeFile.getPath(), "text");
615 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
616 differencer.inject(
617 ImmutableMap.of(actionKey, actionValueWithTreeArtifacts(ImmutableList.of(treeFile))));
618 evaluate();
619
620 FileSystemUtils.writeIsoLatin1(treeFile.getPath(), "other text");
621 assertThat(
622 new FilesystemValueChecker(
623 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
624 .getDirtyActionValues(
625 evaluator.getValues(),
626 batchStatMode.getBatchStat(fs),
ajurkowskifbd44272021-12-02 16:05:49 -0800627 modifiedSet.getModifiedFileSet(treeFile.getExecPath()),
ajurkowskibdc60b02021-11-24 08:47:05 -0800628 /*trustRemoteArtifacts=*/ false))
629 .containsExactly(actionKey);
630 }
631
632 @Test
633 public void getDirtyActionValues_addedTreeFile_returnsTreeKey(
ajurkowskifbd44272021-12-02 16:05:49 -0800634 @TestParameter BatchStatMode batchStatMode, @TestParameter ModifiedSetReporting modifiedSet)
ajurkowskibdc60b02021-11-24 08:47:05 -0800635 throws Exception {
636 SpecialArtifact tree = createTreeArtifact("tree");
637 TreeFileArtifact treeFile = TreeFileArtifact.createTreeOutput(tree, "file1");
638 FileSystemUtils.writeIsoLatin1(treeFile.getPath());
639 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
640 differencer.inject(
641 ImmutableMap.of(actionKey, actionValueWithTreeArtifacts(ImmutableList.of(treeFile))));
642 evaluate();
643
644 TreeFileArtifact newFile = TreeFileArtifact.createTreeOutput(tree, "file2");
645 FileSystemUtils.writeIsoLatin1(newFile.getPath());
646 assertThat(
647 new FilesystemValueChecker(
648 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
649 .getDirtyActionValues(
650 evaluator.getValues(),
651 batchStatMode.getBatchStat(fs),
ajurkowskifbd44272021-12-02 16:05:49 -0800652 modifiedSet.getModifiedFileSet(newFile.getExecPath()),
ajurkowskibdc60b02021-11-24 08:47:05 -0800653 /*trustRemoteArtifacts=*/ false))
654 .containsExactly(actionKey);
655 }
656
657 @Test
658 public void getDirtyActionValues_addedTreeFileToEmptyTree_returnsTreeKey(
ajurkowskifbd44272021-12-02 16:05:49 -0800659 @TestParameter BatchStatMode batchStatMode, @TestParameter ModifiedSetReporting modifiedSet)
ajurkowskibdc60b02021-11-24 08:47:05 -0800660 throws Exception {
661 SpecialArtifact tree = createTreeArtifact("tree");
662 tree.getPath().createDirectoryAndParents();
663 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
664 differencer.inject(
665 ImmutableMap.of(actionKey, actionValueWithTreeArtifact(tree, TreeArtifactValue.empty())));
666 evaluate();
667
668 TreeFileArtifact newFile = TreeFileArtifact.createTreeOutput(tree, "file");
669 FileSystemUtils.writeIsoLatin1(newFile.getPath());
670 assertThat(
671 new FilesystemValueChecker(
672 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
673 .getDirtyActionValues(
674 evaluator.getValues(),
675 batchStatMode.getBatchStat(fs),
ajurkowskifbd44272021-12-02 16:05:49 -0800676 modifiedSet.getModifiedFileSet(newFile.getExecPath()),
ajurkowskibdc60b02021-11-24 08:47:05 -0800677 /*trustRemoteArtifacts=*/ false))
678 .containsExactly(actionKey);
679 }
680
681 @Test
682 public void getDirtyActionValues_deletedTreeFile_returnsTreeKey(
ajurkowskifbd44272021-12-02 16:05:49 -0800683 @TestParameter BatchStatMode batchStatMode, @TestParameter ModifiedSetReporting modifiedSet)
ajurkowskibdc60b02021-11-24 08:47:05 -0800684 throws Exception {
685 SpecialArtifact tree = createTreeArtifact("tree");
686 TreeFileArtifact treeFile = TreeFileArtifact.createTreeOutput(tree, "file");
687 FileSystemUtils.writeIsoLatin1(treeFile.getPath());
688 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
689 differencer.inject(
690 ImmutableMap.of(actionKey, actionValueWithTreeArtifacts(ImmutableList.of(treeFile))));
691 evaluate();
692
693 assertThat(treeFile.getPath().delete()).isTrue();
694 assertThat(
695 new FilesystemValueChecker(
696 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
697 .getDirtyActionValues(
698 evaluator.getValues(),
699 batchStatMode.getBatchStat(fs),
ajurkowskifbd44272021-12-02 16:05:49 -0800700 modifiedSet.getModifiedFileSet(treeFile.getExecPath()),
ajurkowskibdc60b02021-11-24 08:47:05 -0800701 /*trustRemoteArtifacts=*/ false))
702 .containsExactly(actionKey);
703 }
704
705 @Test
706 public void getDirtyActionValues_everythingModified_returnsAllKeys(
707 @TestParameter BatchStatMode batchStatMode) throws Exception {
708 SpecialArtifact tree1 = createTreeArtifact("tree1");
709 TreeFileArtifact tree1File = TreeFileArtifact.createTreeOutput(tree1, "file");
710 FileSystemUtils.writeIsoLatin1(tree1File.getPath(), "text");
711 SpecialArtifact tree2 = createTreeArtifact("tree2");
712 TreeFileArtifact tree2File = TreeFileArtifact.createTreeOutput(tree2, "file");
713 FileSystemUtils.writeIsoLatin1(tree2File.getPath());
ajurkowski280bbe22020-08-19 11:26:20 -0700714 SkyKey actionKey1 = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
715 SkyKey actionKey2 = ActionLookupData.create(ACTION_LOOKUP_KEY, 1);
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000716 differencer.inject(
jhorvitz3daedc32020-07-22 18:33:55 -0700717 ImmutableMap.of(
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000718 actionKey1,
ajurkowskibdc60b02021-11-24 08:47:05 -0800719 actionValueWithTreeArtifacts(ImmutableList.of(tree1File)),
Janak Ramakrishnan5d152692017-03-20 22:07:14 +0000720 actionKey2,
ajurkowskibdc60b02021-11-24 08:47:05 -0800721 actionValueWithTreeArtifacts(ImmutableList.of(tree1File))));
722 evaluate();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000723
ajurkowskibdc60b02021-11-24 08:47:05 -0800724 FileSystemUtils.writeIsoLatin1(tree1File.getPath(), "new text");
725 assertThat(tree2File.getPath().delete()).isTrue();
726 assertThat(
727 new FilesystemValueChecker(
728 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
729 .getDirtyActionValues(
730 evaluator.getValues(),
731 batchStatMode.getBatchStat(fs),
732 ModifiedFileSet.EVERYTHING_MODIFIED,
733 /*trustRemoteArtifacts=*/ false))
734 .containsExactly(actionKey1, actionKey2);
735 }
736
737 @Test
738 public void getDirtyActionValues_changedFileNotInModifiedSet_returnsKeysFromSetOnly(
739 @TestParameter BatchStatMode batchStatMode, @TestParameter boolean reportFirst)
740 throws Exception {
741 SpecialArtifact tree1 = createTreeArtifact("tree1");
742 TreeFileArtifact tree1File = TreeFileArtifact.createTreeOutput(tree1, "file");
743 FileSystemUtils.writeIsoLatin1(tree1File.getPath(), "text");
744 SpecialArtifact tree2 = createTreeArtifact("tree2");
745 TreeFileArtifact tree2File = TreeFileArtifact.createTreeOutput(tree2, "file");
746 FileSystemUtils.writeIsoLatin1(tree2File.getPath());
747 SkyKey actionKey1 = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
748 SkyKey actionKey2 = ActionLookupData.create(ACTION_LOOKUP_KEY, 1);
749 differencer.inject(
750 ImmutableMap.of(
751 actionKey1,
752 actionValueWithTreeArtifacts(ImmutableList.of(tree1File)),
753 actionKey2,
754 actionValueWithTreeArtifacts(ImmutableList.of(tree2File))));
755 evaluate();
756
757 FileSystemUtils.writeIsoLatin1(tree1File.getPath(), "new text");
758 assertThat(tree2File.getPath().delete()).isTrue();
759 assertThat(
760 new FilesystemValueChecker(
761 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
762 .getDirtyActionValues(
763 evaluator.getValues(),
764 batchStatMode.getBatchStat(fs),
765 ModifiedFileSet.builder()
766 .modify((reportFirst ? tree1File : tree2File).getExecPath())
767 .build(),
768 /*trustRemoteArtifacts=*/ false))
769 .containsExactly(reportFirst ? actionKey1 : actionKey2);
770 }
771
772 @Test
773 public void getDirtyActionValues_middleFileSkippedInModifiedFileSet_returnsKeysFromSetOnly(
774 @TestParameter BatchStatMode batchStatMode) throws Exception {
775 SpecialArtifact treeA = createTreeArtifact("a_tree");
776 TreeFileArtifact treeAFile = TreeFileArtifact.createTreeOutput(treeA, "file");
777 FileSystemUtils.writeIsoLatin1(treeAFile.getPath());
778 SpecialArtifact treeB = createTreeArtifact("b_tree");
779 TreeFileArtifact treeBFile = TreeFileArtifact.createTreeOutput(treeB, "file");
780 FileSystemUtils.writeIsoLatin1(treeBFile.getPath());
781 SpecialArtifact treeC = createTreeArtifact("c_tree");
782 TreeFileArtifact treeCFile = TreeFileArtifact.createTreeOutput(treeC, "file");
783 FileSystemUtils.writeIsoLatin1(treeCFile.getPath());
784 SkyKey actionKey1 = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
785 SkyKey actionKey2 = ActionLookupData.create(ACTION_LOOKUP_KEY, 1);
786 SkyKey actionKey3 = ActionLookupData.create(ACTION_LOOKUP_KEY, 2);
787 differencer.inject(
788 ImmutableMap.of(
789 actionKey1,
790 actionValueWithTreeArtifacts(ImmutableList.of(treeAFile)),
791 actionKey2,
792 actionValueWithTreeArtifacts(ImmutableList.of(treeBFile)),
793 actionKey3,
794 actionValueWithTreeArtifacts(ImmutableList.of(treeCFile))));
795 evaluate();
796
797 assertThat(treeAFile.getPath().delete()).isTrue();
798 assertThat(treeBFile.getPath().delete()).isTrue();
799 assertThat(treeCFile.getPath().delete()).isTrue();
800 assertThat(
801 new FilesystemValueChecker(
802 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
803 .getDirtyActionValues(
804 evaluator.getValues(),
805 batchStatMode.getBatchStat(fs),
806 ModifiedFileSet.builder()
807 .modify(treeAFile.getExecPath())
808 .modify(treeCFile.getExecPath())
809 .build(),
810 /*trustRemoteArtifacts=*/ false))
811 .containsExactly(actionKey1, actionKey3);
812 }
813
814 @Test
815 public void getDirtyActionValues_nothingModified_returnsEmptyDiff(
816 @TestParameter BatchStatMode batchStatMode) throws Exception {
817 SpecialArtifact tree = createTreeArtifact("tree");
818 TreeFileArtifact treeFile = TreeFileArtifact.createTreeOutput(tree, "file");
819 FileSystemUtils.writeIsoLatin1(treeFile.getPath());
820 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
821 differencer.inject(
822 ImmutableMap.of(actionKey, actionValueWithTreeArtifacts(ImmutableList.of(treeFile))));
823 evaluate();
824
825 assertThat(treeFile.getPath().delete()).isTrue();
826 assertThat(
827 new FilesystemValueChecker(
828 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST)
829 .getDirtyActionValues(
830 evaluator.getValues(),
831 batchStatMode.getBatchStat(fs),
832 ModifiedFileSet.NOTHING_MODIFIED,
833 /*trustRemoteArtifacts=*/ false))
834 .isEmpty();
835 }
836
837 private void evaluate() throws InterruptedException {
Googler10028672018-10-25 12:14:34 -0700838 EvaluationContext evaluationContext =
839 EvaluationContext.newBuilder()
840 .setKeepGoing(false)
841 .setNumThreads(1)
michajlo7a485be2020-07-30 11:08:46 -0700842 .setEventHandler(NullEventHandler.INSTANCE)
Googler10028672018-10-25 12:14:34 -0700843 .build();
Googler1d8d1382020-05-18 12:10:49 -0700844 assertThat(driver.evaluate(ImmutableList.of(), evaluationContext).hasError()).isFalse();
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000845 }
846
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000847 private Artifact createDerivedArtifact(String relPath) throws IOException {
janakr448f1cf2020-03-30 09:12:44 -0700848 String outSegment = "bin";
849 Path outputPath = fs.getPath("/" + outSegment);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000850 outputPath.createDirectory();
janakraea05602019-05-22 15:41:29 -0700851 return ActionsTestUtil.createArtifact(
Googlerf0b0c392021-01-27 17:56:52 -0800852 ArtifactRoot.asDerivedRoot(fs.getPath("/"), RootType.Output, outSegment),
Googler147c2872021-01-21 08:47:48 -0800853 outputPath.getRelative(relPath));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000854 }
855
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000856 @Test
steinman7184cb32020-04-20 14:33:03 -0700857 // TODO(b/154337187): Remove the following annotation to re-enable once this test is de-flaked.
858 @Ignore
859 public void testDirtyActions() throws Exception {
jhorvitz3daedc32020-07-22 18:33:55 -0700860 checkDirtyActions(null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000861 }
862
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000863 @Test
steinman7184cb32020-04-20 14:33:03 -0700864 // TODO(b/154337187): Remove the following annotation to re-enable once this test is de-flaked.
865 @Ignore
866 public void testDirtyActionsBatchStat() throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000867 checkDirtyActions(
868 new BatchStat() {
869 @Override
870 public List<FileStatusWithDigest> batchStat(
871 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
872 throws IOException {
873 List<FileStatusWithDigest> stats = new ArrayList<>();
874 for (PathFragment pathFrag : paths) {
875 stats.add(
876 FileStatusWithDigestAdapter.adapt(
tomlu6c919062018-01-11 17:32:09 -0800877 fs.getPath("/").getRelative(pathFrag).statIfFound(Symlinks.NOFOLLOW)));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000878 }
879 return stats;
880 }
jhorvitz3daedc32020-07-22 18:33:55 -0700881 });
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000882 }
883
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000884 @Test
steinman68e78f52020-04-23 12:30:19 -0700885 // TODO(b/154337187): Remove the following annotation to re-enable once this test is de-flaked.
886 @Ignore
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000887 public void testDirtyActionsBatchStatWithDigest() throws Exception {
888 checkDirtyActions(
889 new BatchStat() {
890 @Override
891 public List<FileStatusWithDigest> batchStat(
892 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
893 throws IOException {
894 List<FileStatusWithDigest> stats = new ArrayList<>();
895 for (PathFragment pathFrag : paths) {
tomlu6c919062018-01-11 17:32:09 -0800896 final Path path = fs.getPath("/").getRelative(pathFrag);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000897 stats.add(statWithDigest(path, path.statIfFound(Symlinks.NOFOLLOW)));
898 }
899 return stats;
900 }
jhorvitz3daedc32020-07-22 18:33:55 -0700901 });
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000902 }
903
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000904 @Test
steinman68e78f52020-04-23 12:30:19 -0700905 // TODO(b/154337187): Remove the following annotation to re-enable once this test is de-flaked.
906 @Ignore
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000907 public void testDirtyActionsBatchStatFallback() throws Exception {
908 checkDirtyActions(
909 new BatchStat() {
910 @Override
911 public List<FileStatusWithDigest> batchStat(
912 boolean useDigest, boolean includeLinks, Iterable<PathFragment> paths)
913 throws IOException {
914 throw new IOException("try again");
915 }
jhorvitz3daedc32020-07-22 18:33:55 -0700916 });
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000917 }
918
Michael Thvedte4a7b0792016-02-09 12:15:53 +0000919 // TODO(bazel-team): Add some tests for FileSystemValueChecker#changedKeys*() methods.
920 // Presently these appear to be untested.
921
ajurkowskie2982912020-04-09 10:32:08 -0700922 private static ActionExecutionValue actionValue(Action action) {
lberkif7eee1e2019-07-31 05:49:10 -0700923 Map<Artifact, FileArtifactValue> artifactData = new HashMap<>();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000924 for (Artifact output : action.getOutputs()) {
925 try {
926 Path path = output.getPath();
lberkic35878a2019-08-01 02:28:54 -0700927 FileArtifactValue noDigest =
928 ActionMetadataHandler.fileArtifactValueFromArtifact(
929 output,
930 FileStatusWithDigestAdapter.adapt(path.statIfFound(Symlinks.NOFOLLOW)),
931 null);
932 FileArtifactValue withDigest =
933 FileArtifactValue.createFromInjectedDigest(
934 noDigest, path.getDigest(), !output.isConstantMetadata());
935 artifactData.put(output, withDigest);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000936 } catch (IOException e) {
937 throw new IllegalStateException(e);
938 }
939 }
janakrb9d8d582018-06-13 21:57:19 -0700940 return ActionExecutionValue.create(
jhorvitz8470e432021-12-14 09:51:17 -0800941 ImmutableMap.copyOf(artifactData),
Googler974879d2020-05-27 13:25:52 -0700942 /*treeArtifactData=*/ ImmutableMap.of(),
janakrb9d8d582018-06-13 21:57:19 -0700943 /*outputSymlinks=*/ null,
shahanef6f4cf2018-06-26 11:24:59 -0700944 /*discoveredModules=*/ null,
jhorvitz8470e432021-12-14 09:51:17 -0800945 /*shareable=*/ true);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000946 }
947
jhorvitzed7ec3b2020-07-24 15:08:03 -0700948 private static ActionExecutionValue actionValueWithTreeArtifact(
949 SpecialArtifact output, TreeArtifactValue tree) {
buchgr4992ae22019-03-20 04:23:32 -0700950 return ActionExecutionValue.create(
buchgr4992ae22019-03-20 04:23:32 -0700951 ImmutableMap.of(),
jhorvitzed7ec3b2020-07-24 15:08:03 -0700952 ImmutableMap.of(output, tree),
953 /*outputSymlinks=*/ null,
954 /*discoveredModules=*/ null,
jhorvitz8470e432021-12-14 09:51:17 -0800955 /*shareable=*/ true);
buchgr4992ae22019-03-20 04:23:32 -0700956 }
957
ajurkowskie2982912020-04-09 10:32:08 -0700958 private static ActionExecutionValue actionValueWithRemoteArtifact(
buchgr4992ae22019-03-20 04:23:32 -0700959 Artifact output, RemoteFileArtifactValue value) {
960 return ActionExecutionValue.create(
jhorvitzed7ec3b2020-07-24 15:08:03 -0700961 ImmutableMap.of(output, value),
lberkic35878a2019-08-01 02:28:54 -0700962 ImmutableMap.of(),
jhorvitzed7ec3b2020-07-24 15:08:03 -0700963 /*outputSymlinks=*/ null,
964 /*discoveredModules=*/ null,
965 /*actionDependsOnBuildId=*/ false);
buchgr4992ae22019-03-20 04:23:32 -0700966 }
967
968 private RemoteFileArtifactValue createRemoteFileArtifactValue(String contents) {
969 byte[] data = contents.getBytes();
970 DigestHashFunction hashFn = fs.getDigestFunction();
971 HashCode hash = hashFn.getHashFunction().hashBytes(data);
George Gensure3ef8fb92020-05-06 09:49:48 -0700972 return new RemoteFileArtifactValue(hash.asBytes(), data.length, -1, "action-id");
buchgr4992ae22019-03-20 04:23:32 -0700973 }
974
975 @Test
976 public void testRemoteAndLocalArtifacts() throws Exception {
977 // Test that injected remote artifacts are trusted by the FileSystemValueChecker
steinman39c00d22020-03-20 15:23:10 -0700978 // if it is configured to trust remote artifacts, and that local files always take precedence
979 // over remote files.
ajurkowski280bbe22020-08-19 11:26:20 -0700980 SkyKey actionKey1 = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
981 SkyKey actionKey2 = ActionLookupData.create(ACTION_LOOKUP_KEY, 1);
buchgr4992ae22019-03-20 04:23:32 -0700982
983 Artifact out1 = createDerivedArtifact("foo");
984 Artifact out2 = createDerivedArtifact("bar");
985 Map<SkyKey, SkyValue> metadataToInject = new HashMap<>();
986 metadataToInject.put(
987 actionKey1,
988 actionValueWithRemoteArtifact(out1, createRemoteFileArtifactValue("foo-content")));
989 metadataToInject.put(
990 actionKey2,
991 actionValueWithRemoteArtifact(out2, createRemoteFileArtifactValue("bar-content")));
992 differencer.inject(metadataToInject);
993
994 EvaluationContext evaluationContext =
995 EvaluationContext.newBuilder()
996 .setKeepGoing(false)
997 .setNumThreads(1)
michajlo7a485be2020-07-30 11:08:46 -0700998 .setEventHandler(NullEventHandler.INSTANCE)
buchgr4992ae22019-03-20 04:23:32 -0700999 .build();
1000 assertThat(
1001 driver.evaluate(ImmutableList.of(actionKey1, actionKey2), evaluationContext).hasError())
1002 .isFalse();
1003 assertThat(
twerth5aaceb52020-04-07 06:31:56 -07001004 new FilesystemValueChecker(
1005 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
buchgr4992ae22019-03-20 04:23:32 -07001006 .getDirtyActionValues(
1007 evaluator.getValues(),
1008 /* batchStatter= */ null,
steinman39c00d22020-03-20 15:23:10 -07001009 ModifiedFileSet.EVERYTHING_MODIFIED,
1010 /* trustRemoteArtifacts= */ true))
buchgr4992ae22019-03-20 04:23:32 -07001011 .isEmpty();
1012
1013 // Create the "out1" artifact on the filesystem and test that it invalidates the generating
1014 // action's SkyKey.
1015 FileSystemUtils.writeContentAsLatin1(out1.getPath(), "new-foo-content");
1016 assertThat(
twerth5aaceb52020-04-07 06:31:56 -07001017 new FilesystemValueChecker(
1018 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
buchgr4992ae22019-03-20 04:23:32 -07001019 .getDirtyActionValues(
1020 evaluator.getValues(),
1021 /* batchStatter= */ null,
steinman39c00d22020-03-20 15:23:10 -07001022 ModifiedFileSet.EVERYTHING_MODIFIED,
1023 /* trustRemoteArtifacts= */ true))
buchgr4992ae22019-03-20 04:23:32 -07001024 .containsExactly(actionKey1);
1025 }
1026
1027 @Test
1028 public void testRemoteAndLocalTreeArtifacts() throws Exception {
1029 // Test that injected remote tree artifacts are trusted by the FileSystemValueChecker
1030 // and that local files always takes preference over remote files.
ajurkowski280bbe22020-08-19 11:26:20 -07001031 SkyKey actionKey = ActionLookupData.create(ACTION_LOOKUP_KEY, 0);
buchgr4992ae22019-03-20 04:23:32 -07001032
1033 SpecialArtifact treeArtifact = createTreeArtifact("dir");
1034 treeArtifact.getPath().createDirectoryAndParents();
jhorvitzed7ec3b2020-07-24 15:08:03 -07001035 TreeArtifactValue tree =
1036 TreeArtifactValue.newBuilder(treeArtifact)
1037 .putChild(
1038 TreeFileArtifact.createTreeOutput(treeArtifact, "foo"),
1039 createRemoteFileArtifactValue("foo-content"))
1040 .putChild(
1041 TreeFileArtifact.createTreeOutput(treeArtifact, "bar"),
1042 createRemoteFileArtifactValue("bar-content"))
1043 .build();
buchgr4992ae22019-03-20 04:23:32 -07001044
jhorvitzed7ec3b2020-07-24 15:08:03 -07001045 differencer.inject(ImmutableMap.of(actionKey, actionValueWithTreeArtifact(treeArtifact, tree)));
buchgr4992ae22019-03-20 04:23:32 -07001046
1047 EvaluationContext evaluationContext =
1048 EvaluationContext.newBuilder()
1049 .setKeepGoing(false)
1050 .setNumThreads(1)
michajlo7a485be2020-07-30 11:08:46 -07001051 .setEventHandler(NullEventHandler.INSTANCE)
buchgr4992ae22019-03-20 04:23:32 -07001052 .build();
1053 assertThat(driver.evaluate(ImmutableList.of(actionKey), evaluationContext).hasError())
1054 .isFalse();
1055 assertThat(
twerth5aaceb52020-04-07 06:31:56 -07001056 new FilesystemValueChecker(
1057 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
buchgr4992ae22019-03-20 04:23:32 -07001058 .getDirtyActionValues(
1059 evaluator.getValues(),
1060 /* batchStatter= */ null,
steinman39c00d22020-03-20 15:23:10 -07001061 ModifiedFileSet.EVERYTHING_MODIFIED,
1062 /* trustRemoteArtifacts= */ false))
buchgr4992ae22019-03-20 04:23:32 -07001063 .isEmpty();
1064
1065 // Create dir/foo on the local disk and test that it invalidates the associated sky key.
Googler1d8d1382020-05-18 12:10:49 -07001066 TreeFileArtifact fooArtifact = TreeFileArtifact.createTreeOutput(treeArtifact, "foo");
buchgr4992ae22019-03-20 04:23:32 -07001067 FileSystemUtils.writeContentAsLatin1(fooArtifact.getPath(), "new-foo-content");
1068 assertThat(
twerth5aaceb52020-04-07 06:31:56 -07001069 new FilesystemValueChecker(
1070 /* tsgm= */ null, /* lastExecutionTimeRange= */ null, FSVC_THREADS_FOR_TEST)
buchgr4992ae22019-03-20 04:23:32 -07001071 .getDirtyActionValues(
1072 evaluator.getValues(),
1073 /* batchStatter= */ null,
steinman39c00d22020-03-20 15:23:10 -07001074 ModifiedFileSet.EVERYTHING_MODIFIED,
1075 /* trustRemoteArtifacts= */ false))
buchgr4992ae22019-03-20 04:23:32 -07001076 .containsExactly(actionKey);
1077 }
1078
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001079 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001080 public void testPropagatesRuntimeExceptions() throws Exception {
tomluee6a6862018-01-17 14:36:26 -08001081 Collection<SkyKey> values =
1082 ImmutableList.of(
1083 FileValue.key(
1084 RootedPath.toRootedPath(Root.fromPath(pkgRoot), PathFragment.create("foo"))));
Googler10028672018-10-25 12:14:34 -07001085 driver.evaluate(values, EVALUATION_OPTIONS);
janakrc9f44ec2021-07-01 08:13:09 -07001086 AtomicReference<Throwable> uncaughtRef = new AtomicReference<>();
janakr933c3012021-07-13 07:44:15 -07001087 CountDownLatch throwableCaught = new CountDownLatch(1);
janakrc9f44ec2021-07-01 08:13:09 -07001088 Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
janakr933c3012021-07-13 07:44:15 -07001089 (t, e) -> {
1090 uncaughtRef.compareAndSet(null, e);
1091 throwableCaught.countDown();
1092 };
janakrc9f44ec2021-07-01 08:13:09 -07001093 Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
twerth5aaceb52020-04-07 06:31:56 -07001094 FilesystemValueChecker checker =
1095 new FilesystemValueChecker(
janakrc9f44ec2021-07-01 08:13:09 -07001096 /*tsgm=*/ null, /*lastExecutionTimeRange=*/ null, FSVC_THREADS_FOR_TEST);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001097
Nathan Harmata8cd29782015-11-10 03:24:01 +00001098 assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001099
1100 fs.statThrowsRuntimeException = true;
janakrc9f44ec2021-07-01 08:13:09 -07001101 getDirtyFilesystemKeys(evaluator, checker);
janakr933c3012021-07-13 07:44:15 -07001102 // Wait for exception handler to trigger (FVC doesn't clean up crashing threads on its own).
1103 assertThat(throwableCaught.await(TestUtils.WAIT_TIMEOUT_SECONDS, SECONDS)).isTrue();
1104 Throwable thrown = uncaughtRef.get();
1105 assertThat(thrown).isNotNull();
1106 assertThat(thrown).hasMessageThat().isEqualTo("bork");
1107 assertThat(thrown).isInstanceOf(RuntimeException.class);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001108 }
1109
1110 private static void assertEmptyDiff(Diff diff) {
1111 assertDiffWithNewValues(diff);
1112 }
1113
1114 private static void assertDiffWithNewValues(Diff diff, SkyKey... keysWithNewValues) {
1115 assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
1116 assertThat(diff.changedKeysWithNewValues().keySet())
1117 .containsExactlyElementsIn(Arrays.asList(keysWithNewValues));
1118 }
1119
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001120 private static FileStatusWithDigest statWithDigest(final Path path, final FileStatus stat) {
1121 return new FileStatusWithDigest() {
1122 @Nullable
1123 @Override
1124 public byte[] getDigest() throws IOException {
olaolabfd1d332017-06-19 16:55:24 -04001125 return path.getDigest();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001126 }
1127
1128 @Override
1129 public boolean isFile() {
1130 return stat.isFile();
1131 }
1132
1133 @Override
1134 public boolean isSpecialFile() {
1135 return stat.isSpecialFile();
1136 }
1137
1138 @Override
1139 public boolean isDirectory() {
1140 return stat.isDirectory();
1141 }
1142
1143 @Override
1144 public boolean isSymbolicLink() {
1145 return stat.isSymbolicLink();
1146 }
1147
1148 @Override
1149 public long getSize() throws IOException {
1150 return stat.getSize();
1151 }
1152
1153 @Override
1154 public long getLastModifiedTime() throws IOException {
1155 return stat.getLastModifiedTime();
1156 }
1157
1158 @Override
1159 public long getLastChangeTime() throws IOException {
1160 return stat.getLastChangeTime();
1161 }
1162
1163 @Override
1164 public long getNodeId() throws IOException {
1165 return stat.getNodeId();
1166 }
1167 };
1168 }
1169
Nathan Harmata8cd29782015-11-10 03:24:01 +00001170 private static Diff getDirtyFilesystemKeys(MemoizingEvaluator evaluator,
1171 FilesystemValueChecker checker) throws InterruptedException {
1172 return checker.getDirtyKeys(evaluator.getValues(), new BasicFilesystemDirtinessChecker());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001173 }
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +00001174}