| // Copyright 2018 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 static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.fail; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.lib.actions.ActionInput; |
| import com.google.devtools.build.lib.actions.ActionInputHelper; |
| import com.google.devtools.build.lib.actions.ActionInputMap; |
| import com.google.devtools.build.lib.actions.Artifact; |
| 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.ArtifactOwner; |
| import com.google.devtools.build.lib.actions.ArtifactPathResolver; |
| import com.google.devtools.build.lib.actions.ArtifactRoot; |
| import com.google.devtools.build.lib.actions.FileArtifactValue; |
| import com.google.devtools.build.lib.testutil.Scratch; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.devtools.build.lib.vfs.Root; |
| import java.io.FileNotFoundException; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests for {@link ActionMetadataHandler}. */ |
| @RunWith(JUnit4.class) |
| public class ActionMetadataHandlerTest { |
| private Scratch scratch; |
| private ArtifactRoot sourceRoot; |
| private ArtifactRoot outputRoot; |
| |
| @Before |
| public final void setRootDir() throws Exception { |
| scratch = new Scratch(); |
| sourceRoot = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/workspace"))); |
| outputRoot = ArtifactRoot.asDerivedRoot(scratch.dir("/output"), scratch.dir("/output/bin")); |
| } |
| |
| @Test |
| public void withNonArtifactInput() throws Exception { |
| ActionInput input = ActionInputHelper.fromPath("foo/bar"); |
| FileArtifactValue metadata = |
| FileArtifactValue.createNormalFile( |
| new byte[] {1, 2, 3}, /*proxy=*/ null, 10L, /*isShareable=*/ true); |
| ActionInputMap map = new ActionInputMap(1); |
| map.putWithNoDepOwner(input, metadata); |
| assertThat(map.getMetadata(input)).isEqualTo(metadata); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ false, |
| /* outputs= */ ImmutableList.of(), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| assertThat(handler.getMetadata(input)).isNull(); |
| } |
| |
| @Test |
| public void withArtifactInput() throws Exception { |
| PathFragment path = PathFragment.create("src/a"); |
| Artifact artifact = new Artifact(path, sourceRoot); |
| FileArtifactValue metadata = |
| FileArtifactValue.createNormalFile( |
| new byte[] {1, 2, 3}, /*proxy=*/ null, 10L, /*isShareable=*/ true); |
| ActionInputMap map = new ActionInputMap(1); |
| map.putWithNoDepOwner(artifact, metadata); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ false, |
| /* outputs= */ ImmutableList.of(), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| assertThat(handler.getMetadata(artifact)).isEqualTo(metadata); |
| } |
| |
| @Test |
| public void withUnknownSourceArtifactAndNoMissingArtifactsAllowed() throws Exception { |
| PathFragment path = PathFragment.create("src/a"); |
| Artifact artifact = new Artifact(path, sourceRoot); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ false, |
| /* outputs= */ ImmutableList.of(), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| try { |
| handler.getMetadata(artifact); |
| fail(); |
| } catch (IllegalStateException expected) { |
| assertThat(expected).hasMessageThat().contains("null for "); |
| } |
| } |
| |
| @Test |
| public void withUnknownSourceArtifact() throws Exception { |
| PathFragment path = PathFragment.create("src/a"); |
| Artifact artifact = new Artifact(path, sourceRoot); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ true, |
| /* outputs= */ ImmutableList.of(), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| assertThat(handler.getMetadata(artifact)).isNull(); |
| } |
| |
| @Test |
| public void withUnknownOutputArtifactMissingAllowed() throws Exception { |
| PathFragment path = PathFragment.create("foo/bar"); |
| Artifact artifact = new Artifact(path, outputRoot); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ true, |
| /* outputs= */ ImmutableList.of(), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| assertThat(handler.getMetadata(artifact)).isNull(); |
| } |
| |
| @Test |
| public void withUnknownOutputArtifactStatsFile() throws Exception { |
| scratch.file("/output/bin/foo/bar", "not empty"); |
| Artifact artifact = new Artifact(PathFragment.create("foo/bar"), outputRoot); |
| assertThat(artifact.getPath().exists()).isTrue(); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ false, |
| /* outputs= */ ImmutableList.of(artifact), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| assertThat(handler.getMetadata(artifact)).isNotNull(); |
| } |
| |
| @Test |
| public void withUnknownOutputArtifactStatsFileFailsWithException() throws Exception { |
| Artifact artifact = new Artifact(PathFragment.create("foo/bar"), outputRoot); |
| assertThat(artifact.getPath().exists()).isFalse(); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ false, |
| /* outputs= */ ImmutableList.of(artifact), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| try { |
| handler.getMetadata(artifact); |
| fail(); |
| } catch (FileNotFoundException expected) { |
| } |
| } |
| |
| @Test |
| public void withUnknownOutputArtifactMissingDisallowed() throws Exception { |
| PathFragment path = PathFragment.create("foo/bar"); |
| Artifact artifact = new Artifact(path, outputRoot); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ false, |
| /* outputs= */ ImmutableList.of(), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| try { |
| handler.getMetadata(artifact); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test |
| public void withUnknownOutputArtifactMissingAllowedTreeArtifact() throws Exception { |
| PathFragment path = PathFragment.create("bin/foo/bar"); |
| SpecialArtifact treeArtifact = |
| new SpecialArtifact( |
| outputRoot, path, ArtifactOwner.NullArtifactOwner.INSTANCE, SpecialArtifactType.TREE); |
| Artifact artifact = new TreeFileArtifact(treeArtifact, PathFragment.create("baz")); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ true, |
| /* outputs= */ ImmutableList.of(), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| assertThat(handler.getMetadata(artifact)).isNull(); |
| } |
| |
| @Test |
| public void withUnknownOutputArtifactStatsFileTreeArtifact() throws Exception { |
| scratch.file("/output/bin/foo/bar/baz", "not empty"); |
| PathFragment path = PathFragment.create("bin/foo/bar"); |
| SpecialArtifact treeArtifact = |
| new SpecialArtifact( |
| outputRoot, path, ArtifactOwner.NullArtifactOwner.INSTANCE, SpecialArtifactType.TREE); |
| Artifact artifact = new TreeFileArtifact(treeArtifact, PathFragment.create("baz")); |
| assertThat(artifact.getPath().exists()).isTrue(); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ false, |
| /* outputs= */ ImmutableList.of(treeArtifact), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| assertThat(handler.getMetadata(artifact)).isNotNull(); |
| } |
| |
| @Test |
| public void withUnknownOutputArtifactMissingDisallowedTreeArtifact() throws Exception { |
| PathFragment path = PathFragment.create("bin/foo/bar"); |
| SpecialArtifact treeArtifact = |
| new SpecialArtifact( |
| outputRoot, path, ArtifactOwner.NullArtifactOwner.INSTANCE, SpecialArtifactType.TREE); |
| Artifact artifact = new TreeFileArtifact(treeArtifact, PathFragment.create("baz")); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ false, |
| /* outputs= */ ImmutableList.of(), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| try { |
| handler.getMetadata(artifact); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test |
| public void resettingOutputs() throws Exception { |
| scratch.file("/output/bin/foo/bar", "not empty"); |
| PathFragment path = PathFragment.create("foo/bar"); |
| Artifact artifact = new Artifact(path, outputRoot); |
| ActionInputMap map = new ActionInputMap(1); |
| ActionMetadataHandler handler = |
| new ActionMetadataHandler( |
| map, |
| /* missingArtifactsAllowed= */ true, |
| /* outputs= */ ImmutableList.of(artifact), |
| /* tsgm= */ null, |
| ArtifactPathResolver.IDENTITY, |
| new MinimalOutputStore()); |
| handler.discardOutputMetadata(); |
| |
| // The handler doesn't have any info. It'll stat the file and discover that it's 10 bytes long. |
| assertThat(handler.getMetadata(artifact).getSize()).isEqualTo(10); |
| |
| // Inject a remote file of size 42. |
| handler.injectRemoteFile(artifact, new byte[] {1, 2, 3}, 42, 0); |
| assertThat(handler.getMetadata(artifact).getSize()).isEqualTo(42); |
| |
| // Reset this output, which will make the handler stat the file again. |
| handler.resetOutputs(ImmutableList.of(artifact)); |
| assertThat(handler.getMetadata(artifact).getSize()).isEqualTo(10); |
| } |
| } |