| // 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.common.truth.Truth.assertWithMessage; |
| import static org.junit.Assert.assertThrows; |
| import static org.mockito.Mockito.mock; |
| |
| import com.google.common.base.Equivalence; |
| import com.google.common.collect.ImmutableClassToInstanceMap; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| import com.google.common.testing.EqualsTester; |
| import com.google.common.testing.EquivalenceTester; |
| import com.google.devtools.build.lib.actions.Artifact.ArchivedTreeArtifact; |
| import com.google.devtools.build.lib.actions.Artifact.ArtifactSerializationContext; |
| import com.google.devtools.build.lib.actions.Artifact.DerivedArtifact; |
| import com.google.devtools.build.lib.actions.Artifact.OwnerlessArtifactWrapper; |
| import com.google.devtools.build.lib.actions.Artifact.SourceArtifact; |
| import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; |
| import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType; |
| import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact; |
| import com.google.devtools.build.lib.actions.ArtifactRoot.RootType; |
| import com.google.devtools.build.lib.actions.util.ActionsTestUtil; |
| import com.google.devtools.build.lib.actions.util.LabelArtifactOwner; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.cmdline.LabelConstants; |
| import com.google.devtools.build.lib.rules.cpp.CppFileTypes; |
| import com.google.devtools.build.lib.rules.java.JavaSemantics; |
| import com.google.devtools.build.lib.skyframe.config.BuildConfigurationKey; |
| import com.google.devtools.build.lib.skyframe.serialization.AutoRegistry; |
| import com.google.devtools.build.lib.skyframe.serialization.FingerprintValueService; |
| import com.google.devtools.build.lib.skyframe.serialization.FingerprintValueStore; |
| import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; |
| import com.google.devtools.build.lib.skyframe.serialization.ObjectCodecs; |
| import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationDepsUtils; |
| import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester; |
| import com.google.devtools.build.lib.starlarkbuildapi.FileRootApi; |
| import com.google.devtools.build.lib.testutil.Scratch; |
| import com.google.devtools.build.lib.util.FileType; |
| import com.google.devtools.build.lib.util.FileTypeSet; |
| import com.google.devtools.build.lib.vfs.FileSystem; |
| import com.google.devtools.build.lib.vfs.Path; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.devtools.build.lib.vfs.Root; |
| import com.google.devtools.build.lib.vfs.Root.RootCodecDependencies; |
| import com.google.devtools.build.skyframe.SkyFunctionName; |
| import com.google.testing.junit.testparameterinjector.TestParameter; |
| import com.google.testing.junit.testparameterinjector.TestParameterInjector; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import net.starlark.java.eval.Starlark; |
| import net.starlark.java.eval.StarlarkSemantics; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| /** Tests for {@link Artifact}. */ |
| @RunWith(TestParameterInjector.class) |
| public final class ArtifactTest { |
| |
| private final Scratch scratch = new Scratch(); |
| private Path execDir; |
| private ArtifactRoot rootDir; |
| |
| @Before |
| public void setRootDir() throws Exception { |
| execDir = scratch.dir("/base/exec"); |
| rootDir = ArtifactRoot.asDerivedRoot(execDir, RootType.Output, "root"); |
| } |
| |
| @Test |
| public void testConstruction_badRootDir() throws IOException { |
| Path f1 = scratch.file("/exec/dir/file.ext"); |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> |
| ActionsTestUtil.createArtifactWithExecPath( |
| ArtifactRoot.asDerivedRoot(execDir, RootType.Output, "bogus"), |
| f1.relativeTo(execDir)) |
| .getRootRelativePath()); |
| } |
| |
| private static long getUsedMemory() { |
| System.gc(); |
| System.gc(); |
| System.runFinalization(); |
| System.gc(); |
| System.gc(); |
| return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); |
| } |
| |
| @Test |
| public void testMemoryUsage() throws IOException { |
| ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/foo"))); |
| PathFragment aPath = PathFragment.create("src/a"); |
| int arrSize = 1 << 20; |
| Object[] arr = new Object[arrSize]; |
| long usedMemory = getUsedMemory(); |
| for (int i = 0; i < arrSize; i++) { |
| arr[i] = ActionsTestUtil.createArtifactWithExecPath(root, aPath); |
| } |
| assertThat((getUsedMemory() - usedMemory) / arrSize).isAtMost(34L); |
| } |
| |
| @Test |
| public void testEquivalenceRelation() throws Exception { |
| PathFragment aPath = PathFragment.create("src/a"); |
| PathFragment bPath = PathFragment.create("src/b"); |
| assertThat(ActionsTestUtil.createArtifactWithRootRelativePath(rootDir, aPath)) |
| .isEqualTo(ActionsTestUtil.createArtifactWithRootRelativePath(rootDir, aPath)); |
| assertThat(ActionsTestUtil.createArtifactWithRootRelativePath(rootDir, bPath)) |
| .isEqualTo(ActionsTestUtil.createArtifactWithRootRelativePath(rootDir, bPath)); |
| assertThat( |
| ActionsTestUtil.createArtifactWithRootRelativePath(rootDir, aPath) |
| .equals(ActionsTestUtil.createArtifactWithRootRelativePath(rootDir, bPath))) |
| .isFalse(); |
| } |
| |
| @Test |
| public void testComparison() { |
| PathFragment aPath = PathFragment.create("src/a"); |
| PathFragment bPath = PathFragment.create("src/b"); |
| Artifact aArtifact = ActionsTestUtil.createArtifactWithRootRelativePath(rootDir, aPath); |
| Artifact bArtifact = ActionsTestUtil.createArtifactWithRootRelativePath(rootDir, bPath); |
| assertThat(Artifact.EXEC_PATH_COMPARATOR.compare(aArtifact, bArtifact)).isEqualTo(-1); |
| assertThat(Artifact.EXEC_PATH_COMPARATOR.compare(aArtifact, aArtifact)).isEqualTo(0); |
| assertThat(Artifact.EXEC_PATH_COMPARATOR.compare(bArtifact, bArtifact)).isEqualTo(0); |
| assertThat(Artifact.EXEC_PATH_COMPARATOR.compare(bArtifact, aArtifact)).isEqualTo(1); |
| } |
| |
| @Test |
| public void testGetFilename() throws Exception { |
| ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/foo"))); |
| Artifact javaFile = ActionsTestUtil.createArtifact(root, scratch.file("/foo/Bar.java")); |
| Artifact generatedHeader = |
| ActionsTestUtil.createArtifact(root, scratch.file("/foo/bar.proto.h")); |
| Artifact generatedCc = ActionsTestUtil.createArtifact(root, scratch.file("/foo/bar.proto.cc")); |
| Artifact aCPlusPlusFile = ActionsTestUtil.createArtifact(root, scratch.file("/foo/bar.cc")); |
| assertThat(JavaSemantics.JAVA_SOURCE.matches(javaFile.getFilename())).isTrue(); |
| assertThat(CppFileTypes.CPP_HEADER.matches(generatedHeader.getFilename())).isTrue(); |
| assertThat(CppFileTypes.CPP_SOURCE.matches(generatedCc.getFilename())).isTrue(); |
| assertThat(CppFileTypes.CPP_SOURCE.matches(aCPlusPlusFile.getFilename())).isTrue(); |
| } |
| |
| @Test |
| public void testGetExtension() throws Exception { |
| ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/foo"))); |
| Artifact javaFile = ActionsTestUtil.createArtifact(root, scratch.file("/foo/Bar.java")); |
| assertThat(javaFile.getExtension()).isEqualTo("java"); |
| } |
| |
| @Test |
| public void testIsFileType() throws Exception { |
| ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/foo"))); |
| Artifact javaFile = ActionsTestUtil.createArtifact(root, scratch.file("/foo/Bar.java")); |
| assertThat(javaFile.isFileType(FileType.of("java"))).isTrue(); |
| assertThat(javaFile.isFileType(FileType.of("cc"))).isFalse(); |
| } |
| |
| @Test |
| public void testIsFileTypeSet() throws Exception { |
| ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/foo"))); |
| Artifact javaFile = ActionsTestUtil.createArtifact(root, scratch.file("/foo/Bar.java")); |
| assertThat(javaFile.isFileType(FileTypeSet.of(FileType.of("cc"), FileType.of("java")))) |
| .isTrue(); |
| assertThat(javaFile.isFileType(FileTypeSet.of(FileType.of("py"), FileType.of("js")))).isFalse(); |
| assertThat(javaFile.isFileType(FileTypeSet.of())).isFalse(); |
| } |
| |
| @Test |
| public void testMangledPath() { |
| String path = "dir/sub_dir/name:end"; |
| assertThat(Actions.escapedPath(path)).isEqualTo("dir_Ssub_Udir_Sname_Cend"); |
| } |
| |
| private List<Artifact> getFooBarArtifacts() throws Exception { |
| ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/foo"))); |
| Artifact aHeader1 = ActionsTestUtil.createArtifact(root, scratch.file("/foo/bar1.h")); |
| Artifact aHeader2 = ActionsTestUtil.createArtifact(root, scratch.file("/foo/bar2.h")); |
| return Lists.newArrayList(aHeader1, aHeader2); |
| } |
| |
| @Test |
| public void testAddExecPaths() throws Exception { |
| List<String> paths = new ArrayList<>(); |
| Artifact.addExecPaths(getFooBarArtifacts(), paths); |
| assertThat(paths).containsExactly("bar1.h", "bar2.h"); |
| } |
| |
| @Test |
| public void testAddExecPathsNewActionGraph() throws Exception { |
| List<String> paths = new ArrayList<>(); |
| Artifact.addExecPaths(getFooBarArtifacts(), paths); |
| assertThat(paths).containsExactly("bar1.h", "bar2.h"); |
| } |
| |
| @Test |
| public void testRootRelativePathIsSameAsExecPath() throws Exception { |
| ArtifactRoot root = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/foo"))); |
| Artifact a = ActionsTestUtil.createArtifact(root, scratch.file("/foo/bar1.h")); |
| assertThat(a.getRootRelativePath()).isSameInstanceAs(a.getExecPath()); |
| } |
| |
| @Test |
| public void testToDetailString() throws Exception { |
| Path execRoot = scratch.getFileSystem().getPath("/execroot/workspace"); |
| Artifact a = |
| ActionsTestUtil.createArtifact( |
| ArtifactRoot.asDerivedRoot(execRoot, RootType.Output, "b"), "c"); |
| assertThat(a.toDetailString()).isEqualTo("[[<execution_root>]b]c"); |
| } |
| |
| @Test |
| public void testWeirdArtifact() { |
| Path execRoot = scratch.getFileSystem().getPath("/"); |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> |
| ActionsTestUtil.createArtifactWithExecPath( |
| ArtifactRoot.asDerivedRoot(execRoot, RootType.Output, "a"), |
| PathFragment.create("c")) |
| .getRootRelativePath()); |
| } |
| |
| @Test |
| public void derivedArtifactCodecs( |
| @TestParameter boolean includeGeneratingActionKey, @TestParameter boolean useSharedValues) |
| throws Exception { |
| ArtifactSerializationContext artifactContext = |
| new ArtifactSerializationContext() { |
| @Override |
| public SourceArtifact getSourceArtifact( |
| PathFragment execPath, Root root, ArtifactOwner owner) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean includeGeneratingActionKey(DerivedArtifact artifact) { |
| return includeGeneratingActionKey; |
| } |
| }; |
| |
| DerivedArtifact artifact = |
| (DerivedArtifact) ActionsTestUtil.createArtifact(rootDir, "dir/out.txt"); |
| artifact.setGeneratingActionKey(ActionsTestUtil.NULL_ACTION_LOOKUP_DATA); |
| |
| ArtifactRoot anotherRoot = |
| ArtifactRoot.asDerivedRoot(scratch.getFileSystem().getPath("/"), RootType.Output, "other"); |
| DerivedArtifact anotherArtifact = |
| DerivedArtifact.create( |
| anotherRoot, |
| anotherRoot.getExecPath().getRelative("dir/out.txt"), |
| ActionsTestUtil.NULL_ARTIFACT_OWNER); |
| anotherArtifact.setGeneratingActionKey(ActionsTestUtil.NULL_ACTION_LOOKUP_DATA); |
| |
| SpecialArtifact tree = |
| ActionsTestUtil.createTreeArtifactWithGeneratingAction( |
| rootDir, rootDir.getExecPath().getRelative("tree")); |
| TreeFileArtifact treeChild = TreeFileArtifact.createTreeOutput(tree, "child"); |
| ArchivedTreeArtifact archivedTree = ArchivedTreeArtifact.createForTree(tree); |
| ArchivedTreeArtifact customArchivedTree = |
| ArchivedTreeArtifact.createWithCustomDerivedTreeRoot( |
| tree, PathFragment.create("custom"), PathFragment.create("archived.zip")); |
| |
| SpecialArtifact templateExpansionTree = |
| ActionsTestUtil.createTreeArtifactWithGeneratingAction( |
| rootDir, rootDir.getExecPath().getRelative("template")); |
| TreeFileArtifact expansionOutput = |
| TreeFileArtifact.createTemplateExpansionOutput( |
| templateExpansionTree, |
| "output", |
| ActionsTestUtil.NULL_TEMPLATE_EXPANSION_ARTIFACT_OWNER); |
| expansionOutput.setGeneratingActionKey( |
| ActionLookupData.create(ActionsTestUtil.NULL_TEMPLATE_EXPANSION_ARTIFACT_OWNER, 0)); |
| |
| SerializationTester tester = |
| new SerializationTester( |
| artifact, |
| anotherArtifact, |
| tree, |
| treeChild, |
| archivedTree, |
| customArchivedTree, |
| expansionOutput) |
| .addDependency(FileSystem.class, scratch.getFileSystem()) |
| .addDependency( |
| RootCodecDependencies.class, new RootCodecDependencies(anotherRoot.getRoot())) |
| .addDependency(ArtifactSerializationContext.class, artifactContext); |
| |
| if (useSharedValues) { |
| for (ObjectCodec<? extends Artifact> codec : ArtifactCodecs.VALUE_SHARING_CODECS) { |
| tester.addCodec(codec); |
| } |
| tester.makeMemoizingAndAllowFutureBlocking(/* allowFutureBlocking= */ true); |
| } |
| |
| if (!includeGeneratingActionKey) { |
| tester.<DerivedArtifact>setVerificationFunction( |
| (original, deserialized) -> { |
| String debug = |
| String.format( |
| "original=%s\ndeseriaized=%s", |
| original.toDebugString(), deserialized.toDebugString()); |
| assertWithMessage(debug).that(deserialized.hasGeneratingActionKey()).isFalse(); |
| assertWithMessage(debug).that(deserialized.equalsWithoutOwner(original)).isTrue(); |
| assertThat(new OwnerlessArtifactWrapper(deserialized)) |
| .isEqualTo(new OwnerlessArtifactWrapper(original)); |
| |
| assertThrows(debug, RuntimeException.class, deserialized::getArtifactOwner); |
| assertThrows(debug, RuntimeException.class, deserialized::getGeneratingActionKey); |
| assertThrows(debug, RuntimeException.class, deserialized::getOwner); |
| assertThrows(debug, RuntimeException.class, deserialized::getOwnerLabel); |
| assertThrows(debug, RuntimeException.class, () -> deserialized.equals(original)); |
| assertThrows( |
| debug, |
| RuntimeException.class, |
| () -> deserialized.setGeneratingActionKey(original.getGeneratingActionKey())); |
| }); |
| } |
| |
| tester.runTests(); |
| } |
| |
| @Test |
| public void sourceArtifactCodecRecyclesSourceArtifactInstances( |
| @TestParameter boolean useSharedValues) throws Exception { |
| Root root = Root.fromPath(scratch.dir("/")); |
| ArtifactRoot artifactRoot = ArtifactRoot.asSourceRoot(root); |
| ArtifactFactory artifactFactory = |
| new ArtifactFactory(execDir.getParentDirectory(), "blaze-out"); |
| |
| ObjectCodecs objectCodecs = |
| new ObjectCodecs( |
| AutoRegistry.get() |
| .getBuilder() |
| .addReferenceConstant(scratch.getFileSystem()) |
| .setAllowDefaultCodec(true) |
| .build(), |
| ImmutableClassToInstanceMap.builder() |
| .put(FileSystem.class, scratch.getFileSystem()) |
| .put(ArtifactSerializationContext.class, artifactFactory::getSourceArtifact) |
| .put(RootCodecDependencies.class, new RootCodecDependencies(artifactRoot.getRoot())) |
| .build()); |
| |
| FingerprintValueService service = null; |
| if (useSharedValues) { |
| service = FingerprintValueService.createForTesting(FingerprintValueStore.inMemoryStore()); |
| for (ObjectCodec<? extends Artifact> codec : ArtifactCodecs.VALUE_SHARING_CODECS) { |
| objectCodecs = objectCodecs.withCodecOverridesForTesting(ImmutableList.of(codec)); |
| } |
| } |
| |
| PathFragment pathFragment = PathFragment.create("src/foo.cc"); |
| ArtifactOwner owner = new LabelArtifactOwner(Label.parseCanonicalUnchecked("//foo:bar")); |
| SourceArtifact sourceArtifact = new SourceArtifact(artifactRoot, pathFragment, owner); |
| |
| SourceArtifact deserialized1; |
| SourceArtifact deserialized2; |
| if (useSharedValues) { |
| deserialized1 = |
| (SourceArtifact) |
| objectCodecs.deserializeMemoizedAndBlocking( |
| service, |
| objectCodecs |
| .serializeMemoizedAndBlocking( |
| service, sourceArtifact, /* profileCollector= */ null) |
| .getObject()); |
| deserialized2 = |
| (SourceArtifact) |
| objectCodecs.deserializeMemoizedAndBlocking( |
| service, |
| objectCodecs |
| .serializeMemoizedAndBlocking( |
| service, sourceArtifact, /* profileCollector= */ null) |
| .getObject()); |
| } else { |
| deserialized1 = |
| (SourceArtifact) objectCodecs.deserialize(objectCodecs.serialize(sourceArtifact)); |
| deserialized2 = |
| (SourceArtifact) objectCodecs.deserialize(objectCodecs.serialize(sourceArtifact)); |
| } |
| assertThat(deserialized1).isSameInstanceAs(deserialized2); |
| |
| Artifact sourceArtifactFromFactory = |
| artifactFactory.getSourceArtifact(pathFragment, root, owner); |
| Artifact deserialized; |
| if (useSharedValues) { |
| deserialized = |
| (Artifact) |
| objectCodecs.deserializeMemoizedAndBlocking( |
| service, |
| objectCodecs |
| .serializeMemoizedAndBlocking( |
| service, sourceArtifactFromFactory, /* profileCollector= */ null) |
| .getObject()); |
| } else { |
| deserialized = |
| (Artifact) objectCodecs.deserialize(objectCodecs.serialize(sourceArtifactFromFactory)); |
| } |
| assertThat(sourceArtifactFromFactory).isSameInstanceAs(deserialized); |
| } |
| |
| @Test |
| public void testLongDirname() throws Exception { |
| String dirName = createDirNameArtifact().getDirname(); |
| |
| assertThat(dirName).isEqualTo("aaa/bbb/ccc"); |
| } |
| |
| @Test |
| public void testDirnameInExecutionDir() throws Exception { |
| Artifact artifact = |
| ActionsTestUtil.createArtifact( |
| ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/foo"))), |
| scratch.file("/foo/bar.txt")); |
| |
| assertThat(artifact.getDirname()).isEqualTo("."); |
| } |
| |
| @Test |
| public void testCanConstructPathFromDirAndFilename() throws Exception { |
| Artifact artifact = createDirNameArtifact(); |
| String constructed = String.format("%s/%s", artifact.getDirname(), artifact.getFilename()); |
| |
| assertThat(constructed).isEqualTo("aaa/bbb/ccc/ddd"); |
| } |
| |
| @Test |
| public void testIsSourceArtifact() throws Exception { |
| assertThat( |
| new Artifact.SourceArtifact( |
| ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/"))), |
| PathFragment.create("src/foo.cc"), |
| ArtifactOwner.NULL_OWNER) |
| .isSourceArtifact()) |
| .isTrue(); |
| assertThat( |
| ActionsTestUtil.createArtifact( |
| ArtifactRoot.asDerivedRoot(scratch.dir("/genfiles"), RootType.Output, "aaa"), |
| scratch.file("/genfiles/aaa/bar.out")) |
| .isSourceArtifact()) |
| .isFalse(); |
| } |
| |
| @Test |
| public void testGetRoot() throws Exception { |
| Path execRoot = scratch.getFileSystem().getPath("/"); |
| ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, RootType.Output, "newRoot"); |
| assertThat(ActionsTestUtil.createArtifact(root, scratch.file("/newRoot/foo")).getRoot()) |
| .isEqualTo(root); |
| } |
| |
| @Test |
| public void hashCodeAndEquals() { |
| Path execRoot = scratch.getFileSystem().getPath("/"); |
| ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, RootType.Output, "newRoot"); |
| ActionLookupKey firstOwner = |
| new ActionLookupKey() { |
| @Override |
| public Label getLabel() { |
| return null; |
| } |
| |
| @Override |
| public BuildConfigurationKey getConfigurationKey() { |
| return null; |
| } |
| |
| @Override |
| public SkyFunctionName functionName() { |
| return null; |
| } |
| }; |
| ActionLookupKey secondOwner = |
| new ActionLookupKey() { |
| @Override |
| public Label getLabel() { |
| return null; |
| } |
| |
| @Override |
| public BuildConfigurationKey getConfigurationKey() { |
| return null; |
| } |
| |
| @Override |
| public SkyFunctionName functionName() { |
| return null; |
| } |
| }; |
| DerivedArtifact derived1 = |
| DerivedArtifact.create(root, PathFragment.create("newRoot/shared"), firstOwner); |
| derived1.setGeneratingActionKey(ActionLookupData.create(firstOwner, 0)); |
| DerivedArtifact derived2 = |
| DerivedArtifact.create(root, PathFragment.create("newRoot/shared"), secondOwner); |
| derived2.setGeneratingActionKey(ActionLookupData.create(secondOwner, 0)); |
| ArtifactRoot sourceRoot = ArtifactRoot.asSourceRoot(Root.fromPath(root.getRoot().asPath())); |
| Artifact source1 = new SourceArtifact(sourceRoot, PathFragment.create("shared"), firstOwner); |
| Artifact source2 = new SourceArtifact(sourceRoot, PathFragment.create("shared"), secondOwner); |
| new EqualsTester() |
| .addEqualityGroup(derived1) |
| .addEqualityGroup(derived2) |
| .addEqualityGroup(source1, source2) |
| .testEquals(); |
| assertThat(derived1.hashCode()).isNotEqualTo(derived2.hashCode()); |
| assertThat(derived1.hashCode()).isNotEqualTo(source1.hashCode()); |
| assertThat(source1.hashCode()).isEqualTo(source2.hashCode()); |
| Artifact.OwnerlessArtifactWrapper wrapper1 = new Artifact.OwnerlessArtifactWrapper(derived1); |
| Artifact.OwnerlessArtifactWrapper wrapper2 = new Artifact.OwnerlessArtifactWrapper(derived2); |
| Artifact.OwnerlessArtifactWrapper wrapper3 = new Artifact.OwnerlessArtifactWrapper(source1); |
| Artifact.OwnerlessArtifactWrapper wrapper4 = new Artifact.OwnerlessArtifactWrapper(source2); |
| new EqualsTester() |
| .addEqualityGroup(wrapper1, wrapper2) |
| .addEqualityGroup(wrapper3, wrapper4) |
| .testEquals(); |
| Path path1 = derived1.getPath(); |
| Path path2 = derived2.getPath(); |
| Path path3 = source1.getPath(); |
| Path path4 = source2.getPath(); |
| new EqualsTester().addEqualityGroup(path1, path2, path3, path4).testEquals(); |
| } |
| |
| private Artifact createDirNameArtifact() throws Exception { |
| return ActionsTestUtil.createArtifact( |
| ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/"))), |
| scratch.file("/aaa/bbb/ccc/ddd")); |
| } |
| |
| @Test |
| public void canDeclareContentBasedOutput() { |
| Path execRoot = scratch.getFileSystem().getPath("/"); |
| ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, RootType.Output, "newRoot"); |
| assertThat( |
| DerivedArtifact.create( |
| root, |
| PathFragment.create("newRoot/my.output"), |
| ActionsTestUtil.NULL_ARTIFACT_OWNER, |
| /*contentBasedPath=*/ true) |
| .contentBasedPath()) |
| .isTrue(); |
| } |
| |
| @Test |
| public void testGetRepositoryRelativePathExternalSourceArtifacts() throws IOException { |
| ArtifactRoot externalRoot = |
| ArtifactRoot.asExternalSourceRoot( |
| Root.fromPath( |
| scratch |
| .dir("/output_base") |
| .getRelative(LabelConstants.EXTERNAL_REPOSITORY_LOCATION) |
| .getRelative("foo"))); |
| |
| // --experimental_sibling_repository_layout not set |
| assertThat( |
| new Artifact.SourceArtifact( |
| externalRoot, |
| LabelConstants.EXTERNAL_PATH_PREFIX.getRelative("foo/bar/baz.cc"), |
| ArtifactOwner.NULL_OWNER) |
| .getRepositoryRelativePath()) |
| .isEqualTo(PathFragment.create("bar/baz.cc")); |
| |
| // --experimental_sibling_repository_layout set |
| assertThat( |
| new Artifact.SourceArtifact( |
| externalRoot, |
| LabelConstants.EXPERIMENTAL_EXTERNAL_PATH_PREFIX.getRelative("foo/bar/baz.cc"), |
| ArtifactOwner.NULL_OWNER) |
| .getRepositoryRelativePath()) |
| .isEqualTo(PathFragment.create("bar/baz.cc")); |
| } |
| |
| @Test |
| public void archivedTreeArtifact_create_returnsArtifactInArchivedRoot() { |
| ArtifactRoot root = |
| ArtifactRoot.asDerivedRoot(execDir, RootType.Output, "blaze-out", "fastbuild"); |
| SpecialArtifact tree = createTreeArtifact(root, "tree"); |
| |
| ArchivedTreeArtifact archivedTreeArtifact = ArchivedTreeArtifact.createForTree(tree); |
| |
| assertThat(archivedTreeArtifact.getParent()).isSameInstanceAs(tree); |
| assertThat(archivedTreeArtifact.getArtifactOwner()) |
| .isSameInstanceAs(ActionsTestUtil.NULL_ARTIFACT_OWNER); |
| assertThat(archivedTreeArtifact.getExecPathString()) |
| .isEqualTo("blaze-out/:archived_tree_artifacts/fastbuild/tree.zip"); |
| assertThat(archivedTreeArtifact.getRoot().getExecPathString()) |
| .isEqualTo("blaze-out/:archived_tree_artifacts/fastbuild"); |
| } |
| |
| @Test |
| public void archivedTreeArtifact_create_returnsArtifactWithGeneratingActionFromParent() { |
| ActionLookupKey actionLookupKey = mock(ActionLookupKey.class); |
| ActionLookupData actionLookupData = ActionLookupData.create(actionLookupKey, 0); |
| SpecialArtifact tree = createTreeArtifact(rootDir, "tree", actionLookupData); |
| |
| ArchivedTreeArtifact archivedTreeArtifact = ArchivedTreeArtifact.createForTree(tree); |
| |
| assertThat(archivedTreeArtifact.getExecPathString()) |
| .isEqualTo("root/:archived_tree_artifacts/tree.zip"); |
| assertThat(archivedTreeArtifact.getArtifactOwner()).isSameInstanceAs(actionLookupKey); |
| assertThat(archivedTreeArtifact.getGeneratingActionKey()).isSameInstanceAs(actionLookupData); |
| } |
| |
| @Test |
| public void archivedTreeArtifact_createWithCustomDerivedTreeRoot_returnsArtifactWithCustomRoot() { |
| ArtifactRoot root = |
| ArtifactRoot.asDerivedRoot(execDir, RootType.Output, "blaze-out", "fastbuild"); |
| SpecialArtifact tree = createTreeArtifact(root, "dir/tree"); |
| |
| ArchivedTreeArtifact archivedTreeArtifact = |
| ArchivedTreeArtifact.createWithCustomDerivedTreeRoot( |
| tree, PathFragment.create("custom/custom2"), PathFragment.create("treePath/file.xyz")); |
| |
| assertThat(archivedTreeArtifact.getParent()).isSameInstanceAs(tree); |
| assertThat(archivedTreeArtifact.getExecPathString()) |
| .isEqualTo("blaze-out/custom/custom2/fastbuild/treePath/file.xyz"); |
| assertThat(archivedTreeArtifact.getRoot().getExecPathString()) |
| .isEqualTo("blaze-out/custom/custom2/fastbuild"); |
| } |
| |
| @Test |
| public void archivedTreeArtifact_codec_roundTripsArchivedArtifact( |
| @TestParameter boolean useSharedValues) throws Exception { |
| ArchivedTreeArtifact artifact1 = createArchivedTreeArtifact(rootDir, "tree1"); |
| ArtifactRoot anotherRoot = |
| ArtifactRoot.asDerivedRoot(scratch.getFileSystem().getPath("/"), RootType.Output, "src"); |
| ArchivedTreeArtifact artifact2 = createArchivedTreeArtifact(anotherRoot, "tree2"); |
| SerializationTester tester = |
| new SerializationTester(artifact1, artifact2) |
| .addDependency(FileSystem.class, scratch.getFileSystem()) |
| .addDependency( |
| RootCodecDependencies.class, new RootCodecDependencies(anotherRoot.getRoot())) |
| .addDependencies(SerializationDepsUtils.SERIALIZATION_DEPS_FOR_TEST) |
| .<ArchivedTreeArtifact>setVerificationFunction( |
| (original, deserialized) -> { |
| assertThat(original).isEqualTo(deserialized); |
| assertThat(original.getGeneratingActionKey()) |
| .isEqualTo(deserialized.getGeneratingActionKey()); |
| }); |
| if (useSharedValues) { |
| for (ObjectCodec<? extends Artifact> codec : ArtifactCodecs.VALUE_SHARING_CODECS) { |
| tester.addCodec(codec); |
| } |
| tester.makeMemoizingAndAllowFutureBlocking(/* allowFutureBlocking= */ true); |
| } |
| tester.runTests(); |
| } |
| |
| @Test |
| public void archivedTreeArtifact_getExecPathWithinArchivedArtifactsTree_returnsCorrectPath() { |
| assertThat( |
| ArchivedTreeArtifact.getExecPathWithinArchivedArtifactsTree( |
| PathFragment.create("bazel-out/k8-fastbuild/bin/dir/subdir"))) |
| .isEqualTo( |
| PathFragment.create("bazel-out/:archived_tree_artifacts/k8-fastbuild/bin/dir/subdir")); |
| } |
| |
| private static final PathMapper PATH_MAPPER = |
| new PathMapper() { |
| @Override |
| public PathFragment map(PathFragment execPath) { |
| if (execPath.startsWith(PathFragment.create("output"))) { |
| // output/k8-opt/bin/path/to/pkg/file --> output/<hash>/path/to/pkg/file |
| return execPath |
| .subFragment(0, 1) |
| .getRelative(Integer.toUnsignedString(execPath.subFragment(3).hashCode())) |
| .getRelative(execPath.subFragment(3)); |
| } else { |
| return execPath; |
| } |
| } |
| }; |
| |
| @Test |
| public void mappedArtifact() { |
| StarlarkSemantics semantics = PATH_MAPPER.storeIn(StarlarkSemantics.DEFAULT); |
| |
| Root sourceRoot = Root.fromPath(scratch.getFileSystem().getPath("/some/path")); |
| ArtifactRoot sourceArtifactRoot = ArtifactRoot.asSourceRoot(sourceRoot); |
| Artifact sourceArtifact1 = |
| ActionsTestUtil.createArtifactWithExecPath( |
| sourceArtifactRoot, PathFragment.create("path/to/pkg/file1")); |
| Artifact sourceArtifact2 = |
| ActionsTestUtil.createArtifactWithExecPath( |
| sourceArtifactRoot, PathFragment.create("path/to/pkg/file2")); |
| |
| Path execRoot = scratch.getFileSystem().getPath("/some/path"); |
| ArtifactRoot outputArtifactRoot = |
| ArtifactRoot.asDerivedRoot(execRoot, RootType.Output, "output", "k8-opt", "bin"); |
| Artifact outputArtifact1 = |
| ActionsTestUtil.createArtifactWithExecPath( |
| outputArtifactRoot, PathFragment.create("output/k8-opt/bin/path/to/pkg/file1")); |
| Artifact outputArtifact2 = |
| ActionsTestUtil.createArtifactWithExecPath( |
| outputArtifactRoot, PathFragment.create("output/k8-opt/bin/path/to/pkg/file2")); |
| |
| assertThat(sourceArtifact1.getExecPathStringForStarlark(semantics)) |
| .isEqualTo("path/to/pkg/file1"); |
| assertThat(sourceArtifact1.getDirnameForStarlark(semantics)).isEqualTo("path/to/pkg"); |
| |
| FileRootApi mappedSourceRoot1 = sourceArtifact1.getRootForStarlark(semantics); |
| assertThat(mappedSourceRoot1.getExecPathString()).isEqualTo(""); |
| |
| assertThat(sourceArtifact2.getExecPathStringForStarlark(semantics)) |
| .isEqualTo("path/to/pkg/file2"); |
| assertThat(sourceArtifact2.getDirnameForStarlark(semantics)).isEqualTo("path/to/pkg"); |
| |
| FileRootApi mappedSourceRoot2 = sourceArtifact1.getRootForStarlark(semantics); |
| assertThat(mappedSourceRoot2.getExecPathString()).isEqualTo(""); |
| |
| assertThat(outputArtifact1.getExecPathStringForStarlark(semantics)) |
| .isEqualTo("output/3540078408/path/to/pkg/file1"); |
| assertThat(outputArtifact1.getDirnameForStarlark(semantics)) |
| .isEqualTo("output/3540078408/path/to/pkg"); |
| |
| FileRootApi mappedOutputRoot1 = outputArtifact1.getRootForStarlark(semantics); |
| assertThat(mappedOutputRoot1.getExecPathString()).isEqualTo("output/3540078408"); |
| |
| assertThat(outputArtifact2.getExecPathStringForStarlark(semantics)) |
| .isEqualTo("output/3540078409/path/to/pkg/file2"); |
| assertThat(outputArtifact2.getDirnameForStarlark(semantics)) |
| .isEqualTo("output/3540078409/path/to/pkg"); |
| |
| FileRootApi mappedOutputRoot2 = outputArtifact2.getRootForStarlark(semantics); |
| assertThat(mappedOutputRoot2.getExecPathString()).isEqualTo("output/3540078409"); |
| |
| // Starlark equality uses Object#equals. |
| // Mapped roots are always distinct from non-mapped roots, even if their paths are equal. |
| new EqualsTester() |
| .addEqualityGroup(mappedSourceRoot1, mappedSourceRoot2) |
| .addEqualityGroup(mappedOutputRoot1) |
| .addEqualityGroup(mappedOutputRoot2) |
| .addEqualityGroup(sourceRoot) |
| .addEqualityGroup(outputArtifactRoot) |
| .testEquals(); |
| |
| var starlarkCompare = |
| new Equivalence<Comparable<?>>() { |
| @Override |
| protected boolean doEquivalent(Comparable<?> a, Comparable<?> b) { |
| // Compare a and b in both directions as the implementations of compareTo may be |
| // different. |
| return Starlark.ORDERING.compare(a, b) == 0 && Starlark.ORDERING.compare(b, a) == 0; |
| } |
| |
| @Override |
| protected int doHash(Comparable<?> comparable) { |
| return 0; |
| } |
| }; |
| |
| ClassCastException e = |
| assertThrows( |
| ClassCastException.class, |
| () -> Starlark.ORDERING.compare(mappedOutputRoot1, outputArtifactRoot)); |
| assertThat(e).hasMessageThat().isEqualTo("unsupported comparison: mapped_root <=> root"); |
| |
| EquivalenceTester.of(starlarkCompare) |
| .addEquivalenceGroup((Comparable) mappedSourceRoot1, (Comparable) mappedSourceRoot2) |
| .addEquivalenceGroup((Comparable) mappedOutputRoot1) |
| .addEquivalenceGroup((Comparable) mappedOutputRoot2) |
| .test(); |
| } |
| |
| private static SpecialArtifact createTreeArtifact(ArtifactRoot root, String relativePath) { |
| return createTreeArtifact(root, relativePath, ActionsTestUtil.NULL_ACTION_LOOKUP_DATA); |
| } |
| |
| private static SpecialArtifact createTreeArtifact( |
| ArtifactRoot root, String relativePath, ActionLookupData actionLookupData) { |
| SpecialArtifact treeArtifact = |
| SpecialArtifact.create( |
| root, |
| root.getExecPath().getRelative(relativePath), |
| actionLookupData.getActionLookupKey(), |
| SpecialArtifactType.TREE); |
| treeArtifact.setGeneratingActionKey(actionLookupData); |
| return treeArtifact; |
| } |
| |
| private static ArchivedTreeArtifact createArchivedTreeArtifact( |
| ArtifactRoot root, String treeRelativePath) { |
| return ArchivedTreeArtifact.createForTree(createTreeArtifact(root, treeRelativePath)); |
| } |
| } |