blob: 6e62d1afdb019bd910c56639ce5754a1b50a0856 [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;
17import static com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode.CROSS;
18import static com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode.DONT_CROSS;
19import static com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode.REPORT_ERROR;
Laszlo Csomore9c41c52015-12-07 10:32:26 +000020import static com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactoryForTesting.danglingSymlinkForTesting;
21import static com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactoryForTesting.regularFileForTesting;
22import static com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactoryForTesting.symlinkToDirectoryForTesting;
23import static com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactoryForTesting.symlinkToFileForTesting;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000024
tomlua155b532017-11-08 20:12:47 +010025import com.google.common.base.Preconditions;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000026import com.google.common.collect.ImmutableList;
27import com.google.common.collect.ImmutableSet;
28import com.google.common.collect.Sets;
29import com.google.devtools.build.lib.actions.Artifact;
tomlu1cdcdf92018-01-16 11:07:51 -080030import com.google.devtools.build.lib.actions.ArtifactRoot;
felly5be4dd62018-02-05 11:11:53 -080031import com.google.devtools.build.lib.actions.FilesetTraversalParams.DirectTraversalRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000032import com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000033import com.google.devtools.build.lib.analysis.BlazeDirectories;
Ulf Adams015aad92016-07-13 16:49:40 +000034import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
janakr3b63a4e2017-09-14 09:55:40 +020035import com.google.devtools.build.lib.analysis.ServerDirectories;
Ulf Adams015aad92016-07-13 16:49:40 +000036import com.google.devtools.build.lib.analysis.util.AnalysisMock;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000037import com.google.devtools.build.lib.cmdline.PackageIdentifier;
38import com.google.devtools.build.lib.events.NullEventHandler;
39import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
Nathan Harmatad4f75942016-10-18 08:55:17 +000040import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
John Cater5e9ce942016-10-12 17:23:30 +000041import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
kush95bf7c82017-08-30 00:27:35 +020042import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalFunction.FileOperationException;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000043import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFile;
44import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.TraversalRequest;
Florian Weikertcca703a2015-12-07 09:56:38 +000045import com.google.devtools.build.lib.testutil.FoundationTestCase;
laszlocsomor29bdf632018-03-13 02:50:46 -070046import com.google.devtools.build.lib.testutil.TimestampGranularityUtils;
Laszlo Csomora31e0352018-03-09 04:51:38 -080047import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000048import com.google.devtools.build.lib.vfs.Path;
49import com.google.devtools.build.lib.vfs.PathFragment;
tomluee6a6862018-01-17 14:36:26 -080050import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000051import com.google.devtools.build.lib.vfs.RootedPath;
52import com.google.devtools.build.skyframe.ErrorInfo;
53import com.google.devtools.build.skyframe.EvaluationProgressReceiver;
54import com.google.devtools.build.skyframe.EvaluationResult;
55import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
56import com.google.devtools.build.skyframe.MemoizingEvaluator;
57import com.google.devtools.build.skyframe.RecordingDifferencer;
janakr1cde8722017-10-10 03:22:21 +020058import com.google.devtools.build.skyframe.SequencedRecordingDifferencer;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000059import com.google.devtools.build.skyframe.SequentialBuildDriver;
60import com.google.devtools.build.skyframe.SkyFunction;
felly5be4dd62018-02-05 11:11:53 -080061import com.google.devtools.build.skyframe.SkyFunctionException;
62import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000063import com.google.devtools.build.skyframe.SkyFunctionName;
64import com.google.devtools.build.skyframe.SkyKey;
65import com.google.devtools.build.skyframe.SkyValue;
felly5be4dd62018-02-05 11:11:53 -080066import java.io.IOException;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000067import java.io.OutputStream;
68import java.nio.charset.StandardCharsets;
69import java.util.HashMap;
70import java.util.HashSet;
71import java.util.Map;
72import java.util.Set;
73import java.util.UUID;
74import java.util.concurrent.atomic.AtomicReference;
janakr61377f72018-06-04 12:50:13 -070075import java.util.function.Supplier;
felly5be4dd62018-02-05 11:11:53 -080076import javax.annotation.Nullable;
John Cater5e9ce942016-10-12 17:23:30 +000077import org.junit.Before;
78import org.junit.Test;
79import org.junit.runner.RunWith;
80import org.junit.runners.JUnit4;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000081
82/** Tests for {@link RecursiveFilesystemTraversalFunction}. */
Florian Weikert92b22362015-12-03 10:17:18 +000083@RunWith(JUnit4.class)
Florian Weikertcca703a2015-12-07 09:56:38 +000084public final class RecursiveFilesystemTraversalFunctionTest extends FoundationTestCase {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000085 private RecordingEvaluationProgressReceiver progressReceiver;
86 private MemoizingEvaluator evaluator;
87 private SequentialBuildDriver driver;
88 private RecordingDifferencer differencer;
89 private AtomicReference<PathPackageLocator> pkgLocator;
felly5be4dd62018-02-05 11:11:53 -080090 private ArtifactFakeFunction artifactFakeFunction;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000091
Florian Weikert92b22362015-12-03 10:17:18 +000092 @Before
93 public final void setUp() throws Exception {
Ulf Adams015aad92016-07-13 16:49:40 +000094 AnalysisMock analysisMock = AnalysisMock.get();
felly5be4dd62018-02-05 11:11:53 -080095 artifactFakeFunction = new ArtifactFakeFunction();
John Catere0d1d0e2017-11-28 20:47:41 -080096 pkgLocator =
97 new AtomicReference<>(
98 new PathPackageLocator(
99 outputBase,
tomluee6a6862018-01-17 14:36:26 -0800100 ImmutableList.of(Root.fromPath(rootDirectory)),
John Catere0d1d0e2017-11-28 20:47:41 -0800101 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000102 AtomicReference<ImmutableSet<PackageIdentifier>> deletedPackages =
103 new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
Ulf Adams015aad92016-07-13 16:49:40 +0000104 BlazeDirectories directories =
105 new BlazeDirectories(
Klaus Aehligc2499c42018-02-27 05:47:21 -0800106 new ServerDirectories(rootDirectory, outputBase, rootDirectory),
janakr3b63a4e2017-09-14 09:55:40 +0200107 rootDirectory,
cushon849df362018-05-14 01:51:45 -0700108 null,
janakr3b63a4e2017-09-14 09:55:40 +0200109 analysisMock.getProductName());
nharmata3fb7d342018-02-23 11:37:51 -0800110 ExternalFilesHelper externalFilesHelper = ExternalFilesHelper.createForTesting(
Nathan Harmatad4f75942016-10-18 08:55:17 +0000111 pkgLocator, ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS, directories);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000112
Ulf Adams015aad92016-07-13 16:49:40 +0000113 ConfiguredRuleClassProvider ruleClassProvider = analysisMock.createRuleClassProvider();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000114 Map<SkyFunctionName, SkyFunction> skyFunctions = new HashMap<>();
Ulf Adamsc73051c62016-03-23 09:18:13 +0000115 skyFunctions.put(SkyFunctions.FILE_STATE, new FileStateFunction(
felly5be4dd62018-02-05 11:11:53 -0800116 new AtomicReference<>(), externalFilesHelper));
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000117 skyFunctions.put(SkyFunctions.FILE, new FileFunction(pkgLocator));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000118 skyFunctions.put(SkyFunctions.DIRECTORY_LISTING, new DirectoryListingFunction());
119 skyFunctions.put(
120 SkyFunctions.DIRECTORY_LISTING_STATE,
121 new DirectoryListingStateFunction(externalFilesHelper));
122 skyFunctions.put(
123 SkyFunctions.RECURSIVE_FILESYSTEM_TRAVERSAL, new RecursiveFilesystemTraversalFunction());
John Cater5e9ce942016-10-12 17:23:30 +0000124 skyFunctions.put(
125 SkyFunctions.PACKAGE_LOOKUP,
John Cater0c0735a2016-11-11 01:52:02 +0000126 new PackageLookupFunction(
127 deletedPackages,
128 CrossRepositoryLabelViolationStrategy.ERROR,
John Catere0d1d0e2017-11-28 20:47:41 -0800129 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
Eric Fellheimer7ef96d72015-11-12 02:28:44 +0000130 skyFunctions.put(SkyFunctions.BLACKLISTED_PACKAGE_PREFIXES,
nharmatae4eb23f2017-12-05 09:27:45 -0800131 new BlacklistedPackagePrefixesFunction(
132 /*hardcodedBlacklistedPackagePrefixes=*/ ImmutableSet.of(),
133 /*additionalBlacklistedPackagePrefixesFile=*/ PathFragment.EMPTY_FRAGMENT));
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000134 skyFunctions.put(SkyFunctions.PACKAGE,
135 new PackageFunction(null, null, null, null, null, null, null));
Ulf Adams015aad92016-07-13 16:49:40 +0000136 skyFunctions.put(SkyFunctions.WORKSPACE_AST, new WorkspaceASTFunction(ruleClassProvider));
137 skyFunctions.put(
138 SkyFunctions.WORKSPACE_FILE,
139 new WorkspaceFileFunction(
140 ruleClassProvider,
141 analysisMock
janakr52d05e82017-09-22 13:27:14 -0400142 .getPackageFactoryBuilderForTesting(directories)
nharmatae57e9a32018-04-02 15:10:24 -0700143 .build(ruleClassProvider),
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000144 directories));
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000145 skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
John Caterb4f461e2016-10-25 16:16:35 +0000146 skyFunctions.put(SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction());
kush95bf7c82017-08-30 00:27:35 +0200147 skyFunctions.put(
148 SkyFunctions.FILE_SYMLINK_INFINITE_EXPANSION_UNIQUENESS,
149 new FileSymlinkInfiniteExpansionUniquenessFunction());
150 skyFunctions.put(
151 SkyFunctions.FILE_SYMLINK_CYCLE_UNIQUENESS, new FileSymlinkCycleUniquenessFunction());
felly5be4dd62018-02-05 11:11:53 -0800152 skyFunctions.put(
153 SkyFunctions.ARTIFACT,
154 artifactFakeFunction);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000155
156 progressReceiver = new RecordingEvaluationProgressReceiver();
janakr1cde8722017-10-10 03:22:21 +0200157 differencer = new SequencedRecordingDifferencer();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000158 evaluator = new InMemoryMemoizingEvaluator(skyFunctions, differencer, progressReceiver);
159 driver = new SequentialBuildDriver(evaluator);
160 PrecomputedValue.BUILD_ID.set(differencer, UUID.randomUUID());
161 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
162 }
163
164 private Artifact sourceArtifact(String path) {
tomluee6a6862018-01-17 14:36:26 -0800165 return new Artifact(
166 PathFragment.create(path), ArtifactRoot.asSourceRoot(Root.fromPath(rootDirectory)));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000167 }
168
169 private Artifact sourceArtifactUnderPackagePath(String path, String packagePath) {
170 return new Artifact(
tomlu1cdcdf92018-01-16 11:07:51 -0800171 PathFragment.create(path),
tomluee6a6862018-01-17 14:36:26 -0800172 ArtifactRoot.asSourceRoot(Root.fromPath(rootDirectory.getRelative(packagePath))));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000173 }
174
175 private Artifact derivedArtifact(String path) {
nharmatab4060b62017-04-04 17:11:39 +0000176 PathFragment execPath = PathFragment.create("out").getRelative(path);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000177 Artifact output =
178 new Artifact(
tomlu1cdcdf92018-01-16 11:07:51 -0800179 ArtifactRoot.asDerivedRoot(rootDirectory, rootDirectory.getRelative("out")),
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000180 execPath);
181 return output;
182 }
183
184 private static RootedPath rootedPath(Artifact artifact) {
tomluee6a6862018-01-17 14:36:26 -0800185 return RootedPath.toRootedPath(artifact.getRoot().getRoot(), artifact.getRootRelativePath());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000186 }
187
188 private RootedPath rootedPath(String path, String packagePath) {
nharmatab4060b62017-04-04 17:11:39 +0000189 return RootedPath.toRootedPath(
tomluee6a6862018-01-17 14:36:26 -0800190 Root.fromPath(rootDirectory.getRelative(packagePath)), PathFragment.create(path));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000191 }
192
193 private static RootedPath childOf(Artifact artifact, String relative) {
194 return RootedPath.toRootedPath(
tomluee6a6862018-01-17 14:36:26 -0800195 artifact.getRoot().getRoot(), artifact.getRootRelativePath().getRelative(relative));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000196 }
197
198 private static RootedPath childOf(RootedPath path, String relative) {
tomlu8cc5dcf2018-01-19 09:28:06 -0800199 return RootedPath.toRootedPath(
200 path.getRoot(), path.getRootRelativePath().getRelative(relative));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000201 }
202
203 private static RootedPath parentOf(RootedPath path) {
tomlu8cc5dcf2018-01-19 09:28:06 -0800204 PathFragment parent =
205 Preconditions.checkNotNull(path.getRootRelativePath().getParentDirectory());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000206 return RootedPath.toRootedPath(path.getRoot(), parent);
207 }
208
209 private static RootedPath siblingOf(RootedPath path, String relative) {
tomlu8cc5dcf2018-01-19 09:28:06 -0800210 PathFragment parent =
211 Preconditions.checkNotNull(path.getRootRelativePath().getParentDirectory());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000212 return RootedPath.toRootedPath(path.getRoot(), parent.getRelative(relative));
213 }
214
215 private static RootedPath siblingOf(Artifact artifact, String relative) {
216 PathFragment parent =
217 Preconditions.checkNotNull(artifact.getRootRelativePath().getParentDirectory());
tomluee6a6862018-01-17 14:36:26 -0800218 return RootedPath.toRootedPath(artifact.getRoot().getRoot(), parent.getRelative(relative));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000219 }
220
221 private void createFile(Path path, String... contents) throws Exception {
222 if (!path.getParentDirectory().exists()) {
223 scratch.dir(path.getParentDirectory().getPathString());
224 }
225 scratch.file(path.getPathString(), contents);
226 }
227
228 private void createFile(Artifact artifact, String... contents) throws Exception {
229 createFile(artifact.getPath(), contents);
230 }
231
232 private RootedPath createFile(RootedPath path, String... contents) throws Exception {
233 scratch.dir(parentOf(path).asPath().getPathString());
234 createFile(path.asPath(), contents);
235 return path;
236 }
237
238 private static TraversalRequest fileLikeRoot(Artifact file, PackageBoundaryMode pkgBoundaryMode) {
janakr5fb2a482018-03-02 17:48:57 -0800239 return TraversalRequest.create(
240 DirectTraversalRoot.forFileOrDirectory(file),
241 !file.isSourceArtifact(),
242 pkgBoundaryMode,
243 false,
244 null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000245 }
246
247 private static TraversalRequest pkgRoot(
248 RootedPath pkgDirectory, PackageBoundaryMode pkgBoundaryMode) {
janakr5fb2a482018-03-02 17:48:57 -0800249 return TraversalRequest.create(
250 DirectTraversalRoot.forRootedPath(pkgDirectory), false, pkgBoundaryMode, true, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000251 }
252
253 private <T extends SkyValue> EvaluationResult<T> eval(SkyKey key) throws Exception {
254 return driver.evaluate(
255 ImmutableList.of(key),
256 false,
257 SkyframeExecutor.DEFAULT_THREAD_COUNT,
258 NullEventHandler.INSTANCE);
259 }
260
261 private RecursiveFilesystemTraversalValue evalTraversalRequest(TraversalRequest params)
262 throws Exception {
janakr5fb2a482018-03-02 17:48:57 -0800263 EvaluationResult<RecursiveFilesystemTraversalValue> result = eval(params);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000264 assertThat(result.hasError()).isFalse();
janakr5fb2a482018-03-02 17:48:57 -0800265 return result.get(params);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000266 }
267
268 /**
269 * Asserts that the requested SkyValue can be built and results in the expected set of files.
270 *
271 * <p>The metadata of files is ignored in comparing the actual results with the expected ones.
272 * The returned object however contains the actual metadata.
273 */
274 @SafeVarargs
275 private final RecursiveFilesystemTraversalValue traverseAndAssertFiles(
276 TraversalRequest params, ResolvedFile... expectedFilesIgnoringMetadata) throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000277 RecursiveFilesystemTraversalValue result = evalTraversalRequest(params);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000278 Set<ResolvedFile> actual = new HashSet<>();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000279 for (ResolvedFile act : result.getTransitiveFiles()) {
280 // Strip metadata so only the type and path of the objects are compared.
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000281 actual.add(act.stripMetadataForTesting());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000282 }
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000283 assertThat(actual).containsExactly((Object[]) expectedFilesIgnoringMetadata);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000284
285 // The returned object still has the unstripped metadata.
286 return result;
287 }
288
felly5be4dd62018-02-05 11:11:53 -0800289 private void appendToFile(RootedPath rootedPath, SkyKey toInvalidate, String content)
290 throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000291 Path path = rootedPath.asPath();
292 if (path.exists()) {
293 try (OutputStream os = path.getOutputStream(/*append=*/ true)) {
294 os.write(content.getBytes(StandardCharsets.UTF_8));
295 }
felly5be4dd62018-02-05 11:11:53 -0800296 differencer.invalidate(ImmutableList.of(toInvalidate));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000297 } else {
298 createFile(path, content);
299 }
300 }
301
felly5be4dd62018-02-05 11:11:53 -0800302 private void appendToFile(RootedPath rootedPath, String content) throws Exception {
303 appendToFile(rootedPath, FileStateValue.key(rootedPath), content);
304 }
305
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000306 private void appendToFile(Artifact file, String content) throws Exception {
felly5be4dd62018-02-05 11:11:53 -0800307 SkyKey key = file.isSourceArtifact()
308 ? FileStateValue.key(rootedPath(file))
309 : ArtifactSkyKey.key(file, true);
310 appendToFile(rootedPath(file), key, content);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000311 }
312
313 private void invalidateDirectory(RootedPath path) {
314 differencer.invalidate(ImmutableList.of(DirectoryListingStateValue.key(path)));
315 }
316
felly5be4dd62018-02-05 11:11:53 -0800317 private void invalidateOutputArtifact(Artifact output) {
318 assertThat(output.isSourceArtifact()).isFalse();
319 differencer.invalidate(ImmutableList.of(ArtifactSkyKey.key(output, true)));
320 }
321
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000322 private void invalidateDirectory(Artifact directoryArtifact) {
323 invalidateDirectory(rootedPath(directoryArtifact));
324 }
325
326 private static final class RecordingEvaluationProgressReceiver
nharmata8040b0b2017-05-09 13:53:19 -0400327 extends EvaluationProgressReceiver.NullEvaluationProgressReceiver {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000328 Set<SkyKey> invalidations;
janakr61377f72018-06-04 12:50:13 -0700329 Set<SkyKey> evaluations;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000330
331 RecordingEvaluationProgressReceiver() {
332 clear();
333 }
334
335 void clear() {
336 invalidations = Sets.newConcurrentHashSet();
337 evaluations = Sets.newConcurrentHashSet();
338 }
339
340 @Override
341 public void invalidated(SkyKey skyKey, InvalidationState state) {
342 invalidations.add(skyKey);
343 }
344
345 @Override
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000346 public void evaluated(
janakr61377f72018-06-04 12:50:13 -0700347 SkyKey skyKey,
348 Supplier<EvaluationSuccessState> evaluationSuccessState,
349 EvaluationState state) {
350 if (evaluationSuccessState.get().succeeded()) {
351 evaluations.add(skyKey);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000352 }
353 }
354 }
355
Laszlo Csomor207140f2015-12-07 15:07:33 +0000356 private void assertTraversalRootHashesAre(
357 boolean equal, RecursiveFilesystemTraversalValue a, RecursiveFilesystemTraversalValue b)
358 throws Exception {
359 if (equal) {
360 assertThat(a.getResolvedRoot().get().hashCode())
361 .isEqualTo(b.getResolvedRoot().get().hashCode());
362 } else {
363 assertThat(a.getResolvedRoot().get().hashCode())
364 .isNotEqualTo(b.getResolvedRoot().get().hashCode());
365 }
366 }
367
368 private void assertTraversalRootHashesAreEqual(
369 RecursiveFilesystemTraversalValue a, RecursiveFilesystemTraversalValue b) throws Exception {
370 assertTraversalRootHashesAre(true, a, b);
371 }
372
373 private void assertTraversalRootHashesAreNotEqual(
374 RecursiveFilesystemTraversalValue a, RecursiveFilesystemTraversalValue b) throws Exception {
375 assertTraversalRootHashesAre(false, a, b);
376 }
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000377
378 private void assertTraversalOfFile(Artifact rootArtifact) throws Exception {
379 TraversalRequest traversalRoot = fileLikeRoot(rootArtifact, DONT_CROSS);
380 RootedPath rootedPath = createFile(rootedPath(rootArtifact), "foo");
381
382 // Assert that the SkyValue is built and looks right.
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000383 ResolvedFile expected = regularFileForTesting(rootedPath);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000384 RecursiveFilesystemTraversalValue v1 = traverseAndAssertFiles(traversalRoot, expected);
385 assertThat(progressReceiver.invalidations).isEmpty();
janakr61377f72018-06-04 12:50:13 -0700386 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000387 progressReceiver.clear();
388
389 // Edit the file and verify that the value is rebuilt.
390 appendToFile(rootArtifact, "bar");
391 RecursiveFilesystemTraversalValue v2 = traverseAndAssertFiles(traversalRoot, expected);
janakr5fb2a482018-03-02 17:48:57 -0800392 assertThat(progressReceiver.invalidations).contains(traversalRoot);
janakr61377f72018-06-04 12:50:13 -0700393 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000394 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000395 assertTraversalRootHashesAreNotEqual(v1, v2);
396
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000397 progressReceiver.clear();
398 }
399
Florian Weikert92b22362015-12-03 10:17:18 +0000400 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000401 public void testTraversalOfSourceFile() throws Exception {
402 assertTraversalOfFile(sourceArtifact("foo/bar.txt"));
403 }
404
Florian Weikert92b22362015-12-03 10:17:18 +0000405 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000406 public void testTraversalOfGeneratedFile() throws Exception {
407 assertTraversalOfFile(derivedArtifact("foo/bar.txt"));
408 }
409
Florian Weikert92b22362015-12-03 10:17:18 +0000410 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000411 public void testTraversalOfSymlinkToFile() throws Exception {
412 Artifact linkNameArtifact = sourceArtifact("foo/baz/qux.sym");
413 Artifact linkTargetArtifact = sourceArtifact("foo/bar/baz.txt");
nharmatab4060b62017-04-04 17:11:39 +0000414 PathFragment linkValue = PathFragment.create("../bar/baz.txt");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000415 TraversalRequest traversalRoot = fileLikeRoot(linkNameArtifact, DONT_CROSS);
416 createFile(linkTargetArtifact);
417 scratch.dir(linkNameArtifact.getExecPath().getParentDirectory().getPathString());
418 rootDirectory.getRelative(linkNameArtifact.getExecPath()).createSymbolicLink(linkValue);
419
420 // Assert that the SkyValue is built and looks right.
421 RootedPath symlinkNamePath = rootedPath(linkNameArtifact);
422 RootedPath symlinkTargetPath = rootedPath(linkTargetArtifact);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000423 ResolvedFile expected = symlinkToFileForTesting(symlinkTargetPath, symlinkNamePath, linkValue);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000424 RecursiveFilesystemTraversalValue v1 = traverseAndAssertFiles(traversalRoot, expected);
425 assertThat(progressReceiver.invalidations).isEmpty();
janakr61377f72018-06-04 12:50:13 -0700426 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000427 progressReceiver.clear();
428
429 // Edit the target of the symlink and verify that the value is rebuilt.
430 appendToFile(linkTargetArtifact, "bar");
431 RecursiveFilesystemTraversalValue v2 = traverseAndAssertFiles(traversalRoot, expected);
janakr5fb2a482018-03-02 17:48:57 -0800432 assertThat(progressReceiver.invalidations).contains(traversalRoot);
janakr61377f72018-06-04 12:50:13 -0700433 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000434 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000435 assertTraversalRootHashesAreNotEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000436 }
437
Florian Weikert92b22362015-12-03 10:17:18 +0000438 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000439 public void testTraversalOfTransitiveSymlinkToFile() throws Exception {
440 Artifact directLinkArtifact = sourceArtifact("direct/file.sym");
441 Artifact transitiveLinkArtifact = sourceArtifact("transitive/sym.sym");
442 RootedPath fileA = createFile(rootedPath(sourceArtifact("a/file.a")));
443 RootedPath directLink = rootedPath(directLinkArtifact);
444 RootedPath transitiveLink = rootedPath(transitiveLinkArtifact);
nharmatab4060b62017-04-04 17:11:39 +0000445 PathFragment directLinkPath = PathFragment.create("../a/file.a");
446 PathFragment transitiveLinkPath = PathFragment.create("../direct/file.sym");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000447
448 parentOf(directLink).asPath().createDirectory();
449 parentOf(transitiveLink).asPath().createDirectory();
450 directLink.asPath().createSymbolicLink(directLinkPath);
451 transitiveLink.asPath().createSymbolicLink(transitiveLinkPath);
452
453 traverseAndAssertFiles(
454 fileLikeRoot(directLinkArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000455 symlinkToFileForTesting(fileA, directLink, directLinkPath));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000456
457 traverseAndAssertFiles(
458 fileLikeRoot(transitiveLinkArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000459 symlinkToFileForTesting(fileA, transitiveLink, transitiveLinkPath));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000460 }
461
462 private void assertTraversalOfDirectory(Artifact directoryArtifact) throws Exception {
463 // Create files under the directory.
464 // Use the root + root-relative path of the rootArtifact to create these files, rather than
465 // using the rootDirectory + execpath of the rootArtifact. The resulting paths are the same
466 // but the RootedPaths are different:
467 // in the 1st case, it is: RootedPath(/root/execroot, relative), in the second it is
468 // in the 2nd case, it is: RootedPath(/root, execroot/relative).
469 // Creating the files will also create the parent directories.
470 RootedPath file1 = createFile(childOf(directoryArtifact, "bar.txt"));
471 RootedPath file2 = createFile(childOf(directoryArtifact, "baz/qux.txt"));
472
473 TraversalRequest traversalRoot = fileLikeRoot(directoryArtifact, DONT_CROSS);
474
475 // Assert that the SkyValue is built and looks right.
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000476 ResolvedFile expected1 = regularFileForTesting(file1);
477 ResolvedFile expected2 = regularFileForTesting(file2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000478 RecursiveFilesystemTraversalValue v1 =
479 traverseAndAssertFiles(traversalRoot, expected1, expected2);
480 assertThat(progressReceiver.invalidations).isEmpty();
janakr61377f72018-06-04 12:50:13 -0700481 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000482 progressReceiver.clear();
483
484 // Add a new file to the directory and see that the value is rebuilt.
laszlocsomor29bdf632018-03-13 02:50:46 -0700485 TimestampGranularityUtils.waitForTimestampGranularity(
486 directoryArtifact.getPath().stat().getLastChangeTime(), OutErr.SYSTEM_OUT_ERR);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000487 RootedPath file3 = createFile(childOf(directoryArtifact, "foo.txt"));
felly5be4dd62018-02-05 11:11:53 -0800488 if (directoryArtifact.isSourceArtifact()) {
489 invalidateDirectory(directoryArtifact);
490 } else {
491 invalidateOutputArtifact(directoryArtifact);
492 }
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000493 ResolvedFile expected3 = regularFileForTesting(file3);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000494 RecursiveFilesystemTraversalValue v2 =
495 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3);
janakr5fb2a482018-03-02 17:48:57 -0800496 assertThat(progressReceiver.invalidations).contains(traversalRoot);
janakr61377f72018-06-04 12:50:13 -0700497 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000498 // Directories always have the same hash code, but that is fine because their contents are also
499 // part of the RecursiveFilesystemTraversalValue, so v1 and v2 are unequal.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000500 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000501 assertTraversalRootHashesAreEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000502 progressReceiver.clear();
503
504 // Edit a file in the directory and see that the value is rebuilt.
felly5be4dd62018-02-05 11:11:53 -0800505 RecursiveFilesystemTraversalValue v3;
506 if (directoryArtifact.isSourceArtifact()) {
507 SkyKey toInvalidate = FileStateValue.key(file1);
508 appendToFile(file1, toInvalidate, "bar");
509 v3 = traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3);
janakr5fb2a482018-03-02 17:48:57 -0800510 assertThat(progressReceiver.invalidations).contains(traversalRoot);
janakr61377f72018-06-04 12:50:13 -0700511 assertThat(progressReceiver.evaluations).contains(traversalRoot);
felly5be4dd62018-02-05 11:11:53 -0800512 assertThat(v3).isNotEqualTo(v2);
513 // Directories always have the same hash code, but that is fine because their contents are
514 // also part of the RecursiveFilesystemTraversalValue, so v2 and v3 are unequal.
515 assertTraversalRootHashesAreEqual(v2, v3);
516 progressReceiver.clear();
517 } else {
518 // Dependency checking of output directories is unsound. Specifically, the directory mtime
519 // is not changed when a contained file is modified.
520 v3 = v2;
521 }
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000522
523 // Add a new file *outside* of the directory and see that the value is *not* rebuilt.
524 Artifact someFile = sourceArtifact("somewhere/else/a.file");
525 createFile(someFile, "new file");
526 appendToFile(someFile, "not all changes are treated equal");
527 RecursiveFilesystemTraversalValue v4 =
528 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3);
529 assertThat(v4).isEqualTo(v3);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000530 assertTraversalRootHashesAreEqual(v3, v4);
janakr5fb2a482018-03-02 17:48:57 -0800531 assertThat(progressReceiver.invalidations).doesNotContain(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000532 }
533
Florian Weikert92b22362015-12-03 10:17:18 +0000534 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000535 public void testTraversalOfSourceDirectory() throws Exception {
536 assertTraversalOfDirectory(sourceArtifact("dir"));
537 }
538
Florian Weikert92b22362015-12-03 10:17:18 +0000539 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000540 public void testTraversalOfGeneratedDirectory() throws Exception {
541 assertTraversalOfDirectory(derivedArtifact("dir"));
542 }
543
Florian Weikert92b22362015-12-03 10:17:18 +0000544 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000545 public void testTraversalOfTransitiveSymlinkToDirectory() throws Exception {
546 Artifact directLinkArtifact = sourceArtifact("direct/dir.sym");
547 Artifact transitiveLinkArtifact = sourceArtifact("transitive/sym.sym");
548 RootedPath fileA = createFile(rootedPath(sourceArtifact("a/file.a")));
549 RootedPath directLink = rootedPath(directLinkArtifact);
550 RootedPath transitiveLink = rootedPath(transitiveLinkArtifact);
nharmatab4060b62017-04-04 17:11:39 +0000551 PathFragment directLinkPath = PathFragment.create("../a");
552 PathFragment transitiveLinkPath = PathFragment.create("../direct/dir.sym");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000553
554 parentOf(directLink).asPath().createDirectory();
555 parentOf(transitiveLink).asPath().createDirectory();
556 directLink.asPath().createSymbolicLink(directLinkPath);
557 transitiveLink.asPath().createSymbolicLink(transitiveLinkPath);
558
559 // Expect the file as if was a child of the direct symlink, not of the actual directory.
560 traverseAndAssertFiles(
561 fileLikeRoot(directLinkArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000562 symlinkToDirectoryForTesting(parentOf(fileA), directLink, directLinkPath),
563 regularFileForTesting(childOf(directLinkArtifact, "file.a")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000564
565 // Expect the file as if was a child of the transitive symlink, not of the actual directory.
566 traverseAndAssertFiles(
567 fileLikeRoot(transitiveLinkArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000568 symlinkToDirectoryForTesting(parentOf(fileA), transitiveLink, transitiveLinkPath),
569 regularFileForTesting(childOf(transitiveLinkArtifact, "file.a")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000570 }
571
Florian Weikert92b22362015-12-03 10:17:18 +0000572 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000573 public void testTraversePackage() throws Exception {
574 Artifact buildFile = sourceArtifact("pkg/BUILD");
575 RootedPath buildFilePath = createFile(rootedPath(buildFile));
576 RootedPath file1 = createFile(siblingOf(buildFile, "subdir/file.a"));
577
578 traverseAndAssertFiles(
579 pkgRoot(parentOf(buildFilePath), DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000580 regularFileForTesting(buildFilePath),
581 regularFileForTesting(file1));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000582 }
583
Florian Weikert92b22362015-12-03 10:17:18 +0000584 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000585 public void testTraversalOfSymlinkToDirectory() throws Exception {
586 Artifact linkNameArtifact = sourceArtifact("link/foo.sym");
587 Artifact linkTargetArtifact = sourceArtifact("dir");
588 RootedPath linkName = rootedPath(linkNameArtifact);
nharmatab4060b62017-04-04 17:11:39 +0000589 PathFragment linkValue = PathFragment.create("../dir");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000590 RootedPath file1 = createFile(childOf(linkTargetArtifact, "file.1"));
591 createFile(childOf(linkTargetArtifact, "sub/file.2"));
592 scratch.dir(parentOf(linkName).asPath().getPathString());
593 linkName.asPath().createSymbolicLink(linkValue);
594
595 // Assert that the SkyValue is built and looks right.
596 TraversalRequest traversalRoot = fileLikeRoot(linkNameArtifact, DONT_CROSS);
597 ResolvedFile expected1 =
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000598 symlinkToDirectoryForTesting(rootedPath(linkTargetArtifact), linkName, linkValue);
599 ResolvedFile expected2 = regularFileForTesting(childOf(linkNameArtifact, "file.1"));
600 ResolvedFile expected3 = regularFileForTesting(childOf(linkNameArtifact, "sub/file.2"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000601 // We expect to see all the files from the symlink'd directory, under the symlink's path, not
602 // under the symlink target's path.
603 RecursiveFilesystemTraversalValue v1 =
604 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3);
605 assertThat(progressReceiver.invalidations).isEmpty();
janakr61377f72018-06-04 12:50:13 -0700606 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000607 progressReceiver.clear();
608
609 // Add a new file to the directory and see that the value is rebuilt.
610 createFile(childOf(linkTargetArtifact, "file.3"));
611 invalidateDirectory(linkTargetArtifact);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000612 ResolvedFile expected4 = regularFileForTesting(childOf(linkNameArtifact, "file.3"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000613 RecursiveFilesystemTraversalValue v2 =
614 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3, expected4);
janakr5fb2a482018-03-02 17:48:57 -0800615 assertThat(progressReceiver.invalidations).contains(traversalRoot);
janakr61377f72018-06-04 12:50:13 -0700616 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000617 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000618 assertTraversalRootHashesAreNotEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000619 progressReceiver.clear();
620
621 // Edit a file in the directory and see that the value is rebuilt.
622 appendToFile(file1, "bar");
623 RecursiveFilesystemTraversalValue v3 =
624 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3, expected4);
janakr5fb2a482018-03-02 17:48:57 -0800625 assertThat(progressReceiver.invalidations).contains(traversalRoot);
janakr61377f72018-06-04 12:50:13 -0700626 assertThat(progressReceiver.evaluations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000627 assertThat(v3).isNotEqualTo(v2);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000628 assertTraversalRootHashesAreNotEqual(v2, v3);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000629 progressReceiver.clear();
630
631 // Add a new file *outside* of the directory and see that the value is *not* rebuilt.
632 Artifact someFile = sourceArtifact("somewhere/else/a.file");
633 createFile(someFile, "new file");
634 appendToFile(someFile, "not all changes are treated equal");
635 RecursiveFilesystemTraversalValue v4 =
636 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3, expected4);
637 assertThat(v4).isEqualTo(v3);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000638 assertTraversalRootHashesAreEqual(v3, v4);
janakr5fb2a482018-03-02 17:48:57 -0800639 assertThat(progressReceiver.invalidations).doesNotContain(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000640 }
641
Florian Weikert92b22362015-12-03 10:17:18 +0000642 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000643 public void testTraversalOfDanglingSymlink() throws Exception {
644 Artifact linkArtifact = sourceArtifact("a/dangling.sym");
645 RootedPath link = rootedPath(linkArtifact);
nharmatab4060b62017-04-04 17:11:39 +0000646 PathFragment linkTarget = PathFragment.create("non_existent");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000647 parentOf(link).asPath().createDirectory();
648 link.asPath().createSymbolicLink(linkTarget);
649 traverseAndAssertFiles(
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000650 fileLikeRoot(linkArtifact, DONT_CROSS), danglingSymlinkForTesting(link, linkTarget));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000651 }
652
Florian Weikert92b22362015-12-03 10:17:18 +0000653 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000654 public void testTraversalOfDanglingSymlinkInADirectory() throws Exception {
655 Artifact dirArtifact = sourceArtifact("a");
656 RootedPath file = createFile(childOf(dirArtifact, "file.txt"));
657 RootedPath link = rootedPath(sourceArtifact("a/dangling.sym"));
nharmatab4060b62017-04-04 17:11:39 +0000658 PathFragment linkTarget = PathFragment.create("non_existent");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000659 parentOf(link).asPath().createDirectory();
660 link.asPath().createSymbolicLink(linkTarget);
661 traverseAndAssertFiles(
662 fileLikeRoot(dirArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000663 regularFileForTesting(file),
664 danglingSymlinkForTesting(link, linkTarget));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000665 }
666
667 private void assertTraverseSubpackages(PackageBoundaryMode traverseSubpackages) throws Exception {
668 Artifact pkgDirArtifact = sourceArtifact("pkg1/foo");
669 Artifact subpkgDirArtifact = sourceArtifact("pkg1/foo/subdir/subpkg");
670 RootedPath pkgBuildFile = childOf(pkgDirArtifact, "BUILD");
671 RootedPath subpkgBuildFile = childOf(subpkgDirArtifact, "BUILD");
672 scratch.dir(rootedPath(pkgDirArtifact).asPath().getPathString());
673 scratch.dir(rootedPath(subpkgDirArtifact).asPath().getPathString());
674 createFile(pkgBuildFile);
675 createFile(subpkgBuildFile);
676
677 TraversalRequest traversalRoot = pkgRoot(parentOf(pkgBuildFile), traverseSubpackages);
678
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000679 ResolvedFile expected1 = regularFileForTesting(pkgBuildFile);
680 ResolvedFile expected2 = regularFileForTesting(subpkgBuildFile);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000681 switch (traverseSubpackages) {
682 case CROSS:
683 traverseAndAssertFiles(traversalRoot, expected1, expected2);
684 break;
685 case DONT_CROSS:
686 traverseAndAssertFiles(traversalRoot, expected1);
687 break;
688 case REPORT_ERROR:
janakr5fb2a482018-03-02 17:48:57 -0800689 SkyKey key = traversalRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000690 EvaluationResult<SkyValue> result = eval(key);
691 assertThat(result.hasError()).isTrue();
lberkiaea56b32017-05-30 12:35:33 +0200692 assertThat(result.getError().getException())
693 .hasMessageThat()
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000694 .contains("crosses package boundary into package rooted at");
695 break;
696 default:
697 throw new IllegalStateException(traverseSubpackages.toString());
698 }
699 }
700
Florian Weikert92b22362015-12-03 10:17:18 +0000701 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000702 public void testTraverseSubpackages() throws Exception {
703 assertTraverseSubpackages(CROSS);
704 }
705
Florian Weikert92b22362015-12-03 10:17:18 +0000706 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000707 public void testDoNotTraverseSubpackages() throws Exception {
708 assertTraverseSubpackages(DONT_CROSS);
709 }
710
Florian Weikert92b22362015-12-03 10:17:18 +0000711 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000712 public void testReportErrorWhenTraversingSubpackages() throws Exception {
713 assertTraverseSubpackages(REPORT_ERROR);
714 }
715
Florian Weikert92b22362015-12-03 10:17:18 +0000716 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000717 public void testSwitchPackageRootsWhenUsingMultiplePackagePaths() throws Exception {
718 // Layout:
719 // pp1://a/BUILD
720 // pp1://a/file.a
721 // pp1://a/b.sym -> b/ (only created later)
722 // pp1://a/b/
723 // pp1://a/b/file.fake
724 // pp1://a/subdir/file.b
725 //
726 // pp2://a/BUILD
727 // pp2://a/b/
728 // pp2://a/b/BUILD
729 // pp2://a/b/file.a
730 // pp2://a/subdir.fake/
731 // pp2://a/subdir.fake/file.fake
732 //
733 // Notice that pp1://a/b will be overlaid by pp2://a/b as the latter has a BUILD file and that
734 // takes precedence. On the other hand the package definition pp2://a/BUILD will be ignored
735 // since package //a is already defined under pp1.
736 //
737 // Notice also that pp1://a/b.sym is a relative symlink pointing to b/. This should be resolved
738 // to the definition of //a/b/ under pp1, not under pp2.
739
740 // Set the package paths.
John Catere0d1d0e2017-11-28 20:47:41 -0800741 pkgLocator.set(
742 new PathPackageLocator(
743 outputBase,
tomluee6a6862018-01-17 14:36:26 -0800744 ImmutableList.of(
745 Root.fromPath(rootDirectory.getRelative("pp1")),
746 Root.fromPath(rootDirectory.getRelative("pp2"))),
John Catere0d1d0e2017-11-28 20:47:41 -0800747 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000748 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
749
750 Artifact aBuildArtifact = sourceArtifactUnderPackagePath("a/BUILD", "pp1");
751 Artifact bBuildArtifact = sourceArtifactUnderPackagePath("a/b/BUILD", "pp2");
752
753 RootedPath pp1aBuild = createFile(rootedPath(aBuildArtifact));
754 RootedPath pp1aFileA = createFile(siblingOf(pp1aBuild, "file.a"));
755 RootedPath pp1bFileFake = createFile(siblingOf(pp1aBuild, "b/file.fake"));
756 RootedPath pp1aSubdirFileB = createFile(siblingOf(pp1aBuild, "subdir/file.b"));
757
758 RootedPath pp2aBuild = createFile(rootedPath("a/BUILD", "pp2"));
759 RootedPath pp2bBuild = createFile(rootedPath(bBuildArtifact));
760 RootedPath pp2bFileA = createFile(siblingOf(pp2bBuild, "file.a"));
761 createFile(siblingOf(pp2aBuild, "subdir.fake/file.fake"));
762
763 // Traverse //a including subpackages. The result should contain the pp1-definition of //a and
764 // the pp2-definition of //a/b.
765 traverseAndAssertFiles(
766 pkgRoot(parentOf(rootedPath(aBuildArtifact)), CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000767 regularFileForTesting(pp1aBuild),
768 regularFileForTesting(pp1aFileA),
769 regularFileForTesting(pp1aSubdirFileB),
770 regularFileForTesting(pp2bBuild),
771 regularFileForTesting(pp2bFileA));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000772
773 // Traverse //a excluding subpackages. The result should only contain files from //a and not
774 // from //a/b.
775 traverseAndAssertFiles(
776 pkgRoot(parentOf(rootedPath(aBuildArtifact)), DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000777 regularFileForTesting(pp1aBuild),
778 regularFileForTesting(pp1aFileA),
779 regularFileForTesting(pp1aSubdirFileB));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000780
781 // Create a relative symlink pp1://a/b.sym -> b/. It will be resolved to the subdirectory
782 // pp1://a/b, even though a package definition pp2://a/b exists.
783 RootedPath pp1aBsym = siblingOf(pp1aFileA, "b.sym");
nharmatab4060b62017-04-04 17:11:39 +0000784 pp1aBsym.asPath().createSymbolicLink(PathFragment.create("b"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000785 invalidateDirectory(parentOf(pp1aBsym));
786
787 // Traverse //a excluding subpackages. The relative symlink //a/b.sym points to the subdirectory
788 // a/b, i.e. the pp1-definition, even though there is a pp2-defined package //a/b and we expect
789 // to see b.sym/b.fake (not b/b.fake).
790 traverseAndAssertFiles(
791 pkgRoot(parentOf(rootedPath(aBuildArtifact)), DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000792 regularFileForTesting(pp1aBuild),
793 regularFileForTesting(pp1aFileA),
794 regularFileForTesting(childOf(pp1aBsym, "file.fake")),
nharmatab4060b62017-04-04 17:11:39 +0000795 symlinkToDirectoryForTesting(parentOf(pp1bFileFake), pp1aBsym, PathFragment.create("b")),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000796 regularFileForTesting(pp1aSubdirFileB));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000797 }
798
Florian Weikert92b22362015-12-03 10:17:18 +0000799 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000800 public void testFileDigestChangeCausesRebuild() throws Exception {
801 Artifact artifact = sourceArtifact("foo/bar.txt");
802 RootedPath path = rootedPath(artifact);
803 createFile(path, "hello");
804
805 // Assert that the SkyValue is built and looks right.
806 TraversalRequest params = fileLikeRoot(artifact, DONT_CROSS);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000807 ResolvedFile expected = regularFileForTesting(path);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000808 RecursiveFilesystemTraversalValue v1 = traverseAndAssertFiles(params, expected);
janakr61377f72018-06-04 12:50:13 -0700809 assertThat(progressReceiver.evaluations).contains(params);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000810 progressReceiver.clear();
811
812 // Change the digest of the file. See that the value is rebuilt.
813 appendToFile(path, "world");
814 RecursiveFilesystemTraversalValue v2 = traverseAndAssertFiles(params, expected);
janakr5fb2a482018-03-02 17:48:57 -0800815 assertThat(progressReceiver.invalidations).contains(params);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000816 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000817 assertTraversalRootHashesAreNotEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000818 }
819
Florian Weikert92b22362015-12-03 10:17:18 +0000820 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000821 public void testFileMtimeChangeDoesNotCauseRebuildIfDigestIsUnchanged() throws Exception {
822 Artifact artifact = sourceArtifact("foo/bar.txt");
823 RootedPath path = rootedPath(artifact);
824 createFile(path, "hello");
825
826 // Assert that the SkyValue is built and looks right.
827 TraversalRequest params = fileLikeRoot(artifact, DONT_CROSS);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000828 ResolvedFile expected = regularFileForTesting(path);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000829 RecursiveFilesystemTraversalValue v1 = traverseAndAssertFiles(params, expected);
janakr61377f72018-06-04 12:50:13 -0700830 assertThat(progressReceiver.evaluations).contains(params);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000831 progressReceiver.clear();
832
833 // Change the mtime of the file but not the digest. See that the value is *not* rebuilt.
laszlocsomor29bdf632018-03-13 02:50:46 -0700834 TimestampGranularityUtils.waitForTimestampGranularity(
835 path.asPath().stat().getLastChangeTime(), OutErr.SYSTEM_OUT_ERR);
Laszlo Csomora31e0352018-03-09 04:51:38 -0800836 path.asPath().setLastModifiedTime(System.currentTimeMillis());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000837 RecursiveFilesystemTraversalValue v2 = traverseAndAssertFiles(params, expected);
838 assertThat(v2).isEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000839 assertTraversalRootHashesAreEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000840 }
841
Florian Weikert92b22362015-12-03 10:17:18 +0000842 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000843 public void testGeneratedDirectoryConflictsWithPackage() throws Exception {
844 Artifact genDir = derivedArtifact("a/b");
845 createFile(rootedPath(sourceArtifact("a/b/c/file.real")));
846 createFile(rootedPath(derivedArtifact("a/b/c/file.fake")));
847 createFile(sourceArtifact("a/b/c/BUILD"));
848
janakr5fb2a482018-03-02 17:48:57 -0800849 SkyKey key = fileLikeRoot(genDir, CROSS);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000850 EvaluationResult<SkyValue> result = eval(key);
851 assertThat(result.hasError()).isTrue();
852 ErrorInfo error = result.getError(key);
nharmatabea67e92017-06-16 00:26:27 +0200853 assertThat(error.isTransitivelyTransient()).isFalse();
lberkiaea56b32017-05-30 12:35:33 +0200854 assertThat(error.getException())
855 .hasMessageThat()
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000856 .contains("Generated directory a/b/c conflicts with package under the same path.");
857 }
kush95bf7c82017-08-30 00:27:35 +0200858
859 @Test
860 public void unboundedSymlinkExpansionError() throws Exception {
861 Artifact bazLink = sourceArtifact("foo/baz.sym");
862 Path parentDir = scratch.dir("foo");
863 bazLink.getPath().createSymbolicLink(parentDir);
janakr5fb2a482018-03-02 17:48:57 -0800864 SkyKey key = pkgRoot(parentOf(rootedPath(bazLink)), DONT_CROSS);
kush95bf7c82017-08-30 00:27:35 +0200865 EvaluationResult<SkyValue> result = eval(key);
866 assertThat(result.hasError()).isTrue();
867 ErrorInfo error = result.getError(key);
868 assertThat(error.getException()).isInstanceOf(FileOperationException.class);
869 assertThat(error.getException()).hasMessageThat().contains("Infinite symlink expansion");
870 }
871
872 @Test
873 public void symlinkChainError() throws Exception {
874 scratch.dir("a");
875 Artifact fooLink = sourceArtifact("a/foo.sym");
876 Artifact barLink = sourceArtifact("a/bar.sym");
877 Artifact bazLink = sourceArtifact("a/baz.sym");
878 fooLink.getPath().createSymbolicLink(barLink.getPath());
879 barLink.getPath().createSymbolicLink(bazLink.getPath());
880 bazLink.getPath().createSymbolicLink(fooLink.getPath());
881
janakr5fb2a482018-03-02 17:48:57 -0800882 SkyKey key = pkgRoot(parentOf(rootedPath(bazLink)), DONT_CROSS);
kush95bf7c82017-08-30 00:27:35 +0200883 EvaluationResult<SkyValue> result = eval(key);
884 assertThat(result.hasError()).isTrue();
885 ErrorInfo error = result.getError(key);
886 assertThat(error.getException()).isInstanceOf(FileOperationException.class);
887 assertThat(error.getException()).hasMessageThat().contains("Symlink cycle");
888 }
felly5be4dd62018-02-05 11:11:53 -0800889
890 private static class ArtifactFakeFunction implements SkyFunction {
891 @Nullable
892 @Override
893 public SkyValue compute(SkyKey skyKey, Environment env)
894 throws SkyFunctionException, InterruptedException {
felly1eeba9c2018-02-05 14:32:51 -0800895 ArtifactSkyKey artifactKey = (ArtifactSkyKey) skyKey.argument();
896 Artifact artifact = artifactKey.getArtifact();
felly5be4dd62018-02-05 11:11:53 -0800897 try {
898 return FileArtifactValue.create(artifact.getPath());
899 } catch (IOException e) {
900 throw new SkyFunctionException(e, Transience.PERSISTENT){};
901 }
902 }
903
904 @Nullable
905 @Override
906 public String extractTag(SkyKey skyKey) {
907 return null;
908 }
909 }
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000910}