blob: 07845171e4cf0f12af27a3aa6ff88824796cedba [file] [log] [blame]
// Copyright 2020 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.skyframe;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.ActionLookupKey;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.ArchivedTreeArtifact;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.actions.ArtifactRoot.RootType;
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.testutil.ManualClock;
import com.google.devtools.build.lib.vfs.DigestHashFunction;
import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.FileStatusWithDigestAdapter;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Symlinks;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
import com.google.devtools.build.skyframe.SkyFunctionName;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Helper class to allow sharing test helpers between {@link FilesystemValueCheckerTest} and {@link
* FilesystemValueCheckerParameterizedTest}.
*/
public class FilesystemValueCheckerTestBase {
static final int FSVC_THREADS_FOR_TEST = 200;
static final ActionLookupKey ACTION_LOOKUP_KEY =
new ActionLookupKey() {
@Override
public SkyFunctionName functionName() {
return SkyFunctionName.FOR_TESTING;
}
@Nullable
@Override
public Label getLabel() {
return null;
}
};
final MockFileSystem fs = new MockFileSystem();
SpecialArtifact createTreeArtifact(String relPath) throws IOException {
String outSegment = "bin";
Path outputDir = fs.getPath("/" + outSegment);
Path outputPath = outputDir.getRelative(relPath);
outputDir.createDirectory();
ArtifactRoot derivedRoot =
ArtifactRoot.asDerivedRoot(fs.getPath("/"), RootType.Output, outSegment);
return ActionsTestUtil.createTreeArtifactWithGeneratingAction(
derivedRoot,
derivedRoot.getExecPath().getRelative(derivedRoot.getRoot().relativize(outputPath)));
}
static ActionExecutionValue actionValueWithTreeArtifacts(List<TreeFileArtifact> contents)
throws IOException {
return actionValueWithTreeArtifacts(contents, ImmutableList.of());
}
static ActionExecutionValue actionValueWithTreeArtifacts(
Iterable<TreeFileArtifact> contents, Iterable<ArchivedTreeArtifact> archivedTreeArtifacts)
throws IOException {
TreeArtifactValue.MultiBuilder treeArtifacts = TreeArtifactValue.newMultiBuilder();
for (TreeFileArtifact output : contents) {
treeArtifacts.putChild(output, createMetadataFromFileSystem(output));
}
for (ArchivedTreeArtifact archivedTreeArtifact : archivedTreeArtifacts) {
treeArtifacts.setArchivedRepresentation(
archivedTreeArtifact, createMetadataFromFileSystem(archivedTreeArtifact));
}
Map<Artifact, TreeArtifactValue> treeArtifactData = new HashMap<>();
treeArtifacts.injectTo(treeArtifactData::put);
return ActionExecutionValue.create(
/*artifactData=*/ ImmutableMap.of(),
treeArtifactData,
/*outputSymlinks=*/ null,
/*discoveredModules=*/ null,
/*actionDependsOnBuildId=*/ false);
}
private static FileArtifactValue createMetadataFromFileSystem(Artifact artifact)
throws IOException {
Path path = artifact.getPath();
FileArtifactValue noDigest =
ActionMetadataHandler.fileArtifactValueFromArtifact(
artifact, FileStatusWithDigestAdapter.adapt(path.statIfFound(Symlinks.NOFOLLOW)), null);
return FileArtifactValue.createFromInjectedDigest(
noDigest, path.getDigest(), !artifact.isConstantMetadata());
}
void writeFile(Path path, String... lines) throws IOException {
// Make sure we advance the clock to detect modifications which do not change the size, which
// rely on ctime.
fs.advanceClockMillis(1);
FileSystemUtils.writeIsoLatin1(path, lines);
}
static final class MockFileSystem extends InMemoryFileSystem {
boolean statThrowsRuntimeException;
boolean readlinkThrowsIoException;
MockFileSystem() {
this(new ManualClock());
}
private MockFileSystem(ManualClock clock) {
super(clock, DigestHashFunction.SHA256);
}
@Override
public FileStatus statIfFound(PathFragment path, boolean followSymlinks) throws IOException {
if (statThrowsRuntimeException) {
throw new RuntimeException("bork");
}
return super.statIfFound(path, followSymlinks);
}
@Override
protected PathFragment readSymbolicLink(PathFragment path) throws IOException {
if (readlinkThrowsIoException) {
throw new IOException("readlink failed");
}
return super.readSymbolicLink(path);
}
void advanceClockMillis(int millis) {
((ManualClock) clock).advanceMillis(millis);
}
}
}