blob: 21a19ed290a9dc23f36e251eb103e5f8387442d2 [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.actions;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.NULL_ACTION_OWNER;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.NULL_ARTIFACT_OWNER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.testutil.Scratch;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Tests {@link ArtifactFactory}. Also see {@link ArtifactTest} for a test
* of individual artifacts.
*/
@RunWith(JUnit4.class)
public class ArtifactFactoryTest {
private Scratch scratch = new Scratch();
private Path execRoot;
private Root clientRoot;
private Root clientRoRoot;
private Root outRoot;
private PathFragment fooPath;
private PackageIdentifier fooPackage;
private PathFragment fooRelative;
private PathFragment barPath;
private PackageIdentifier barPackage;
private PathFragment barRelative;
private ArtifactFactory artifactFactory;
@Before
public void setUp() throws Exception {
execRoot = scratch.dir("/output/workspace");
clientRoot = Root.asSourceRoot(scratch.dir("/client/workspace"));
clientRoRoot = Root.asSourceRoot(scratch.dir("/client/RO/workspace"));
outRoot = Root.asDerivedRoot(execRoot, execRoot.getRelative("out-root/x/bin"));
fooPath = new PathFragment("foo");
fooPackage = PackageIdentifier.createInDefaultRepo(fooPath);
fooRelative = fooPath.getRelative("foosource.txt");
barPath = new PathFragment("foo/bar");
barPackage = PackageIdentifier.createInDefaultRepo(barPath);
barRelative = barPath.getRelative("barsource.txt");
artifactFactory = new ArtifactFactory(execRoot);
setupRoots();
}
private void setupRoots() {
Map<PackageIdentifier, Root> packageRootMap = new HashMap<>();
packageRootMap.put(fooPackage, clientRoot);
packageRootMap.put(barPackage, clientRoRoot);
artifactFactory.setPackageRoots(packageRootMap);
artifactFactory.setDerivedArtifactRoots(ImmutableList.of(outRoot));
}
@Test
public void testGetSourceArtifactYieldsSameArtifact() throws Exception {
assertSame(artifactFactory.getSourceArtifact(fooRelative, clientRoot),
artifactFactory.getSourceArtifact(fooRelative, clientRoot));
}
@Test
public void testGetSourceArtifactUnnormalized() throws Exception {
assertSame(artifactFactory.getSourceArtifact(fooRelative, clientRoot),
artifactFactory.getSourceArtifact(new PathFragment("foo/./foosource.txt"),
clientRoot));
}
@Test
public void testResolveArtifact_noDerived_simpleSource() throws Exception {
assertSame(artifactFactory.getSourceArtifact(fooRelative, clientRoot),
artifactFactory.resolveSourceArtifact(fooRelative));
assertSame(artifactFactory.getSourceArtifact(barRelative, clientRoRoot),
artifactFactory.resolveSourceArtifact(barRelative));
}
@Test
public void testResolveArtifact_noDerived_derivedRoot() throws Exception {
assertNull(artifactFactory.resolveSourceArtifact(
outRoot.getPath().getRelative(fooRelative).relativeTo(execRoot)));
assertNull(artifactFactory.resolveSourceArtifact(
outRoot.getPath().getRelative(barRelative).relativeTo(execRoot)));
}
@Test
public void testResolveArtifact_noDerived_simpleSource_other() throws Exception {
Artifact actual = artifactFactory.resolveSourceArtifact(fooRelative);
assertSame(artifactFactory.getSourceArtifact(fooRelative, clientRoot), actual);
actual = artifactFactory.resolveSourceArtifact(barRelative);
assertSame(artifactFactory.getSourceArtifact(barRelative, clientRoRoot), actual);
}
@Test
public void testResolveArtifactWithUpLevelFailsCleanly() throws Exception {
// We need a package in the root directory to make every exec path (even one with up-level
// references) be in a package.
Map<PackageIdentifier, Root> packageRoots = ImmutableMap.of(
PackageIdentifier.createInDefaultRepo(new PathFragment("")), clientRoot);
artifactFactory.setPackageRoots(packageRoots);
PathFragment outsideWorkspace = new PathFragment("../foo");
PathFragment insideWorkspace =
new PathFragment("../" + clientRoot.getPath().getBaseName() + "/foo");
assertNull(artifactFactory.resolveSourceArtifact(outsideWorkspace));
assertNull("Up-level-containing paths that descend into the right workspace aren't allowed",
artifactFactory.resolveSourceArtifact(insideWorkspace));
MockPackageRootResolver packageRootResolver = new MockPackageRootResolver();
packageRootResolver.setPackageRoots(packageRoots);
Map<PathFragment, Artifact> result = new HashMap<>();
result.put(insideWorkspace, null);
result.put(outsideWorkspace, null);
assertThat(
artifactFactory.resolveSourceArtifacts(ImmutableList.of(insideWorkspace, outsideWorkspace),
packageRootResolver).entrySet()).containsExactlyElementsIn(result.entrySet());
}
@Test
public void testClearResetsFactory() {
Artifact fooArtifact = artifactFactory.getSourceArtifact(fooRelative, clientRoot);
artifactFactory.clear();
setupRoots();
assertNotSame(fooArtifact, artifactFactory.getSourceArtifact(fooRelative, clientRoot));
}
@Test
public void testFindDerivedRoot() throws Exception {
assertSame(outRoot,
artifactFactory.findDerivedRoot(outRoot.getPath().getRelative(fooRelative)));
assertSame(outRoot,
artifactFactory.findDerivedRoot(outRoot.getPath().getRelative(barRelative)));
}
@Test
public void testSetGeneratingActionIdempotenceNewActionGraph() throws Exception {
Artifact a = artifactFactory.getDerivedArtifact(fooRelative, outRoot, NULL_ARTIFACT_OWNER);
Artifact b = artifactFactory.getDerivedArtifact(barRelative, outRoot, NULL_ARTIFACT_OWNER);
MutableActionGraph actionGraph = new MapBasedActionGraph();
Action originalAction = new ActionsTestUtil.NullAction(NULL_ACTION_OWNER, a);
actionGraph.registerAction(originalAction);
// Creating a second Action referring to the Artifact should create a conflict.
try {
Action action = new ActionsTestUtil.NullAction(NULL_ACTION_OWNER, a, b);
actionGraph.registerAction(action);
fail();
} catch (ActionConflictException e) {
assertSame(a, e.getArtifact());
assertSame(originalAction, actionGraph.getGeneratingAction(a));
}
}
@Test
public void testGetDerivedArtifact() throws Exception {
PathFragment toolPath = new PathFragment("_bin/tool");
Artifact artifact = artifactFactory.getDerivedArtifact(toolPath);
assertEquals(toolPath, artifact.getExecPath());
assertEquals(Root.asDerivedRoot(execRoot), artifact.getRoot());
assertEquals(execRoot.getRelative(toolPath), artifact.getPath());
assertNull(artifact.getOwner());
}
@Test
public void testGetDerivedArtifactFailsForAbsolutePath() throws Exception {
try {
artifactFactory.getDerivedArtifact(new PathFragment("/_bin/b"));
fail();
} catch (IllegalArgumentException e) {
// Expected exception
}
}
private static class MockPackageRootResolver implements PackageRootResolver {
private Map<PathFragment, Root> packageRoots = Maps.newHashMap();
public void setPackageRoots(Map<PackageIdentifier, Root> packageRoots) {
for (Entry<PackageIdentifier, Root> packageRoot : packageRoots.entrySet()) {
this.packageRoots.put(packageRoot.getKey().getPackageFragment(), packageRoot.getValue());
}
}
@Override
public Map<PathFragment, Root> findPackageRoots(Iterable<PathFragment> execPaths) {
Map<PathFragment, Root> result = new HashMap<>();
for (PathFragment execPath : execPaths) {
for (PathFragment dir = execPath.getParentDirectory(); dir != null;
dir = dir.getParentDirectory()) {
if (packageRoots.get(dir) != null) {
result.put(execPath, packageRoots.get(dir));
}
}
if (result.get(execPath) == null) {
result.put(execPath, null);
}
}
return result;
}
}
@Test
public void testArtifactDeserializationWithoutReusedArtifacts() throws Exception {
PathFragment derivedPath = outRoot.getExecPath().getRelative("fruit/banana");
artifactFactory.clear();
artifactFactory.setDerivedArtifactRoots(ImmutableList.of(outRoot));
MockPackageRootResolver rootResolver = new MockPackageRootResolver();
rootResolver.setPackageRoots(
ImmutableMap.of(PackageIdentifier.createInDefaultRepo(""), clientRoot));
Artifact artifact1 = artifactFactory.deserializeArtifact(derivedPath, rootResolver);
Artifact artifact2 = artifactFactory.deserializeArtifact(derivedPath, rootResolver);
assertEquals(artifact1, artifact2);
assertNull(artifact1.getOwner());
assertNull(artifact2.getOwner());
assertEquals(derivedPath, artifact1.getExecPath());
assertEquals(derivedPath, artifact2.getExecPath());
// Source artifacts are always reused
PathFragment sourcePath = clientRoot.getExecPath().getRelative("fruit/mango");
artifact1 = artifactFactory.deserializeArtifact(sourcePath, rootResolver);
artifact2 = artifactFactory.deserializeArtifact(sourcePath, rootResolver);
assertSame(artifact1, artifact2);
assertEquals(sourcePath, artifact1.getExecPath());
}
@Test
public void testDeserializationWithInvalidPath() throws Exception {
artifactFactory.clear();
PathFragment randomPath = new PathFragment("maracuja/lemon/kiwi");
Artifact artifact = artifactFactory.deserializeArtifact(randomPath,
new MockPackageRootResolver());
assertNull(artifact);
}
}