| // Copyright 2014 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| package com.google.devtools.build.lib.vfs.util; |
| |
| import com.google.devtools.build.lib.clock.BlazeClock; |
| import com.google.devtools.build.lib.testutil.TestUtils; |
| import com.google.devtools.build.lib.util.StringUtilities; |
| import com.google.devtools.build.lib.vfs.FileSystem; |
| import com.google.devtools.build.lib.vfs.FileSystemUtils; |
| import com.google.devtools.build.lib.vfs.Path; |
| import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| |
| /** |
| * Base class for a testing apparatus for a scratch filesystem. |
| */ |
| public class FsApparatus { |
| |
| /* ---------- State that the apparatus initializes / operates on --------- */ |
| protected FileSystem fileSystem = null; |
| protected Path workingDir = null; |
| |
| public static FsApparatus newInMemory() { |
| return new FsApparatus(); |
| } |
| |
| // TestUtil.getTmpDir is slow, so cache the result here |
| private static final String TMP_DIR = |
| new File(TestUtils.tmpDir(), "bs").toString(); |
| |
| |
| /** |
| * When using a Native file system, absolute paths will be treated as absolute paths on the unix |
| * file system, as opposed to paths relative to the backing temp directory. So for simplicity, |
| * you ought to only use relative paths for FsApparatus#file, FsApparatus#dir, and |
| * FsApparatus#path. Otherwise, be aware of the following issue |
| * |
| * Path p1 = scratch.path(...); |
| * Path p2 = scratch.path(p1.getPathString()); |
| * |
| * We'd like the invariant that p1.equals(p2) regardless if scratch is in-memory or not, but this |
| * does not hold with our usage of Unix filesystems. |
| */ |
| public static FsApparatus newNative() { |
| FileSystem fs = FileSystems.getNativeFileSystem(); |
| Path wd = fs.getPath(TMP_DIR); |
| |
| try { |
| wd.deleteTree(); |
| } catch (IOException e) { |
| throw new AssertionError(e.getMessage()); |
| } |
| |
| return new FsApparatus(fs, wd); |
| } |
| |
| private FsApparatus() { |
| fileSystem = new InMemoryFileSystem(BlazeClock.instance()); |
| workingDir = fileSystem.getPath("/"); |
| } |
| |
| public FsApparatus(FileSystem fs, Path cwd) { |
| fileSystem = fs; |
| workingDir = cwd; |
| } |
| |
| public FsApparatus(FileSystem fs) { |
| fileSystem = fs; |
| workingDir = fs.getPath("/"); |
| } |
| |
| public FileSystem fs() { |
| return fileSystem; |
| } |
| |
| /** |
| * Creates a scratch file in the scratch filesystem with the given {@code pathName} with |
| * {@code lines} being its content. The method returns a Path instance for the scratch file. |
| */ |
| public Path file(String pathName, String... lines) throws IOException { |
| Path file = path(pathName); |
| Path parentDir = file.getParentDirectory(); |
| if (!parentDir.exists()) { |
| FileSystemUtils.createDirectoryAndParents(parentDir); |
| } |
| if (file.exists()) { |
| throw new IOException("Could not create scratch file (file exists) " |
| + file); |
| } |
| String fileContent = StringUtilities.joinLines(lines); |
| FileSystemUtils.writeContentAsLatin1(file, fileContent); |
| return file; |
| } |
| |
| /** |
| * Creates or recreates a scratch file just like {@link #file} but tolerating an existing file. |
| */ |
| public Path overwriteFile(String pathName, String... lines) throws IOException { |
| try { |
| path(pathName).delete(); |
| } catch (FileNotFoundException e) { |
| // Ignored. |
| } |
| return file(pathName, lines); |
| } |
| |
| /** |
| * Initializes this apparatus (if it hasn't been initialized yet), and creates |
| * a directory in the scratch filesystem, with the given {@code pathName}. |
| * Creates parent directories as necessary. |
| */ |
| public Path dir(String pathName) throws IOException { |
| Path dir = path(pathName); |
| if (!dir.exists()) { |
| FileSystemUtils.createDirectoryAndParents(dir); |
| } |
| if (!dir.isDirectory()) { |
| throw new IOException("Exists, but is not a directory: " + dir); |
| } |
| return dir; |
| } |
| |
| /** |
| * Resolves {@code pathName} relative to the working directory. Note that this will not create any |
| * entity in the filesystem; i.e., the file that the object is describing may not exist in the |
| * filesystem. |
| */ |
| public Path path(String pathName) { |
| return workingDir.getRelative(pathName); |
| } |
| |
| /** |
| * Create a fresh directory in the system temporary directory, instead of the |
| * testing directory provided by the testing framework. This path is usually |
| * shorter than a path starting with TestUtil.getTmpDir(). We care about the |
| * length because of the path length restriction for Unix local socket files. |
| * |
| * Clients are responsible for deleting the directory after tests. |
| */ |
| public Path createUnixTempDir() throws IOException { |
| if (fileSystem instanceof InMemoryFileSystem) { |
| throw new IOException("Can not create Unix temporary directories in " |
| + "an in-memory file system"); |
| } |
| File file = File.createTempFile("scratch", "tmp"); |
| final Path path = fileSystem.getPath(file.getAbsolutePath()); |
| path.delete(); |
| path.createDirectory(); |
| return path; |
| } |
| } |