// Copyright 2015 Google Inc. 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.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.packages.PackageIdentifier; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.util.FsApparatus; 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 FsApparatus scratch = FsApparatus.newInMemory(); 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 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 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 packageRoots = Maps.newHashMap(); public void setPackageRoots(Map packageRoots) { for (Entry packageRoot : packageRoots.entrySet()) { this.packageRoots.put(packageRoot.getKey().getPackageFragment(), packageRoot.getValue()); } } @Override public Map findPackageRoots(Iterable execPaths) { Map 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); } }