blob: cdcb22e4cff721e548935e4859ff71069c6d8b27 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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.vfs;
15
lberkiaea56b32017-05-30 12:35:33 +020016import static com.google.common.truth.Truth.assertThat;
Chris Parsonscb5aa002016-07-26 17:52:37 +000017
tomlu18291e52017-12-19 11:54:08 -080018import com.google.common.collect.Iterables;
19import com.google.common.util.concurrent.Futures;
20import com.google.common.util.concurrent.ListenableFuture;
21import com.google.common.util.concurrent.ListeningExecutorService;
22import com.google.common.util.concurrent.MoreExecutors;
Chris Parsonscb5aa002016-07-26 17:52:37 +000023import com.google.devtools.build.lib.testutil.ManualClock;
tomlu18291e52017-12-19 11:54:08 -080024import com.google.devtools.build.lib.testutil.TestUtils;
Googlere1cd9502016-09-07 14:33:29 +000025import java.io.IOException;
26import java.nio.file.Files;
27import java.nio.file.LinkOption;
tomlu18291e52017-12-19 11:54:08 -080028import java.nio.file.Paths;
Googlere1cd9502016-09-07 14:33:29 +000029import java.nio.file.attribute.BasicFileAttributes;
tomlu18291e52017-12-19 11:54:08 -080030import java.util.ArrayList;
31import java.util.List;
32import java.util.Objects;
33import java.util.Optional;
34import java.util.concurrent.Executors;
35import java.util.concurrent.TimeUnit;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010036import org.junit.Test;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037
38/**
ccalvarin94c171c2018-08-16 15:42:19 -070039 * Tests for the {@link JavaIoFileSystem}. That file system by itself is not capable of creating
40 * symlinks; use the unix one to create them, so that the test can check that the file system
41 * handles their existence correctly.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043public class JavaIoFileSystemTest extends SymlinkAwareFileSystemTest {
44
Chris Parsonscb5aa002016-07-26 17:52:37 +000045 private ManualClock clock;
46
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010047 @Override
ccalvarin94c171c2018-08-16 15:42:19 -070048 public FileSystem getFreshFileSystem(DigestHashFunction digestHashFunction) {
Chris Parsonscb5aa002016-07-26 17:52:37 +000049 clock = new ManualClock();
ccalvarin94c171c2018-08-16 15:42:19 -070050 return new JavaIoFileSystem(clock, digestHashFunction);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051 }
52
Chris Parsonscb5aa002016-07-26 17:52:37 +000053 // Tests are inherited from the FileSystemTest
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054
55 // JavaIoFileSystem incorrectly throws a FileNotFoundException for all IO errors. This means that
56 // statIfFound incorrectly suppresses those errors.
57 @Override
58 @Test
59 public void testBadPermissionsThrowsExceptionOnStatIfFound() {}
Chris Parsonscb5aa002016-07-26 17:52:37 +000060
61 @Test
62 public void testSetLastModifiedTime() throws Exception {
63 Path file = xEmptyDirectory.getChild("new-file");
64 FileSystemUtils.createEmptyFile(file);
65
66 file.setLastModifiedTime(1000L);
lberkiaea56b32017-05-30 12:35:33 +020067 assertThat(file.getLastModifiedTime()).isEqualTo(1000L);
Chris Parsonscb5aa002016-07-26 17:52:37 +000068 file.setLastModifiedTime(0L);
lberkiaea56b32017-05-30 12:35:33 +020069 assertThat(file.getLastModifiedTime()).isEqualTo(0L);
Chris Parsonscb5aa002016-07-26 17:52:37 +000070
71 clock.advanceMillis(42000L);
72 file.setLastModifiedTime(-1L);
lberkiaea56b32017-05-30 12:35:33 +020073 assertThat(file.getLastModifiedTime()).isEqualTo(42000L);
Chris Parsonscb5aa002016-07-26 17:52:37 +000074 }
Googlere1cd9502016-09-07 14:33:29 +000075
76 @Override
77 protected boolean isHardLinked(Path a, Path b) throws IOException {
78 return Files.readAttributes(
tomlu18291e52017-12-19 11:54:08 -080079 Paths.get(a.toString()), BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)
Googlere1cd9502016-09-07 14:33:29 +000080 .fileKey()
81 .equals(
82 Files.readAttributes(
tomlu18291e52017-12-19 11:54:08 -080083 Paths.get(b.toString()), BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS)
Googlere1cd9502016-09-07 14:33:29 +000084 .fileKey());
85 }
tomlu18291e52017-12-19 11:54:08 -080086
87 /**
88 * This test has a large number of threads racing to create the same subdirectories.
89 *
90 * <p>We create N number of distinct directory trees, eg. the tree "0-0/0-1/0-2/0-3/0-4" followed
91 * by the tree "1-0/1-1/1-2/1-3/1-4" etc. If there is race we should quickly get a deadlock.
92 *
93 * <p>A timeout of this test is likely because of a deadlock.
94 */
95 @Test
96 public void testCreateDirectoriesThreadSafety() throws Exception {
97 int threadCount = 200;
98 int directoryCreationCount = 500; // We create this many sets of directories
99 int subDirectoryCount = 5; // Each directory tree is this deep
100 ListeningExecutorService executor =
101 MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(threadCount));
102 List<ListenableFuture<IOException>> futures = new ArrayList<>();
103 for (int threadIndex = 0; threadIndex < threadCount; ++threadIndex) {
104 futures.add(
105 executor.submit(
106 () -> {
107 try {
108 for (int loopi = 0; loopi < directoryCreationCount; ++loopi) {
109 List<Path> subDirs =
110 getSubDirectories(xEmptyDirectory, loopi, subDirectoryCount);
111 Path lastDir = Iterables.getLast(subDirs);
112 FileSystemUtils.createDirectoryAndParents(lastDir);
113 }
114 } catch (IOException e) {
115 return e;
116 }
117 return null;
118 }));
119 }
120 ListenableFuture<List<IOException>> all = Futures.allAsList(futures);
121 // If the test times out here then there's likely to be a deadlock
122 List<IOException> exceptions =
123 all.get(TestUtils.WAIT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
124 Optional<IOException> error = exceptions.stream().filter(Objects::nonNull).findFirst();
125 if (error.isPresent()) {
126 throw error.get();
127 }
128 }
129
130 private static List<Path> getSubDirectories(Path base, int loopi, int subDirectoryCount) {
131 Path path = base;
132 List<Path> subDirs = new ArrayList<>();
133 for (int subDirIndex = 0; subDirIndex < subDirectoryCount; ++subDirIndex) {
134 path = path.getChild(String.format("%d-%d", loopi, subDirIndex));
135 subDirs.add(path);
136 }
137 return subDirs;
138 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100139}