blob: 146fc864595fd089c61b68f466fe182df8b7a2e5 [file] [log] [blame]
// Copyright 2015 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.packages.util;
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.testutil.BlazeTestUtils;
import com.google.devtools.build.lib.testutil.TestConstants;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.util.FileSystems;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.annotation.Nullable;
/**
* Configuration for the mock client setup that we use for testing.
*/
public final class MockToolsConfig {
private final Path rootDirectory;
private final boolean realFileSystem;
// Allow the injection of the runfiles directory where actual tools are found.
// TestUtil.getRunfilesDir() caches the value of the "TEST_SRCDIR" system property, which makes
// it impossible to change if it doesn't get set early in test configuration setup.
private final Path runfilesDirectory;
public MockToolsConfig(Path rootDirectory) {
this(rootDirectory, false, null);
}
public MockToolsConfig(Path rootDirectory, boolean realFileSystem) {
this(rootDirectory, realFileSystem, null);
}
public MockToolsConfig(
Path rootDirectory, boolean realFileSystem, @Nullable Path runfilesDirectoryOpt) {
this.rootDirectory = rootDirectory;
this.realFileSystem = realFileSystem;
if (!realFileSystem) {
this.runfilesDirectory = null;
} else if (runfilesDirectoryOpt == null) {
// Turning the absolute path string from runfilesDir into a Path object.
this.runfilesDirectory = rootDirectory.getRelative(BlazeTestUtils.runfilesDir());
} else {
this.runfilesDirectory = runfilesDirectoryOpt;
}
}
public boolean isRealFileSystem() {
return realFileSystem;
}
public Path getPath(String relativePath) {
Preconditions.checkState(!relativePath.startsWith("/"), relativePath);
return rootDirectory.getRelative(relativePath);
}
public Path create(String relativePath, String... lines) throws IOException {
Path path = getPath(relativePath);
if (!path.exists()) {
FileSystemUtils.writeIsoLatin1(path, lines);
} else if (lines.length > 0) {
String existingContent = new String(FileSystemUtils.readContentAsLatin1(path));
StringBuilder newContent = new StringBuilder();
for (String line : lines) {
newContent.append(line);
newContent.append(System.lineSeparator());
}
if (!newContent.toString().trim().equals(existingContent.trim())) {
throw new IOException(
"Conflict: '"
+ relativePath
+ "':\n'"
+ newContent
+ "'\n vs \n'"
+ existingContent
+ "'");
}
}
return path;
}
public Path overwrite(String relativePath, String... lines) throws IOException {
Path path = getPath(relativePath);
if (path.exists()) {
path.deleteTree();
}
return create(relativePath, lines);
}
public Path append(String relativePath, String... lines) throws IOException {
Path path = getPath(relativePath);
if (!path.exists()) {
return create(relativePath, lines);
}
FileSystemUtils.appendIsoLatin1(path, lines);
return path;
}
/**
* Links a tool into the workspace by creating a symbolic link to a real file. The target location
* in the workspace uses the same relative path as the given path to the tool in the runfiles
* tree. Use this if you do not need to rename or relocate the file, i.e., if the location in the
* workspace and the runfiles tree matches. Otherwise use {@link #linkTool(String, String)}.
*
* @param relativePath the relative path within the runfiles tree of the current test
* @throws IOException
*/
public void linkTool(String relativePath) throws IOException {
Preconditions.checkState(realFileSystem);
linkTool(relativePath, relativePath);
}
public void copyTool(String relativePath) throws IOException {
Path runfiles = FileSystems.getNativeFileSystem().getPath(BlazeTestUtils.runfilesDir());
Path source = runfiles.getRelative(TestConstants.WORKSPACE_NAME).getRelative(relativePath);
create(relativePath, FileSystemUtils.readContent(source, StandardCharsets.ISO_8859_1));
}
/**
* Links a tool into the workspace by creating a symbolic link to a real file.
*
* @param relativePath the relative path within the runfiles tree of the current test
* @param dest the relative path in the mock client
* @throws IOException
*/
public void linkTool(String relativePath, String dest) throws IOException {
Preconditions.checkState(realFileSystem);
Path target = runfilesDirectory.getRelative(TestConstants.WORKSPACE_NAME + "/" + relativePath);
if (!target.exists()) {
// In some cases we run tests in a special client with a ../READONLY/ path where we may also
// find the runfiles. Try that, too.
Path readOnlyClientPath =
getPath("../READONLY/" + TestConstants.WORKSPACE_NAME + "/" + relativePath);
if (!readOnlyClientPath.exists()) {
throw new IOException("target does not exist " + target);
} else {
target = readOnlyClientPath;
}
}
Path path = getPath(dest);
FileSystemUtils.createDirectoryAndParents(path.getParentDirectory());
path.delete();
path.createSymbolicLink(target);
}
/**
* Convenience method to link multiple tools. Same as calling {@link #linkTool(String)} for each
* parameter.
*/
public void linkTools(String... tools) throws IOException {
for (String tool : tools) {
linkTool(tool);
}
}
}