blob: 5eb13b326df02381f3df12b779909e405a4f3074 [file] [log] [blame]
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +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;
Ulf Adamsc73051c62016-03-23 09:18:13 +000015
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000016import static com.google.common.truth.Truth.assertThat;
17import static com.google.devtools.build.lib.skyframe.SkyframeExecutor.DEFAULT_THREAD_COUNT;
Googler54e24df2016-03-28 19:11:39 +000018import static com.google.devtools.build.skyframe.EvaluationResultSubjectFactory.assertThatEvaluationResult;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000019import static org.junit.Assert.assertArrayEquals;
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +000020import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertFalse;
22import static org.junit.Assert.assertNotSame;
23import static org.junit.Assert.assertNull;
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +000024import static org.junit.Assert.assertTrue;
25import static org.junit.Assert.fail;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000026
27import com.google.common.base.Function;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000028import com.google.common.base.Strings;
29import com.google.common.collect.ImmutableList;
30import com.google.common.collect.ImmutableMap;
31import com.google.common.collect.ImmutableSet;
32import com.google.common.collect.Iterables;
33import com.google.common.collect.Lists;
34import com.google.common.collect.Maps;
35import com.google.common.collect.Sets;
36import com.google.common.testing.EqualsTester;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000037import com.google.devtools.build.lib.analysis.BlazeDirectories;
Lukacs Berkide2183d2015-12-16 11:25:36 +000038import com.google.devtools.build.lib.cmdline.Label;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000039import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000040import com.google.devtools.build.lib.events.NullEventHandler;
41import com.google.devtools.build.lib.events.StoredEventHandler;
42import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
Nathan Harmatad4f75942016-10-18 08:55:17 +000043import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
John Cater5e9ce942016-10-12 17:23:30 +000044import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
John Cater0c0735a2016-11-11 01:52:02 +000045import com.google.devtools.build.lib.skyframe.PackageLookupValue.BuildFileName;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000046import com.google.devtools.build.lib.testutil.ManualClock;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +000047import com.google.devtools.build.lib.testutil.TestConstants;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000048import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000049import com.google.devtools.build.lib.testutil.TestUtils;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000050import com.google.devtools.build.lib.util.Pair;
Mark Schaller6df81792015-12-10 18:47:47 +000051import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000052import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
53import com.google.devtools.build.lib.vfs.FileStatus;
54import com.google.devtools.build.lib.vfs.FileSystem;
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000055import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000056import com.google.devtools.build.lib.vfs.FileSystemUtils;
57import com.google.devtools.build.lib.vfs.Path;
58import com.google.devtools.build.lib.vfs.PathFragment;
59import com.google.devtools.build.lib.vfs.RootedPath;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000060import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
Yun Peng22eb3322016-06-21 14:38:12 +000061import com.google.devtools.build.lib.vfs.util.FileSystems;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000062import com.google.devtools.build.skyframe.ErrorInfo;
63import com.google.devtools.build.skyframe.EvaluationResult;
64import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
65import com.google.devtools.build.skyframe.MemoizingEvaluator;
66import com.google.devtools.build.skyframe.RecordingDifferencer;
67import com.google.devtools.build.skyframe.SequentialBuildDriver;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000068import com.google.devtools.build.skyframe.SkyFunction;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000069import com.google.devtools.build.skyframe.SkyFunctionName;
70import com.google.devtools.build.skyframe.SkyKey;
71import com.google.devtools.build.skyframe.SkyValue;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000072import java.io.ByteArrayInputStream;
73import java.io.ByteArrayOutputStream;
74import java.io.IOException;
75import java.io.ObjectInputStream;
76import java.io.ObjectOutputStream;
77import java.io.OutputStream;
Michajlo Matijkiw528957e2016-01-19 21:17:45 +000078import java.nio.charset.StandardCharsets;
79import java.security.MessageDigest;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000080import java.util.List;
81import java.util.Map;
82import java.util.Set;
83import java.util.UUID;
84import java.util.concurrent.atomic.AtomicInteger;
85import java.util.concurrent.atomic.AtomicReference;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000086import javax.annotation.Nullable;
John Cater94695912016-08-03 12:09:39 +000087import org.junit.Before;
88import org.junit.Test;
89import org.junit.runner.RunWith;
90import org.junit.runners.JUnit4;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000091
Yun Peng22eb3322016-06-21 14:38:12 +000092/** Tests for {@link FileFunction}. */
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +000093@RunWith(JUnit4.class)
94public class FileFunctionTest {
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000095 private CustomInMemoryFs fs;
96 private Path pkgRoot;
Lukacs Berkid3262d12015-10-30 14:33:51 +000097 private Path outputBase;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +000098 private PathPackageLocator pkgLocator;
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000099 private boolean fastDigest;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000100 private ManualClock manualClock;
101 private RecordingDifferencer differencer;
102
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000103 @Before
Yun Peng22eb3322016-06-21 14:38:12 +0000104 public final void createFsAndRoot() throws Exception {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000105 fastDigest = true;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000106 manualClock = new ManualClock();
107 createFsAndRoot(new CustomInMemoryFs(manualClock));
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000108 }
109
110 private void createFsAndRoot(CustomInMemoryFs fs) throws IOException {
111 this.fs = fs;
112 pkgRoot = fs.getRootDirectory().getRelative("root");
Lukacs Berkid3262d12015-10-30 14:33:51 +0000113 outputBase = fs.getRootDirectory().getRelative("output_base");
114 pkgLocator = new PathPackageLocator(outputBase, ImmutableList.of(pkgRoot));
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000115 FileSystemUtils.createDirectoryAndParents(pkgRoot);
116 }
117
118 private SequentialBuildDriver makeDriver() {
Nathan Harmatad4f75942016-10-18 08:55:17 +0000119 return makeDriver(ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000120 }
121
Nathan Harmatad4f75942016-10-18 08:55:17 +0000122 private SequentialBuildDriver makeDriver(ExternalFileAction externalFileAction) {
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000123 AtomicReference<PathPackageLocator> pkgLocatorRef = new AtomicReference<>(pkgLocator);
Yun Peng22eb3322016-06-21 14:38:12 +0000124 BlazeDirectories directories =
125 new BlazeDirectories(pkgRoot, outputBase, pkgRoot, TestConstants.PRODUCT_NAME);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000126 ExternalFilesHelper externalFilesHelper =
Nathan Harmatad4f75942016-10-18 08:55:17 +0000127 new ExternalFilesHelper(pkgLocatorRef, externalFileAction, directories);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000128 differencer = new RecordingDifferencer();
129 MemoizingEvaluator evaluator =
130 new InMemoryMemoizingEvaluator(
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000131 ImmutableMap.<SkyFunctionName, SkyFunction>builder()
Yun Peng22eb3322016-06-21 14:38:12 +0000132 .put(
133 SkyFunctions.FILE_STATE,
134 new FileStateFunction(
135 new AtomicReference<TimestampGranularityMonitor>(), externalFilesHelper))
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000136 .put(
137 SkyFunctions.FILE_SYMLINK_CYCLE_UNIQUENESS,
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000138 new FileSymlinkCycleUniquenessFunction())
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000139 .put(
140 SkyFunctions.FILE_SYMLINK_INFINITE_EXPANSION_UNIQUENESS,
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000141 new FileSymlinkInfiniteExpansionUniquenessFunction())
142 .put(SkyFunctions.FILE, new FileFunction(pkgLocatorRef))
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000143 .put(
144 SkyFunctions.PACKAGE,
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000145 new PackageFunction(null, null, null, null, null, null, null))
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000146 .put(
147 SkyFunctions.PACKAGE_LOOKUP,
148 new PackageLookupFunction(
John Cater5e9ce942016-10-12 17:23:30 +0000149 new AtomicReference<>(ImmutableSet.<PackageIdentifier>of()),
John Cater0c0735a2016-11-11 01:52:02 +0000150 CrossRepositoryLabelViolationStrategy.ERROR,
151 ImmutableList.of(BuildFileName.BUILD_DOT_BAZEL, BuildFileName.BUILD)))
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000152 .put(
153 SkyFunctions.WORKSPACE_AST,
Damien Martin-Guillerez4ecfe512016-01-22 11:03:23 +0000154 new WorkspaceASTFunction(TestRuleClassProvider.getRuleClassProvider()))
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000155 .put(
156 SkyFunctions.WORKSPACE_FILE,
157 new WorkspaceFileFunction(
158 TestRuleClassProvider.getRuleClassProvider(),
Nathan Harmata3ad56452016-06-10 16:17:45 +0000159 TestConstants.PACKAGE_FACTORY_FACTORY_FOR_TESTING.create(
160 TestRuleClassProvider.getRuleClassProvider(), fs),
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000161 directories))
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000162 .put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction())
John Caterb4f461e2016-10-25 16:16:35 +0000163 .put(SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction())
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000164 .build(),
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000165 differencer);
166 PrecomputedValue.BUILD_ID.set(differencer, UUID.randomUUID());
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000167 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000168 return new SequentialBuildDriver(evaluator);
169 }
170
171 private FileValue valueForPath(Path path) throws InterruptedException {
172 return valueForPathHelper(pkgRoot, path);
173 }
174
175 private FileValue valueForPathOutsidePkgRoot(Path path) throws InterruptedException {
176 return valueForPathHelper(fs.getRootDirectory(), path);
177 }
178
179 private FileValue valueForPathHelper(Path root, Path path) throws InterruptedException {
180 PathFragment pathFragment = path.relativeTo(root);
181 RootedPath rootedPath = RootedPath.toRootedPath(root, pathFragment);
182 SequentialBuildDriver driver = makeDriver();
183 SkyKey key = FileValue.key(rootedPath);
184 EvaluationResult<FileValue> result =
185 driver.evaluate(
186 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
187 assertFalse(result.hasError());
188 return result.get(key);
189 }
190
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000191 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000192 public void testFileValueHashCodeAndEqualsContract() throws Exception {
193 Path pathA = file(pkgRoot + "a", "a");
194 Path pathB = file(pkgRoot + "b", "b");
195 FileValue valueA1 = valueForPathOutsidePkgRoot(pathA);
196 FileValue valueA2 = valueForPathOutsidePkgRoot(pathA);
197 FileValue valueB1 = valueForPathOutsidePkgRoot(pathB);
198 FileValue valueB2 = valueForPathOutsidePkgRoot(pathB);
199 new EqualsTester()
200 .addEqualityGroup(valueA1, valueA2)
201 .addEqualityGroup(valueB1, valueB2)
202 .testEquals();
203 }
204
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000205 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000206 public void testIsDirectory() throws Exception {
207 assertFalse(valueForPath(file("a")).isDirectory());
208 assertFalse(valueForPath(path("nonexistent")).isDirectory());
209 assertTrue(valueForPath(directory("dir")).isDirectory());
210
211 assertFalse(valueForPath(symlink("sa", "a")).isDirectory());
212 assertFalse(valueForPath(symlink("smissing", "missing")).isDirectory());
213 assertTrue(valueForPath(symlink("sdir", "dir")).isDirectory());
214 assertTrue(valueForPath(symlink("ssdir", "sdir")).isDirectory());
215 }
216
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000217 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000218 public void testIsFile() throws Exception {
219 assertTrue(valueForPath(file("a")).isFile());
220 assertFalse(valueForPath(path("nonexistent")).isFile());
221 assertFalse(valueForPath(directory("dir")).isFile());
222
223 assertTrue(valueForPath(symlink("sa", "a")).isFile());
224 assertFalse(valueForPath(symlink("smissing", "missing")).isFile());
225 assertFalse(valueForPath(symlink("sdir", "dir")).isFile());
226 assertTrue(valueForPath(symlink("ssfile", "sa")).isFile());
227 }
228
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000229 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000230 public void testSimpleIndependentFiles() throws Exception {
231 file("a");
232 file("b");
233
234 Set<RootedPath> seenFiles = Sets.newHashSet();
235 seenFiles.addAll(getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("a", false, "b"));
236 seenFiles.addAll(getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("b", false, "a"));
237 assertThat(seenFiles).containsExactly(rootedPath("a"), rootedPath("b"), rootedPath(""));
238 }
239
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000240 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000241 public void testSimpleSymlink() throws Exception {
242 symlink("a", "b");
243 file("b");
244
245 assertValueChangesIfContentsOfFileChanges("a", false, "b");
246 assertValueChangesIfContentsOfFileChanges("b", true, "a");
247 }
248
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000249 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000250 public void testTransitiveSymlink() throws Exception {
251 symlink("a", "b");
252 symlink("b", "c");
253 file("c");
254
255 assertValueChangesIfContentsOfFileChanges("a", false, "b");
256 assertValueChangesIfContentsOfFileChanges("a", false, "c");
257 assertValueChangesIfContentsOfFileChanges("b", true, "a");
258 assertValueChangesIfContentsOfFileChanges("c", true, "b");
259 assertValueChangesIfContentsOfFileChanges("c", true, "a");
260 }
261
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000262 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000263 public void testFileUnderDirectorySymlink() throws Exception {
264 symlink("a", "b/c");
265 symlink("b", "d");
266 assertValueChangesIfContentsOfDirectoryChanges("b", true, "a/e");
267 }
268
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000269 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000270 public void testSymlinkInDirectory() throws Exception {
271 symlink("a/aa", "ab");
272 file("a/ab");
273
274 assertValueChangesIfContentsOfFileChanges("a/aa", false, "a/ab");
275 assertValueChangesIfContentsOfFileChanges("a/ab", true, "a/aa");
276 }
277
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000278 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000279 public void testRelativeSymlink() throws Exception {
280 symlink("a/aa/aaa", "../ab/aba");
281 file("a/ab/aba");
282 assertValueChangesIfContentsOfFileChanges("a/ab/aba", true, "a/aa/aaa");
283 }
284
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000285 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000286 public void testDoubleRelativeSymlink() throws Exception {
287 symlink("a/b/c/d", "../../e/f");
288 file("a/e/f");
289 assertValueChangesIfContentsOfFileChanges("a/e/f", true, "a/b/c/d");
290 }
291
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000292 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000293 public void testExternalRelativeSymlink() throws Exception {
294 symlink("a", "../outside");
295 file("b");
296 file("../outside");
297 Set<RootedPath> seenFiles = Sets.newHashSet();
298 seenFiles.addAll(getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("b", false, "a"));
299 seenFiles.addAll(
300 getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("../outside", true, "a"));
301 assertThat(seenFiles)
302 .containsExactly(
303 rootedPath("a"),
304 rootedPath(""),
305 RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.EMPTY_FRAGMENT),
306 RootedPath.toRootedPath(fs.getRootDirectory(), new PathFragment("outside")));
307 }
308
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000309 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000310 public void testAbsoluteSymlink() throws Exception {
311 symlink("a", "/absolute");
312 file("b");
313 file("/absolute");
314 Set<RootedPath> seenFiles = Sets.newHashSet();
315 seenFiles.addAll(getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("b", false, "a"));
316 seenFiles.addAll(
317 getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("/absolute", true, "a"));
318 assertThat(seenFiles)
319 .containsExactly(
320 rootedPath("a"),
321 rootedPath(""),
322 RootedPath.toRootedPath(fs.getRootDirectory(), PathFragment.EMPTY_FRAGMENT),
323 RootedPath.toRootedPath(fs.getRootDirectory(), new PathFragment("absolute")));
324 }
325
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000326 @Test
Lukacs Berkide2183d2015-12-16 11:25:36 +0000327 public void testAbsoluteSymlinkToExternal() throws Exception {
328 String externalPath =
Kristina Chodorowbd016c92016-09-09 14:10:44 +0000329 outputBase.getRelative(Label.EXTERNAL_PACKAGE_NAME).getRelative("a/b").getPathString();
Lukacs Berkide2183d2015-12-16 11:25:36 +0000330 symlink("a", externalPath);
331 file("b");
332 file(externalPath);
333 Set<RootedPath> seenFiles = Sets.newHashSet();
334 seenFiles.addAll(getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("b", false, "a"));
335 seenFiles.addAll(
336 getFilesSeenAndAssertValueChangesIfContentsOfFileChanges(externalPath, true, "a"));
337 Path root = fs.getRootDirectory();
338 assertThat(seenFiles)
339 .containsExactly(
340 rootedPath("WORKSPACE"),
341 rootedPath("a"),
342 rootedPath(""),
343 RootedPath.toRootedPath(root, PathFragment.EMPTY_FRAGMENT),
344 RootedPath.toRootedPath(root, new PathFragment("output_base")),
345 RootedPath.toRootedPath(root, new PathFragment("output_base/external")),
346 RootedPath.toRootedPath(root, new PathFragment("output_base/external/a")),
347 RootedPath.toRootedPath(root, new PathFragment("output_base/external/a/b")));
348 }
349
350 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000351 public void testSymlinkAsAncestor() throws Exception {
352 file("a/b/c/d");
353 symlink("f", "a/b/c");
354 assertValueChangesIfContentsOfFileChanges("a/b/c/d", true, "f/d");
355 }
356
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000357 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000358 public void testSymlinkAsAncestorNested() throws Exception {
359 file("a/b/c/d");
360 symlink("f", "a/b");
361 assertValueChangesIfContentsOfFileChanges("a/b/c/d", true, "f/c/d");
362 }
363
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000364 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000365 public void testTwoSymlinksInAncestors() throws Exception {
366 file("a/aa/aaa/aaaa");
367 symlink("b/ba/baa", "../../a/aa");
368 symlink("c/ca", "../b/ba");
369
370 assertValueChangesIfContentsOfFileChanges("c/ca", true, "c/ca/baa/aaa/aaaa");
371 assertValueChangesIfContentsOfFileChanges("b/ba/baa", true, "c/ca/baa/aaa/aaaa");
372 assertValueChangesIfContentsOfFileChanges("a/aa/aaa/aaaa", true, "c/ca/baa/aaa/aaaa");
373 }
374
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000375 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000376 public void testSelfReferencingSymlink() throws Exception {
377 symlink("a", "a");
378 assertError("a");
379 }
380
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000381 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000382 public void testMutuallyReferencingSymlinks() throws Exception {
383 symlink("a", "b");
384 symlink("b", "a");
385 assertError("a");
386 }
387
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000388 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000389 public void testRecursiveNestingSymlink() throws Exception {
390 symlink("a/a", "../a");
391 assertError("a/a/b");
392 }
393
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000394 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000395 public void testBrokenSymlink() throws Exception {
396 symlink("a", "b");
397 Set<RootedPath> seenFiles = Sets.newHashSet();
398 seenFiles.addAll(getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("b", true, "a"));
399 seenFiles.addAll(getFilesSeenAndAssertValueChangesIfContentsOfFileChanges("a", false, "b"));
400 assertThat(seenFiles).containsExactly(rootedPath("a"), rootedPath("b"), rootedPath(""));
401 }
402
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000403 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000404 public void testBrokenDirectorySymlink() throws Exception {
405 symlink("a", "b");
406 file("c");
407
408 assertValueChangesIfContentsOfDirectoryChanges("a", true, "a/aa");
409 // This just creates the directory "b", which doesn't change the value for "a/aa", since "a/aa"
410 // still has real path "b/aa" and still doesn't exist.
411 assertValueChangesIfContentsOfDirectoryChanges("b", false, "a/aa");
412 assertValueChangesIfContentsOfFileChanges("c", false, "a/aa");
413 }
414
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000415 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000416 public void testTraverseIntoVirtualNonDirectory() throws Exception {
417 file("dir/a");
418 symlink("vdir", "dir");
419 // The following evaluation should not throw IOExceptions.
420 assertNoError("vdir/a/aa/aaa");
421 }
422
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000423 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000424 public void testFileCreation() throws Exception {
425 FileValue a = valueForPath(path("file"));
426 Path p = file("file");
427 FileValue b = valueForPath(p);
428 assertFalse(a.equals(b));
429 }
430
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000431 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000432 public void testEmptyFile() throws Exception {
433 final byte[] digest = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
434 createFsAndRoot(
435 new CustomInMemoryFs(manualClock) {
436 @Override
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000437 protected byte[] getFastDigest(Path path, HashFunction hf) throws IOException {
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000438 return digest;
439 }
440 });
441 Path p = file("file");
442 p.setLastModifiedTime(0L);
443 FileValue a = valueForPath(p);
444 p.setLastModifiedTime(1L);
445 assertThat(valueForPath(p)).isNotEqualTo(a);
446 p.setLastModifiedTime(0L);
447 assertEquals(a, valueForPath(p));
448 FileSystemUtils.writeContentAsLatin1(p, "content");
449 // Same digest, but now non-empty.
450 assertThat(valueForPath(p)).isNotEqualTo(a);
451 }
452
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000453 @Test
Michajlo Matijkiw528957e2016-01-19 21:17:45 +0000454 public void testUnreadableFileWithNoFastDigest() throws Exception {
455 Path p = file("unreadable");
456 p.chmod(0);
457 p.setLastModifiedTime(0L);
458
459 FileValue value = valueForPath(p);
460 assertTrue(value.exists());
461 assertThat(value.getDigest()).isNull();
462
463 p.setLastModifiedTime(10L);
464 assertThat(valueForPath(p)).isNotEqualTo(value);
465
466 p.setLastModifiedTime(0L);
467 assertThat(valueForPath(p)).isEqualTo(value);
468 }
469
470 @Test
471 public void testUnreadableFileWithFastDigest() throws Exception {
Yun Peng22eb3322016-06-21 14:38:12 +0000472 final byte[] expectedDigest =
473 MessageDigest.getInstance("md5").digest("blah".getBytes(StandardCharsets.UTF_8));
Michajlo Matijkiw528957e2016-01-19 21:17:45 +0000474
475 createFsAndRoot(
476 new CustomInMemoryFs(manualClock) {
477 @Override
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000478 protected byte[] getFastDigest(Path path, HashFunction hf) {
Michajlo Matijkiw528957e2016-01-19 21:17:45 +0000479 return path.getBaseName().equals("unreadable") ? expectedDigest : null;
480 }
481 });
482
483 Path p = file("unreadable");
484 p.chmod(0);
485
486 FileValue value = valueForPath(p);
487 assertThat(value.exists()).isTrue();
488 assertThat(value.getDigest()).isNotNull();
489 }
490
491 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000492 public void testFileModificationModTime() throws Exception {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000493 fastDigest = false;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000494 Path p = file("file");
495 FileValue a = valueForPath(p);
496 p.setLastModifiedTime(42);
497 FileValue b = valueForPath(p);
498 assertFalse(a.equals(b));
499 }
500
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000501 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000502 public void testFileModificationDigest() throws Exception {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000503 fastDigest = true;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000504 Path p = file("file");
505 FileValue a = valueForPath(p);
506 FileSystemUtils.writeContentAsLatin1(p, "goop");
507 FileValue b = valueForPath(p);
508 assertFalse(a.equals(b));
509 }
510
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000511 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000512 public void testModTimeVsDigest() throws Exception {
513 Path p = file("somefile", "fizzley");
514
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000515 fastDigest = true;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000516 FileValue aMd5 = valueForPath(p);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000517 fastDigest = false;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000518 FileValue aModTime = valueForPath(p);
519 assertThat(aModTime).isNotEqualTo(aMd5);
520 new EqualsTester().addEqualityGroup(aMd5).addEqualityGroup(aModTime).testEquals();
521 }
522
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000523 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000524 public void testFileDeletion() throws Exception {
525 Path p = file("file");
526 FileValue a = valueForPath(p);
527 p.delete();
528 FileValue b = valueForPath(p);
529 assertFalse(a.equals(b));
530 }
531
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000532 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000533 public void testFileTypeChange() throws Exception {
534 Path p = file("file");
535 FileValue a = valueForPath(p);
536 p.delete();
537 p = symlink("file", "foo");
538 FileValue b = valueForPath(p);
539 p.delete();
540 FileSystemUtils.createDirectoryAndParents(pkgRoot.getRelative("file"));
541 FileValue c = valueForPath(p);
542 assertFalse(a.equals(b));
543 assertFalse(b.equals(c));
544 assertFalse(a.equals(c));
545 }
546
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000547 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000548 public void testSymlinkTargetChange() throws Exception {
549 Path p = symlink("symlink", "foo");
550 FileValue a = valueForPath(p);
551 p.delete();
552 p = symlink("symlink", "bar");
553 FileValue b = valueForPath(p);
554 assertThat(b).isNotEqualTo(a);
555 }
556
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000557 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000558 public void testSymlinkTargetContentsChangeModTime() throws Exception {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000559 fastDigest = false;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000560 Path fooPath = file("foo");
561 FileSystemUtils.writeContentAsLatin1(fooPath, "foo");
562 Path p = symlink("symlink", "foo");
563 FileValue a = valueForPath(p);
564 fooPath.setLastModifiedTime(88);
565 FileValue b = valueForPath(p);
566 assertThat(b).isNotEqualTo(a);
567 }
568
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000569 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000570 public void testSymlinkTargetContentsChangeDigest() throws Exception {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000571 fastDigest = true;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000572 Path fooPath = file("foo");
573 FileSystemUtils.writeContentAsLatin1(fooPath, "foo");
574 Path p = symlink("symlink", "foo");
575 FileValue a = valueForPath(p);
576 FileSystemUtils.writeContentAsLatin1(fooPath, "bar");
577 FileValue b = valueForPath(p);
578 assertThat(b).isNotEqualTo(a);
579 }
580
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000581 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000582 public void testRealPath() throws Exception {
583 file("file");
584 directory("directory");
585 file("directory/file");
586 symlink("directory/link", "file");
587 symlink("directory/doublelink", "link");
588 symlink("directory/parentlink", "../file");
589 symlink("directory/doubleparentlink", "../link");
590 symlink("link", "file");
591 symlink("deadlink", "missing_file");
592 symlink("dirlink", "directory");
593 symlink("doublelink", "link");
594 symlink("doubledirlink", "dirlink");
595
596 checkRealPath("file");
597 checkRealPath("link");
598 checkRealPath("doublelink");
599
600 for (String dir : new String[] {"directory", "dirlink", "doubledirlink"}) {
601 checkRealPath(dir);
602 checkRealPath(dir + "/file");
603 checkRealPath(dir + "/link");
604 checkRealPath(dir + "/doublelink");
605 checkRealPath(dir + "/parentlink");
606 }
607
608 assertRealPath("missing", "missing");
609 assertRealPath("deadlink", "missing_file");
610 }
611
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000612 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000613 public void testRealPathRelativeSymlink() throws Exception {
614 directory("dir");
615 symlink("dir/link", "../dir2");
616 directory("dir2");
617 symlink("dir2/filelink", "../dest");
618 file("dest");
619
620 checkRealPath("dir/link/filelink");
621 }
622
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000623 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000624 public void testSymlinkAcrossPackageRoots() throws Exception {
625 Path otherPkgRoot = fs.getRootDirectory().getRelative("other_root");
Lukacs Berkid3262d12015-10-30 14:33:51 +0000626 pkgLocator = new PathPackageLocator(outputBase, ImmutableList.of(pkgRoot, otherPkgRoot));
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000627 symlink("a", "/other_root/b");
628 assertValueChangesIfContentsOfFileChanges("/other_root/b", true, "a");
629 }
630
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000631 @Test
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000632 public void testFilesOutsideRootIsReEvaluated() throws Exception {
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000633 Path file = file("/outsideroot");
634 SequentialBuildDriver driver = makeDriver();
635 SkyKey key = skyKey("/outsideroot");
636 EvaluationResult<SkyValue> result;
637 result =
638 driver.evaluate(
639 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
640 if (result.hasError()) {
641 fail(String.format("Evaluation error for %s: %s", key, result.getError()));
642 }
643 FileValue oldValue = (FileValue) result.get(key);
644 assertTrue(oldValue.exists());
645
646 file.delete();
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000647 differencer.invalidate(ImmutableList.of(fileStateSkyKey("/outsideroot")));
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000648 result =
649 driver.evaluate(
650 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
651 if (result.hasError()) {
652 fail(String.format("Evaluation error for %s: %s", key, result.getError()));
653 }
654 FileValue newValue = (FileValue) result.get(key);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000655 assertNotSame(oldValue, newValue);
656 assertFalse(newValue.exists());
657 }
658
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000659 @Test
Nathan Harmatad4f75942016-10-18 08:55:17 +0000660 public void testFilesOutsideRootWhenExternalAssumedNonExistentAndImmutable() throws Exception {
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000661 file("/outsideroot");
662
Nathan Harmatad4f75942016-10-18 08:55:17 +0000663 SequentialBuildDriver driver =
664 makeDriver(ExternalFileAction.ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000665 SkyKey key = skyKey("/outsideroot");
666 EvaluationResult<SkyValue> result =
667 driver.evaluate(
668 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
669
Nathan Harmatad4f75942016-10-18 08:55:17 +0000670 assertThatEvaluationResult(result).hasNoError();
671 FileValue value = (FileValue) result.get(key);
672 assertThat(value).isNotNull();
673 assertFalse(value.exists());
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000674 }
675
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000676 @Test
Nathan Harmatad4f75942016-10-18 08:55:17 +0000677 public void testAbsoluteSymlinksToFilesOutsideRootWhenExternalAssumedNonExistentAndImmutable()
678 throws Exception {
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000679 file("/outsideroot");
680 symlink("a", "/outsideroot");
681
Nathan Harmatad4f75942016-10-18 08:55:17 +0000682 SequentialBuildDriver driver =
683 makeDriver(ExternalFileAction.ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000684 SkyKey key = skyKey("a");
685 EvaluationResult<SkyValue> result =
686 driver.evaluate(
687 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
688
Nathan Harmatad4f75942016-10-18 08:55:17 +0000689 assertThatEvaluationResult(result).hasNoError();
690 FileValue value = (FileValue) result.get(key);
691 assertThat(value).isNotNull();
692 assertFalse(value.exists());
Googler54e24df2016-03-28 19:11:39 +0000693 }
694
Googler54e24df2016-03-28 19:11:39 +0000695 @Test
Nathan Harmatad4f75942016-10-18 08:55:17 +0000696 public void testAbsoluteSymlinksReferredByInternalFilesToFilesOutsideRootWhenExternalAssumedNonExistentAndImmutable()
Googler54e24df2016-03-28 19:11:39 +0000697 throws Exception {
698 file("/outsideroot/src/foo/bar");
699 symlink("/root/src", "/outsideroot/src");
700
Nathan Harmatad4f75942016-10-18 08:55:17 +0000701 SequentialBuildDriver driver =
702 makeDriver(ExternalFileAction.ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS);
Googler54e24df2016-03-28 19:11:39 +0000703 SkyKey key = skyKey("/root/src/foo/bar");
704 EvaluationResult<SkyValue> result =
705 driver.evaluate(
706 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
707
Nathan Harmatad4f75942016-10-18 08:55:17 +0000708 assertThatEvaluationResult(result).hasNoError();
709 FileValue value = (FileValue) result.get(key);
710 assertThat(value).isNotNull();
711 assertFalse(value.exists());
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000712 }
713
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000714 @Test
Nathan Harmatad4f75942016-10-18 08:55:17 +0000715 public void testRelativeSymlinksToFilesOutsideRootWhenExternalAssumedNonExistentAndImmutable()
716 throws Exception {
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000717 file("../outsideroot");
718 symlink("a", "../outsideroot");
Nathan Harmatad4f75942016-10-18 08:55:17 +0000719 SequentialBuildDriver driver =
720 makeDriver(ExternalFileAction.ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000721 SkyKey key = skyKey("a");
722 EvaluationResult<SkyValue> result =
723 driver.evaluate(
724 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
Googler54e24df2016-03-28 19:11:39 +0000725
Nathan Harmatad4f75942016-10-18 08:55:17 +0000726 assertThatEvaluationResult(result).hasNoError();
727 FileValue value = (FileValue) result.get(key);
728 assertThat(value).isNotNull();
729 assertFalse(value.exists());
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000730 }
731
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000732 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000733 public void testAbsoluteSymlinksBackIntoSourcesOkWhenExternalDisallowed() throws Exception {
734 Path file = file("insideroot");
735 symlink("a", file.getPathString());
736
Nathan Harmatad4f75942016-10-18 08:55:17 +0000737 SequentialBuildDriver driver =
738 makeDriver(ExternalFileAction.ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS);
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000739 SkyKey key = skyKey("a");
740 EvaluationResult<SkyValue> result =
741 driver.evaluate(
742 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
743
Nathan Harmatad4f75942016-10-18 08:55:17 +0000744 assertThatEvaluationResult(result).hasNoError();
745 FileValue value = (FileValue) result.get(key);
746 assertThat(value).isNotNull();
747 assertTrue(value.exists());
748 assertThat(value.realRootedPath().getRelativePath().getPathString()).isEqualTo("insideroot");
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000749 }
750
751 @SuppressWarnings({"rawtypes", "unchecked"})
752 private static Set<RootedPath> filesSeen(MemoizingEvaluator graph) {
753 return ImmutableSet.copyOf(
754 (Iterable<RootedPath>)
755 (Iterable)
756 Iterables.transform(
757 Iterables.filter(
758 graph.getValues().keySet(),
759 SkyFunctionName.functionIs(SkyFunctions.FILE_STATE)),
760 SkyKey.NODE_NAME));
761 }
762
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000763 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000764 public void testSize() throws Exception {
765 Path file = file("file");
766 int fileSize = 20;
767 FileSystemUtils.writeContentAsLatin1(file, Strings.repeat("a", fileSize));
768 assertEquals(fileSize, valueForPath(file).getSize());
769 Path dir = directory("directory");
770 file(dir.getChild("child").getPathString());
771 try {
772 valueForPath(dir).getSize();
773 fail();
774 } catch (IllegalStateException e) {
775 // Expected.
776 }
777 Path nonexistent = fs.getPath("/root/noexist");
778 try {
779 valueForPath(nonexistent).getSize();
780 fail();
781 } catch (IllegalStateException e) {
782 // Expected.
783 }
784 Path symlink = symlink("link", "/root/file");
785 // Symlink stores size of target, not link.
786 assertEquals(fileSize, valueForPath(symlink).getSize());
787 assertTrue(symlink.delete());
788 symlink = symlink("link", "/root/directory");
789 try {
790 valueForPath(symlink).getSize();
791 fail();
792 } catch (IllegalStateException e) {
793 // Expected.
794 }
795 assertTrue(symlink.delete());
796 symlink = symlink("link", "/root/noexist");
797 try {
798 valueForPath(symlink).getSize();
799 fail();
800 } catch (IllegalStateException e) {
801 // Expected.
802 }
803 }
804
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000805 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000806 public void testDigest() throws Exception {
807 final AtomicInteger digestCalls = new AtomicInteger(0);
808 int expectedCalls = 0;
809 fs =
810 new CustomInMemoryFs(manualClock) {
811 @Override
812 protected byte[] getMD5Digest(Path path) throws IOException {
813 digestCalls.incrementAndGet();
814 return super.getMD5Digest(path);
815 }
816 };
817 pkgRoot = fs.getRootDirectory().getRelative("root");
818 Path file = file("file");
819 FileSystemUtils.writeContentAsLatin1(file, Strings.repeat("a", 20));
820 byte[] digest = file.getMD5Digest();
821 expectedCalls++;
822 assertEquals(expectedCalls, digestCalls.get());
823 FileValue value = valueForPath(file);
824 expectedCalls++;
825 assertEquals(expectedCalls, digestCalls.get());
826 assertArrayEquals(digest, value.getDigest());
827 // Digest is cached -- no filesystem access.
828 assertEquals(expectedCalls, digestCalls.get());
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000829 fastDigest = false;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000830 digestCalls.set(0);
831 value = valueForPath(file);
832 // No new digest calls.
833 assertEquals(0, digestCalls.get());
834 assertNull(value.getDigest());
835 assertEquals(0, digestCalls.get());
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000836 fastDigest = true;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000837 Path dir = directory("directory");
838 try {
839 assertNull(valueForPath(dir).getDigest());
840 fail();
841 } catch (IllegalStateException e) {
842 // Expected.
843 }
844 assertEquals(0, digestCalls.get()); // No digest calls made for directory.
845 Path nonexistent = fs.getPath("/root/noexist");
846 try {
847 assertNull(valueForPath(nonexistent).getDigest());
848 fail();
849 } catch (IllegalStateException e) {
850 // Expected.
851 }
852 assertEquals(0, digestCalls.get()); // No digest calls made for nonexistent file.
853 Path symlink = symlink("link", "/root/file");
854 value = valueForPath(symlink);
855 assertEquals(1, digestCalls.get());
856 // Symlink stores digest of target, not link.
857 assertArrayEquals(digest, value.getDigest());
858 assertEquals(1, digestCalls.get());
859 digestCalls.set(0);
860 assertTrue(symlink.delete());
861 symlink = symlink("link", "/root/directory");
862 // Symlink stores digest of target, not link, for directories too.
863 try {
864 assertNull(valueForPath(symlink).getDigest());
865 fail();
866 } catch (IllegalStateException e) {
867 // Expected.
868 }
869 assertEquals(0, digestCalls.get());
870 }
871
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000872 @Test
Nathan Harmatac0f3d4e2016-10-17 15:49:34 +0000873 public void testDoesntStatChildIfParentDoesntExist() throws Exception {
874 // Our custom filesystem says "a" does not exist, so FileFunction shouldn't bother trying to
875 // think about "a/b". Test for this by having a stat of "a/b" fail with an io error, and
876 // observing that we don't encounter the error.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000877 fs.stubStat(path("a"), null);
Nathan Harmatac0f3d4e2016-10-17 15:49:34 +0000878 fs.stubStatError(path("a/b"), new IOException("ouch!"));
879 assertThat(valueForPath(path("a/b")).exists()).isFalse();
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000880 }
881
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000882 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000883 public void testFilesystemInconsistencies_ParentIsntADirectory() throws Exception {
884 file("a/b");
885 // Our custom filesystem says "a/b" exists but its parent "a" is a file.
886 FileStatus inconsistentParentFileStatus =
887 new FileStatus() {
888 @Override
889 public boolean isFile() {
890 return true;
891 }
892
893 @Override
894 public boolean isSpecialFile() {
895 return false;
896 }
897
898 @Override
899 public boolean isDirectory() {
900 return false;
901 }
902
903 @Override
904 public boolean isSymbolicLink() {
905 return false;
906 }
907
908 @Override
909 public long getSize() throws IOException {
910 return 0;
911 }
912
913 @Override
914 public long getLastModifiedTime() throws IOException {
915 return 0;
916 }
917
918 @Override
919 public long getLastChangeTime() throws IOException {
920 return 0;
921 }
922
923 @Override
924 public long getNodeId() throws IOException {
925 return 0;
926 }
927 };
928 fs.stubStat(path("a"), inconsistentParentFileStatus);
929 // Disable fast-path md5 so that we don't try try to md5 the "a" (since it actually physically
930 // is a directory).
Ola Rozenfeld39dbc982016-11-17 20:14:56 +0000931 fastDigest = false;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000932 SequentialBuildDriver driver = makeDriver();
933 SkyKey skyKey = skyKey("a/b");
934 EvaluationResult<FileValue> result =
935 driver.evaluate(
936 ImmutableList.of(skyKey), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
937 assertTrue(result.hasError());
938 ErrorInfo errorInfo = result.getError(skyKey);
939 assertThat(errorInfo.getException()).isInstanceOf(InconsistentFilesystemException.class);
940 assertThat(errorInfo.getException().getMessage())
941 .contains("file /root/a/b exists but its parent path /root/a isn't an existing directory");
942 }
943
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +0000944 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000945 public void testFilesystemInconsistencies_GetFastDigest() throws Exception {
946 file("a");
947 // Our custom filesystem says "a/b" exists but "a" does not exist.
948 fs.stubFastDigestError(path("a"), new IOException("nope"));
949 SequentialBuildDriver driver = makeDriver();
950 SkyKey skyKey = skyKey("a");
951 EvaluationResult<FileValue> result =
952 driver.evaluate(
953 ImmutableList.of(skyKey), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
954 assertTrue(result.hasError());
955 ErrorInfo errorInfo = result.getError(skyKey);
956 assertThat(errorInfo.getException()).isInstanceOf(InconsistentFilesystemException.class);
957 assertThat(errorInfo.getException().getMessage()).contains("encountered error 'nope'");
958 assertThat(errorInfo.getException().getMessage()).contains("/root/a is no longer a file");
959 }
960
Michajlo Matijkiw528957e2016-01-19 21:17:45 +0000961 @Test
962 public void testFilesystemInconsistencies_GetFastDigestAndIsReadableFailure() throws Exception {
963 createFsAndRoot(
964 new CustomInMemoryFs(manualClock) {
965 @Override
966 protected boolean isReadable(Path path) throws IOException {
967 if (path.getBaseName().equals("unreadable")) {
968 throw new IOException("isReadable failed");
969 }
970 return super.isReadable(path);
971 }
972 });
973
974 Path p = file("unreadable");
975 p.chmod(0);
976
977 SequentialBuildDriver driver = makeDriver();
978 SkyKey skyKey = skyKey("unreadable");
979 EvaluationResult<FileValue> result =
980 driver.evaluate(
981 ImmutableList.of(skyKey), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
982 assertTrue(result.hasError());
983 ErrorInfo errorInfo = result.getError(skyKey);
984 assertThat(errorInfo.getException()).isInstanceOf(InconsistentFilesystemException.class);
985 assertThat(errorInfo.getException().getMessage())
986 .contains("encountered error 'isReadable failed'");
987 assertThat(errorInfo.getException().getMessage())
988 .contains("/root/unreadable is no longer a file");
989 }
990
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +0000991 private void runTestSymlinkCycle(boolean ancestorCycle, boolean startInCycle) throws Exception {
992 symlink("a", "b");
993 symlink("b", "c");
994 symlink("c", "d");
995 symlink("d", "e");
996 symlink("e", "c");
997 // We build multiple keys at once to make sure the cycle is reported exactly once.
998 Map<RootedPath, ImmutableList<RootedPath>> startToCycleMap =
999 ImmutableMap.<RootedPath, ImmutableList<RootedPath>>builder()
1000 .put(
1001 rootedPath("a"),
1002 ImmutableList.of(rootedPath("c"), rootedPath("d"), rootedPath("e")))
1003 .put(
1004 rootedPath("b"),
1005 ImmutableList.of(rootedPath("c"), rootedPath("d"), rootedPath("e")))
1006 .put(
1007 rootedPath("d"),
1008 ImmutableList.<RootedPath>of(rootedPath("d"), rootedPath("e"), rootedPath("c")))
1009 .put(
1010 rootedPath("e"),
1011 ImmutableList.<RootedPath>of(rootedPath("e"), rootedPath("c"), rootedPath("d")))
1012 .put(
1013 rootedPath("a/some/descendant"),
1014 ImmutableList.of(rootedPath("c"), rootedPath("d"), rootedPath("e")))
1015 .put(
1016 rootedPath("b/some/descendant"),
1017 ImmutableList.of(rootedPath("c"), rootedPath("d"), rootedPath("e")))
1018 .put(
1019 rootedPath("d/some/descendant"),
1020 ImmutableList.<RootedPath>of(rootedPath("d"), rootedPath("e"), rootedPath("c")))
1021 .put(
1022 rootedPath("e/some/descendant"),
1023 ImmutableList.<RootedPath>of(rootedPath("e"), rootedPath("c"), rootedPath("d")))
1024 .build();
1025 Map<RootedPath, ImmutableList<RootedPath>> startToPathToCycleMap =
1026 ImmutableMap.<RootedPath, ImmutableList<RootedPath>>builder()
1027 .put(rootedPath("a"), ImmutableList.of(rootedPath("a"), rootedPath("b")))
1028 .put(rootedPath("b"), ImmutableList.of(rootedPath("b")))
1029 .put(rootedPath("d"), ImmutableList.<RootedPath>of())
1030 .put(rootedPath("e"), ImmutableList.<RootedPath>of())
1031 .put(
1032 rootedPath("a/some/descendant"), ImmutableList.of(rootedPath("a"), rootedPath("b")))
1033 .put(rootedPath("b/some/descendant"), ImmutableList.of(rootedPath("b")))
1034 .put(rootedPath("d/some/descendant"), ImmutableList.<RootedPath>of())
1035 .put(rootedPath("e/some/descendant"), ImmutableList.<RootedPath>of())
1036 .build();
1037 ImmutableList<SkyKey> keys;
1038 if (ancestorCycle && startInCycle) {
1039 keys = ImmutableList.of(skyKey("d/some/descendant"), skyKey("e/some/descendant"));
1040 } else if (ancestorCycle && !startInCycle) {
1041 keys = ImmutableList.of(skyKey("a/some/descendant"), skyKey("b/some/descendant"));
1042 } else if (!ancestorCycle && startInCycle) {
1043 keys = ImmutableList.of(skyKey("d"), skyKey("e"));
1044 } else {
1045 keys = ImmutableList.of(skyKey("a"), skyKey("b"));
1046 }
1047 StoredEventHandler eventHandler = new StoredEventHandler();
1048 SequentialBuildDriver driver = makeDriver();
1049 EvaluationResult<FileValue> result =
1050 driver.evaluate(keys, /*keepGoing=*/ true, DEFAULT_THREAD_COUNT, eventHandler);
1051 assertTrue(result.hasError());
1052 for (SkyKey key : keys) {
1053 ErrorInfo errorInfo = result.getError(key);
1054 // FileFunction detects symlink cycles explicitly.
1055 assertThat(errorInfo.getCycleInfo()).isEmpty();
1056 FileSymlinkCycleException fsce = (FileSymlinkCycleException) errorInfo.getException();
1057 RootedPath start = (RootedPath) key.argument();
1058 assertThat(fsce.getPathToCycle())
1059 .containsExactlyElementsIn(startToPathToCycleMap.get(start))
1060 .inOrder();
1061 assertThat(fsce.getCycle()).containsExactlyElementsIn(startToCycleMap.get(start)).inOrder();
1062 }
1063 // Check that the unique cycle was reported exactly once.
1064 assertThat(eventHandler.getEvents()).hasSize(1);
1065 assertThat(Iterables.getOnlyElement(eventHandler.getEvents()).getMessage())
1066 .contains("circular symlinks detected");
1067 }
1068
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001069 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001070 public void testSymlinkCycle_AncestorCycle_StartInCycle() throws Exception {
1071 runTestSymlinkCycle(/*ancestorCycle=*/ true, /*startInCycle=*/ true);
1072 }
1073
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001074 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001075 public void testSymlinkCycle_AncestorCycle_StartOutOfCycle() throws Exception {
1076 runTestSymlinkCycle(/*ancestorCycle=*/ true, /*startInCycle=*/ false);
1077 }
1078
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001079 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001080 public void testSymlinkCycle_RegularCycle_StartInCycle() throws Exception {
1081 runTestSymlinkCycle(/*ancestorCycle=*/ false, /*startInCycle=*/ true);
1082 }
1083
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001084 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001085 public void testSymlinkCycle_RegularCycle_StartOutOfCycle() throws Exception {
1086 runTestSymlinkCycle(/*ancestorCycle=*/ false, /*startInCycle=*/ false);
1087 }
1088
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001089 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001090 public void testSerialization() throws Exception {
1091 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1092 ObjectOutputStream oos = new ObjectOutputStream(bos);
1093
1094 FileSystem oldFileSystem = Path.getFileSystemForSerialization();
1095 try {
Yun Peng22eb3322016-06-21 14:38:12 +00001096 // InMemoryFS is not supported for serialization.
1097 FileSystem fs = FileSystems.getJavaIoFileSystem();
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001098 Path.setFileSystemForSerialization(fs);
1099 pkgRoot = fs.getRootDirectory();
1100
1101 FileValue a = valueForPath(fs.getPath("/"));
1102
1103 Path tmp = fs.getPath(TestUtils.tmpDirFile().getAbsoluteFile() + "/file.txt");
1104
1105 FileSystemUtils.writeContentAsLatin1(tmp, "test contents");
1106
1107 FileValue b = valueForPath(tmp);
1108 Preconditions.checkState(b.isFile());
1109 FileValue c = valueForPath(fs.getPath("/does/not/exist"));
1110 oos.writeObject(a);
1111 oos.writeObject(b);
1112 oos.writeObject(c);
1113
1114 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
1115 ObjectInputStream ois = new ObjectInputStream(bis);
1116
1117 FileValue a2 = (FileValue) ois.readObject();
1118 FileValue b2 = (FileValue) ois.readObject();
1119 FileValue c2 = (FileValue) ois.readObject();
1120
1121 assertEquals(a, a2);
1122 assertEquals(b, b2);
1123 assertEquals(c, c2);
1124 assertFalse(a2.equals(b2));
1125 } finally {
1126 Path.setFileSystemForSerialization(oldFileSystem);
1127 }
1128 }
1129
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001130 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001131 public void testFileStateEquality() throws Exception {
1132 file("a");
1133 symlink("b1", "a");
1134 symlink("b2", "a");
1135 symlink("b3", "zzz");
1136 directory("d1");
1137 directory("d2");
1138 SkyKey file = fileStateSkyKey("a");
1139 SkyKey symlink1 = fileStateSkyKey("b1");
1140 SkyKey symlink2 = fileStateSkyKey("b2");
1141 SkyKey symlink3 = fileStateSkyKey("b3");
1142 SkyKey missing1 = fileStateSkyKey("c1");
1143 SkyKey missing2 = fileStateSkyKey("c2");
1144 SkyKey directory1 = fileStateSkyKey("d1");
1145 SkyKey directory2 = fileStateSkyKey("d2");
1146 ImmutableList<SkyKey> keys =
1147 ImmutableList.of(
1148 file, symlink1, symlink2, symlink3, missing1, missing2, directory1, directory2);
1149
1150 SequentialBuildDriver driver = makeDriver();
1151 EvaluationResult<SkyValue> result =
1152 driver.evaluate(keys, false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
1153
1154 new EqualsTester()
1155 .addEqualityGroup(result.get(file))
1156 .addEqualityGroup(result.get(symlink1), result.get(symlink2))
1157 .addEqualityGroup(result.get(symlink3))
1158 .addEqualityGroup(result.get(missing1), result.get(missing2))
1159 .addEqualityGroup(result.get(directory1), result.get(directory2))
1160 .testEquals();
1161 }
1162
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001163 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001164 public void testSymlinkToPackagePathBoundary() throws Exception {
1165 Path path = path("this/is/a/path");
1166 FileSystemUtils.ensureSymbolicLink(path, pkgRoot);
1167 assertError("this/is/a/path");
1168 }
1169
1170 private void runTestInfiniteSymlinkExpansion(boolean symlinkToAncestor, boolean absoluteSymlink)
1171 throws Exception {
1172 Path otherPath = path("other");
1173 RootedPath otherRootedPath = RootedPath.toRootedPath(pkgRoot, otherPath.relativeTo(pkgRoot));
1174 Path ancestorPath = path("a");
1175 RootedPath ancestorRootedPath =
1176 RootedPath.toRootedPath(pkgRoot, ancestorPath.relativeTo(pkgRoot));
1177 FileSystemUtils.ensureSymbolicLink(otherPath, ancestorPath);
1178 Path intermediatePath = path("inter");
1179 RootedPath intermediateRootedPath =
1180 RootedPath.toRootedPath(pkgRoot, intermediatePath.relativeTo(pkgRoot));
1181 Path descendantPath = path("a/b/c/d/e");
1182 RootedPath descendantRootedPath =
1183 RootedPath.toRootedPath(pkgRoot, descendantPath.relativeTo(pkgRoot));
1184 if (symlinkToAncestor) {
1185 FileSystemUtils.ensureSymbolicLink(descendantPath, intermediatePath);
1186 if (absoluteSymlink) {
1187 FileSystemUtils.ensureSymbolicLink(intermediatePath, ancestorPath);
1188 } else {
1189 FileSystemUtils.ensureSymbolicLink(intermediatePath, ancestorRootedPath.getRelativePath());
1190 }
1191 } else {
1192 FileSystemUtils.ensureSymbolicLink(ancestorPath, intermediatePath);
1193 if (absoluteSymlink) {
1194 FileSystemUtils.ensureSymbolicLink(intermediatePath, descendantPath);
1195 } else {
1196 FileSystemUtils.ensureSymbolicLink(
1197 intermediatePath, descendantRootedPath.getRelativePath());
1198 }
1199 }
1200 StoredEventHandler eventHandler = new StoredEventHandler();
1201 SequentialBuildDriver driver = makeDriver();
1202 SkyKey ancestorPathKey = FileValue.key(ancestorRootedPath);
1203 SkyKey descendantPathKey = FileValue.key(descendantRootedPath);
1204 SkyKey otherPathKey = FileValue.key(otherRootedPath);
1205 ImmutableList<SkyKey> keys;
1206 ImmutableList<SkyKey> errorKeys;
1207 ImmutableList<RootedPath> expectedChain;
1208 if (symlinkToAncestor) {
1209 keys = ImmutableList.of(descendantPathKey, otherPathKey);
1210 errorKeys = ImmutableList.of(descendantPathKey);
1211 expectedChain =
1212 ImmutableList.of(descendantRootedPath, intermediateRootedPath, ancestorRootedPath);
1213 } else {
1214 keys = ImmutableList.of(ancestorPathKey, otherPathKey);
1215 errorKeys = keys;
1216 expectedChain =
1217 ImmutableList.of(ancestorRootedPath, intermediateRootedPath, descendantRootedPath);
1218 }
1219 EvaluationResult<FileValue> result =
1220 driver.evaluate(keys, /*keepGoing=*/ true, DEFAULT_THREAD_COUNT, eventHandler);
1221 assertTrue(result.hasError());
1222 for (SkyKey key : errorKeys) {
1223 ErrorInfo errorInfo = result.getError(key);
1224 // FileFunction detects infinite symlink expansion explicitly.
1225 assertThat(errorInfo.getCycleInfo()).isEmpty();
1226 FileSymlinkInfiniteExpansionException fsiee =
1227 (FileSymlinkInfiniteExpansionException) errorInfo.getException();
1228 assertThat(fsiee.getMessage()).contains("Infinite symlink expansion");
1229 assertThat(fsiee.getChain()).containsExactlyElementsIn(expectedChain).inOrder();
1230 }
1231 // Check that the unique symlink expansion error was reported exactly once.
1232 assertThat(eventHandler.getEvents()).hasSize(1);
1233 assertThat(Iterables.getOnlyElement(eventHandler.getEvents()).getMessage())
1234 .contains("infinite symlink expansion detected");
1235 }
1236
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001237 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001238 public void testInfiniteSymlinkExpansion_AbsoluteSymlinkToDescendant() throws Exception {
1239 runTestInfiniteSymlinkExpansion(/*ancestor=*/ false, /*absoluteSymlink=*/ true);
1240 }
1241
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001242 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001243 public void testInfiniteSymlinkExpansion_RelativeSymlinkToDescendant() throws Exception {
1244 runTestInfiniteSymlinkExpansion(/*ancestor=*/ false, /*absoluteSymlink=*/ false);
1245 }
1246
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001247 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001248 public void testInfiniteSymlinkExpansion_AbsoluteSymlinkToAncestor() throws Exception {
1249 runTestInfiniteSymlinkExpansion(/*ancestor=*/ true, /*absoluteSymlink=*/ true);
1250 }
1251
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001252 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001253 public void testInfiniteSymlinkExpansion_RelativeSymlinkToAncestor() throws Exception {
1254 runTestInfiniteSymlinkExpansion(/*ancestor=*/ true, /*absoluteSymlink=*/ false);
1255 }
1256
Han-Wen Nienhuys3b2eae32015-10-28 16:35:08 +00001257 @Test
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001258 public void testChildOfNonexistentParent() throws Exception {
1259 Path ancestor = directory("this/is/an/ancestor");
1260 Path parent = ancestor.getChild("parent");
1261 Path child = parent.getChild("child");
1262 assertFalse(valueForPath(parent).exists());
1263 assertFalse(valueForPath(child).exists());
1264 }
1265
1266 private void checkRealPath(String pathString) throws Exception {
1267 Path realPath = pkgRoot.getRelative(pathString).resolveSymbolicLinks();
1268 assertRealPath(pathString, realPath.relativeTo(pkgRoot).toString());
1269 }
1270
1271 private void assertRealPath(String pathString, String expectedRealPathString) throws Exception {
1272 SequentialBuildDriver driver = makeDriver();
1273 SkyKey key = skyKey(pathString);
1274 EvaluationResult<SkyValue> result =
1275 driver.evaluate(
1276 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
1277 if (result.hasError()) {
1278 fail(String.format("Evaluation error for %s: %s", key, result.getError()));
1279 }
1280 FileValue fileValue = (FileValue) result.get(key);
1281 assertEquals(
1282 pkgRoot.getRelative(expectedRealPathString).toString(),
1283 fileValue.realRootedPath().asPath().toString());
1284 }
1285
1286 /**
Yun Peng22eb3322016-06-21 14:38:12 +00001287 * Returns a callback that, when executed, deletes the given path. Not meant to be called directly
1288 * by tests.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001289 */
1290 private Runnable makeDeletePathCallback(final Path toDelete) {
1291 return new Runnable() {
1292 @Override
1293 public void run() {
1294 try {
1295 toDelete.delete();
1296 } catch (IOException e) {
1297 e.printStackTrace();
1298 fail(e.getMessage());
1299 }
1300 }
1301 };
1302 }
1303
1304 /**
Yun Peng22eb3322016-06-21 14:38:12 +00001305 * Returns a callback that, when executed, writes the given bytes to the given file path. Not
1306 * meant to be called directly by tests.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001307 */
1308 private Runnable makeWriteFileContentCallback(final Path toChange, final byte[] contents) {
1309 return new Runnable() {
1310 @Override
1311 public void run() {
1312 OutputStream outputStream;
1313 try {
1314 outputStream = toChange.getOutputStream();
1315 outputStream.write(contents);
1316 outputStream.close();
1317 } catch (IOException e) {
1318 e.printStackTrace();
1319 fail(e.getMessage());
1320 }
1321 }
1322 };
1323 }
1324
1325 /**
Yun Peng22eb3322016-06-21 14:38:12 +00001326 * Returns a callback that, when executed, creates the given directory path. Not meant to be
1327 * called directly by tests.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001328 */
1329 private Runnable makeCreateDirectoryCallback(final Path toCreate) {
1330 return new Runnable() {
1331 @Override
1332 public void run() {
1333 try {
1334 toCreate.createDirectory();
1335 } catch (IOException e) {
1336 e.printStackTrace();
1337 fail(e.getMessage());
1338 }
1339 }
1340 };
1341 }
1342
1343 /**
Yun Peng22eb3322016-06-21 14:38:12 +00001344 * Returns a callback that, when executed, makes {@code toLink} a symlink to {@code toTarget}. Not
1345 * meant to be called directly by tests.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001346 */
1347 private Runnable makeSymlinkCallback(final Path toLink, final PathFragment toTarget) {
1348 return new Runnable() {
1349 @Override
1350 public void run() {
1351 try {
1352 FileSystemUtils.ensureSymbolicLink(toLink, toTarget);
1353 } catch (IOException e) {
1354 e.printStackTrace();
1355 fail(e.getMessage());
1356 }
1357 }
1358 };
1359 }
1360
Yun Peng22eb3322016-06-21 14:38:12 +00001361 /** Returns the files that would be changed/created if {@code path} were to be changed/created. */
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001362 private ImmutableList<String> filesTouchedIfTouched(Path path) {
1363 List<String> filesToBeTouched = Lists.newArrayList();
1364 do {
1365 filesToBeTouched.add(path.getPathString());
1366 path = path.getParentDirectory();
1367 } while (!path.exists());
1368 return ImmutableList.copyOf(filesToBeTouched);
1369 }
1370
1371 /**
1372 * Changes the contents of the FileValue for the given file in some way e.g.
Yun Peng22eb3322016-06-21 14:38:12 +00001373 *
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001374 * <ul>
Yun Peng22eb3322016-06-21 14:38:12 +00001375 * <li> If it's a regular file, the contents will be changed.
1376 * <li> If it's a non-existent file, it will be created.
1377 * <ul>
1378 * and then returns the file(s) changed paired with a callback to undo the change. Not meant
1379 * to be called directly by tests.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001380 */
1381 private Pair<ImmutableList<String>, Runnable> changeFile(String fileStringToChange)
1382 throws Exception {
1383 Path fileToChange = path(fileStringToChange);
1384 if (fileToChange.exists()) {
1385 final byte[] oldContents = FileSystemUtils.readContent(fileToChange);
1386 OutputStream outputStream = fileToChange.getOutputStream(/*append=*/ true);
1387 outputStream.write(new byte[] {(byte) 42}, 0, 1);
1388 outputStream.close();
1389 return Pair.of(
1390 ImmutableList.of(fileStringToChange),
1391 makeWriteFileContentCallback(fileToChange, oldContents));
1392 } else {
1393 ImmutableList<String> filesTouched = filesTouchedIfTouched(fileToChange);
1394 file(fileStringToChange, "new stuff");
1395 return Pair.of(ImmutableList.copyOf(filesTouched), makeDeletePathCallback(fileToChange));
1396 }
1397 }
1398
1399 /**
1400 * Changes the contents of the FileValue for the given directory in some way e.g.
Yun Peng22eb3322016-06-21 14:38:12 +00001401 *
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001402 * <ul>
Yun Peng22eb3322016-06-21 14:38:12 +00001403 * <li> If it exists, the directory will be deleted.
1404 * <li> If it doesn't exist, the directory will be created.
1405 * <ul>
1406 * and then returns the file(s) changed paired with a callback to undo the change. Not meant
1407 * to be called directly by tests.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001408 */
1409 private Pair<ImmutableList<String>, Runnable> changeDirectory(String directoryStringToChange)
1410 throws Exception {
1411 final Path directoryToChange = path(directoryStringToChange);
1412 if (directoryToChange.exists()) {
1413 directoryToChange.delete();
1414 return Pair.of(
1415 ImmutableList.of(directoryStringToChange),
1416 makeCreateDirectoryCallback(directoryToChange));
1417 } else {
1418 directoryToChange.createDirectory();
1419 return Pair.of(
1420 ImmutableList.of(directoryStringToChange), makeDeletePathCallback(directoryToChange));
1421 }
1422 }
1423
1424 /**
Yun Peng22eb3322016-06-21 14:38:12 +00001425 * Performs filesystem operations to change the file or directory denoted by {@code
1426 * changedPathString} and returns the file(s) changed paired with a callback to undo the change.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001427 * Not meant to be called directly by tests.
1428 *
Yun Peng22eb3322016-06-21 14:38:12 +00001429 * @param isSupposedToBeFile whether the path denoted by the given string is supposed to be a file
1430 * or a directory. This is needed is the path doesn't exist yet, and so the filesystem doesn't
1431 * know.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001432 */
1433 private Pair<ImmutableList<String>, Runnable> change(
1434 String changedPathString, boolean isSupposedToBeFile) throws Exception {
1435 final Path changedPath = path(changedPathString);
1436 if (changedPath.isSymbolicLink()) {
1437 ImmutableList<String> filesTouched = filesTouchedIfTouched(changedPath);
1438 PathFragment oldTarget = changedPath.readSymbolicLink();
1439 FileSystemUtils.ensureSymbolicLink(changedPath, oldTarget.getChild("__different_target__"));
1440 return Pair.of(filesTouched, makeSymlinkCallback(changedPath, oldTarget));
1441 } else if (isSupposedToBeFile) {
1442 return changeFile(changedPathString);
1443 } else {
1444 return changeDirectory(changedPathString);
1445 }
1446 }
1447
1448 /**
1449 * Asserts that if the contents of {@code changedPathString} changes, then the FileValue
1450 * corresponding to {@code pathString} will change. Not meant to be called directly by tests.
1451 */
1452 private void assertValueChangesIfContentsOfFileChanges(
1453 String changedPathString, boolean changes, String pathString) throws Exception {
1454 getFilesSeenAndAssertValueChangesIfContentsOfFileChanges(
1455 changedPathString, changes, pathString);
1456 }
1457
1458 /**
1459 * Asserts that if the contents of {@code changedPathString} changes, then the FileValue
1460 * corresponding to {@code pathString} will change. Returns the paths of all files seen.
1461 */
1462 private Set<RootedPath> getFilesSeenAndAssertValueChangesIfContentsOfFileChanges(
1463 String changedPathString, boolean changes, String pathString) throws Exception {
1464 return assertChangesIfChanges(changedPathString, true, changes, pathString);
1465 }
1466
1467 /**
1468 * Asserts that if the directory {@code changedPathString} changes, then the FileValue
1469 * corresponding to {@code pathString} will change. Returns the paths of all files seen.
1470 */
1471 private Set<RootedPath> assertValueChangesIfContentsOfDirectoryChanges(
1472 String changedPathString, boolean changes, String pathString) throws Exception {
1473 return assertChangesIfChanges(changedPathString, false, changes, pathString);
1474 }
1475
1476 /**
1477 * Asserts that if the contents of {@code changedPathString} changes, then the FileValue
Yun Peng22eb3322016-06-21 14:38:12 +00001478 * corresponding to {@code pathString} will change. Returns the paths of all files seen. Not meant
1479 * to be called directly by tests.
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001480 */
1481 private Set<RootedPath> assertChangesIfChanges(
1482 String changedPathString, boolean isFile, boolean changes, String pathString)
1483 throws Exception {
1484 SequentialBuildDriver driver = makeDriver();
1485 SkyKey key = skyKey(pathString);
1486 EvaluationResult<SkyValue> result;
1487 result =
1488 driver.evaluate(
1489 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
1490 if (result.hasError()) {
1491 fail(String.format("Evaluation error for %s: %s", key, result.getError()));
1492 }
1493 SkyValue oldValue = result.get(key);
1494
1495 Pair<ImmutableList<String>, Runnable> changeResult = change(changedPathString, isFile);
1496 ImmutableList<String> changedPathStrings = changeResult.first;
1497 Runnable undoCallback = changeResult.second;
1498 differencer.invalidate(
1499 Iterables.transform(
1500 changedPathStrings,
1501 new Function<String, SkyKey>() {
1502 @Override
1503 public SkyKey apply(String input) {
1504 return fileStateSkyKey(input);
1505 }
1506 }));
1507
1508 result =
1509 driver.evaluate(
1510 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
1511 if (result.hasError()) {
1512 fail(String.format("Evaluation error for %s: %s", key, result.getError()));
1513 }
1514
1515 SkyValue newValue = result.get(key);
1516 assertTrue(
1517 String.format(
1518 "Changing the contents of %s %s should%s change the value for file %s.",
Yun Peng22eb3322016-06-21 14:38:12 +00001519 isFile ? "file" : "directory", changedPathString, changes ? "" : " not", pathString),
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001520 changes != newValue.equals(oldValue));
1521
1522 // Restore the original file.
1523 undoCallback.run();
1524 return filesSeen(driver.getGraphForTesting());
1525 }
1526
1527 /**
1528 * Asserts that trying to construct a FileValue for {@code path} succeeds. Returns the paths of
1529 * all files seen.
1530 */
1531 private Set<RootedPath> assertNoError(String pathString) throws Exception {
1532 SequentialBuildDriver driver = makeDriver();
1533 SkyKey key = skyKey(pathString);
1534 EvaluationResult<FileValue> result;
1535 result =
1536 driver.evaluate(
1537 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
1538 assertFalse(
1539 "Did not expect error while evaluating " + pathString + ", got " + result.get(key),
1540 result.hasError());
1541 return filesSeen(driver.getGraphForTesting());
1542 }
1543
1544 /**
1545 * Asserts that trying to construct a FileValue for {@code path} fails. Returns the paths of all
1546 * files seen.
1547 */
1548 private Set<RootedPath> assertError(String pathString) throws Exception {
1549 SequentialBuildDriver driver = makeDriver();
1550 SkyKey key = skyKey(pathString);
1551 EvaluationResult<FileValue> result;
1552 result =
1553 driver.evaluate(
1554 ImmutableList.of(key), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
1555 assertTrue(
1556 "Expected error while evaluating " + pathString + ", got " + result.get(key),
1557 result.hasError());
1558 assertTrue(
1559 !Iterables.isEmpty(result.getError().getCycleInfo())
1560 || result.getError().getException() != null);
1561 return filesSeen(driver.getGraphForTesting());
1562 }
1563
1564 private Path file(String fileName) throws Exception {
1565 Path path = path(fileName);
1566 FileSystemUtils.createDirectoryAndParents(path.getParentDirectory());
1567 FileSystemUtils.createEmptyFile(path);
1568 return path;
1569 }
1570
1571 private Path file(String fileName, String contents) throws Exception {
1572 Path path = path(fileName);
1573 FileSystemUtils.createDirectoryAndParents(path.getParentDirectory());
1574 FileSystemUtils.writeContentAsLatin1(path, contents);
1575 return path;
1576 }
1577
1578 private Path directory(String directoryName) throws Exception {
1579 Path path = path(directoryName);
1580 FileSystemUtils.createDirectoryAndParents(path);
1581 return path;
1582 }
1583
1584 private Path symlink(String link, String target) throws Exception {
1585 Path path = path(link);
1586 FileSystemUtils.createDirectoryAndParents(path.getParentDirectory());
1587 path.createSymbolicLink(new PathFragment(target));
1588 return path;
1589 }
1590
1591 private Path path(String rootRelativePath) {
1592 return pkgRoot.getRelative(new PathFragment(rootRelativePath));
1593 }
1594
1595 private RootedPath rootedPath(String pathString) {
1596 Path path = path(pathString);
1597 for (Path root : pkgLocator.getPathEntries()) {
1598 if (path.startsWith(root)) {
1599 return RootedPath.toRootedPath(root, path);
1600 }
1601 }
1602 return RootedPath.toRootedPath(fs.getRootDirectory(), path);
1603 }
1604
1605 private SkyKey skyKey(String pathString) {
1606 return FileValue.key(rootedPath(pathString));
1607 }
1608
1609 private SkyKey fileStateSkyKey(String pathString) {
1610 return FileStateValue.key(rootedPath(pathString));
1611 }
1612
1613 private class CustomInMemoryFs extends InMemoryFileSystem {
1614
Nathan Harmatac0f3d4e2016-10-17 15:49:34 +00001615 private final Map<Path, FileStatus> stubbedStats = Maps.newHashMap();
1616 private final Map<Path, IOException> stubbedStatErrors = Maps.newHashMap();
1617 private final Map<Path, IOException> stubbedFastDigestErrors = Maps.newHashMap();
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001618
1619 public CustomInMemoryFs(ManualClock manualClock) {
1620 super(manualClock);
1621 }
1622
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001623 public void stubFastDigestError(Path path, IOException error) {
1624 stubbedFastDigestErrors.put(path, error);
1625 }
1626
1627 @Override
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001628 protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001629 if (stubbedFastDigestErrors.containsKey(path)) {
1630 throw stubbedFastDigestErrors.get(path);
1631 }
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001632 return fastDigest ? getDigest(path) : null;
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001633 }
1634
1635 public void stubStat(Path path, @Nullable FileStatus stubbedResult) {
1636 stubbedStats.put(path, stubbedResult);
1637 }
1638
Nathan Harmatac0f3d4e2016-10-17 15:49:34 +00001639 public void stubStatError(Path path, IOException error) {
1640 stubbedStatErrors.put(path, error);
1641 }
1642
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001643 @Override
1644 public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
Nathan Harmatac0f3d4e2016-10-17 15:49:34 +00001645 if (stubbedStatErrors.containsKey(path)) {
1646 throw stubbedStatErrors.get(path);
1647 }
Han-Wen Nienhuyseb71ecc2015-10-28 15:48:36 +00001648 if (stubbedStats.containsKey(path)) {
1649 return stubbedStats.get(path);
1650 }
1651 return super.stat(path, followSymlinks);
1652 }
1653 }
1654}