blob: ba35d5dff17dabe5b42cb1aa24db4f03d5562f4e [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.analysis.actions;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.NULL_ACTION_OWNER;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionExecutionContext.LostInputsCheck;
import com.google.devtools.build.lib.actions.ActionInputPrefetcher;
import com.google.devtools.build.lib.actions.ActionKeyContext;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.actions.Executor;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.ServerDirectories;
import com.google.devtools.build.lib.collect.nestedset.NestedSetExpander;
import com.google.devtools.build.lib.events.StoredEventHandler;
import com.google.devtools.build.lib.exec.BinTools;
import com.google.devtools.build.lib.exec.util.TestExecutorBuilder;
import com.google.devtools.build.lib.testutil.FoundationTestCase;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.Root;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests {@link TemplateExpansionAction}.
*/
@RunWith(JUnit4.class)
public class TemplateExpansionActionTest extends FoundationTestCase {
private static final String TEMPLATE = Joiner.on('\n').join("key=%key%", "value=%value%");
private static final String SPECIAL_CHARS = "Š©±½_strøget";
private ArtifactRoot outputRoot;
private Artifact inputArtifact;
private Artifact outputArtifact;
private Path output;
private List<Substitution> substitutions;
private BlazeDirectories directories;
private BinTools binTools;
private final ActionKeyContext actionKeyContext = new ActionKeyContext();
@Before
public final void createDirectoriesAndTools() throws Exception {
createArtifacts(TEMPLATE);
substitutions = Lists.newArrayList();
substitutions.add(Substitution.of("%key%", "foo"));
substitutions.add(Substitution.of("%value%", "bar"));
directories =
new BlazeDirectories(
new ServerDirectories(
scratch.resolve("/install"),
scratch.resolve("/base"),
scratch.resolve("/userRoot")),
scratch.resolve("/workspace"),
/* defaultSystemJavabase= */ null,
"mock-product-name");
binTools = BinTools.empty(directories);
}
private void createArtifacts(String template) throws Exception {
ArtifactRoot workspace = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/workspace")));
scratch.dir("/workspace/out");
outputRoot = ArtifactRoot.asDerivedRoot(scratch.dir("/workspace"), "out");
Path input = scratch.overwriteFile("/workspace/input.txt", StandardCharsets.UTF_8, template);
inputArtifact = ActionsTestUtil.createArtifact(workspace, input);
output = scratch.resolve("/workspace/out/destination.txt");
outputArtifact = ActionsTestUtil.createArtifact(outputRoot, output);
}
private TemplateExpansionAction create() {
TemplateExpansionAction result = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact, Template.forString(TEMPLATE), substitutions, false);
return result;
}
@Test
public void testInputsIsEmpty() {
assertThat(create().getInputs().toList()).isEmpty();
}
@Test
public void testDestinationArtifactIsOutput() {
assertThat(create().getOutputs()).containsExactly(outputArtifact);
}
@Test
public void testExpansion() throws Exception {
Executor executor = new TestExecutorBuilder(fileSystem, directories, binTools).build();
create().execute(createContext(executor));
String content = new String(FileSystemUtils.readContentAsLatin1(output));
String expected = Joiner.on('\n').join("key=foo", "value=bar");
assertThat(content).isEqualTo(expected);
}
@Test
public void testKeySameIfSame() throws Exception {
Artifact outputArtifact2 =
ActionsTestUtil.createArtifact(
outputRoot, scratch.resolve("/workspace/out/destination.txt"));
TemplateExpansionAction a = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact, Template.forString(TEMPLATE),
ImmutableList.of(Substitution.of("%key%", "foo")), false);
TemplateExpansionAction b = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact2, Template.forString(TEMPLATE),
ImmutableList.of(Substitution.of("%key%", "foo")), false);
assertThat(computeKey(a)).isEqualTo(computeKey(b));
}
@Test
public void testKeyDiffersForSubstitution() throws Exception {
Artifact outputArtifact2 =
ActionsTestUtil.createArtifact(
outputRoot, scratch.resolve("/workspace/out/destination.txt"));
TemplateExpansionAction a = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact, Template.forString(TEMPLATE),
ImmutableList.of(Substitution.of("%key%", "foo")), false);
TemplateExpansionAction b = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact2, Template.forString(TEMPLATE),
ImmutableList.of(Substitution.of("%key%", "foo2")), false);
assertThat(computeKey(a)).isNotEqualTo(computeKey(b));
}
@Test
public void testKeyDiffersForExecutable() throws Exception {
Artifact outputArtifact2 =
ActionsTestUtil.createArtifact(
outputRoot, scratch.resolve("/workspace/out/destination.txt"));
TemplateExpansionAction a = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact, Template.forString(TEMPLATE),
ImmutableList.of(Substitution.of("%key%", "foo")), false);
TemplateExpansionAction b = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact2, Template.forString(TEMPLATE),
ImmutableList.of(Substitution.of("%key%", "foo")), true);
assertThat(computeKey(a)).isNotEqualTo(computeKey(b));
}
@Test
public void testKeyDiffersForTemplates() throws Exception {
Artifact outputArtifact2 =
ActionsTestUtil.createArtifact(
outputRoot, scratch.resolve("/workspace/out/destination.txt"));
TemplateExpansionAction a = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact, Template.forString(TEMPLATE),
ImmutableList.of(Substitution.of("%key%", "foo")), false);
TemplateExpansionAction b = new TemplateExpansionAction(NULL_ACTION_OWNER,
outputArtifact2, Template.forString(TEMPLATE + " "),
ImmutableList.of(Substitution.of("%key%", "foo")), false);
assertThat(computeKey(a)).isNotEqualTo(computeKey(b));
}
private TemplateExpansionAction createWithArtifact() {
return createWithArtifact(substitutions);
}
private TemplateExpansionAction createWithArtifact(List<Substitution> substitutions) {
TemplateExpansionAction result = new TemplateExpansionAction(
NULL_ACTION_OWNER, inputArtifact, outputArtifact, substitutions, false);
return result;
}
private ActionExecutionContext createContext(Executor executor) {
return new ActionExecutionContext(
executor,
/*actionInputFileCache=*/ null,
ActionInputPrefetcher.NONE,
actionKeyContext,
/*metadataHandler=*/ null,
LostInputsCheck.NONE,
new FileOutErr(),
new StoredEventHandler(),
/*clientEnv=*/ ImmutableMap.of(),
/*topLevelFilesets=*/ ImmutableMap.of(),
/*artifactExpander=*/ null,
/*actionFileSystem=*/ null,
/*skyframeDepsResult=*/ null,
NestedSetExpander.DEFAULT);
}
private void executeTemplateExpansion(String expected) throws Exception {
executeTemplateExpansion(expected, substitutions);
}
private void executeTemplateExpansion(String expected, List<Substitution> substitutions)
throws Exception {
Executor executor = new TestExecutorBuilder(fileSystem, directories, binTools).build();
createWithArtifact(substitutions).execute(createContext(executor));
String actual = FileSystemUtils.readContent(output, StandardCharsets.UTF_8);
assertThat(actual).isEqualTo(expected);
}
@Test
public void testArtifactTemplateHasInput() {
assertThat(createWithArtifact().getInputs().toList()).containsExactly(inputArtifact);
}
@Test
public void testArtifactTemplateHasOutput() {
assertThat(createWithArtifact().getOutputs()).containsExactly(outputArtifact);
}
@Test
public void testArtifactTemplateExpansion() throws Exception {
// The trailing "" is needed because scratch.overwriteFile implicitly appends "\n".
String expected = Joiner.on('\n').join("key=foo", "value=bar", "");
executeTemplateExpansion(expected);
}
@Test
public void testWithSpecialCharacters() throws Exception {
// We have to overwrite the artifacts since we need our template in "inputs"
createArtifacts(SPECIAL_CHARS + "%key%");
// scratch.overwriteFile appends a newline, so we need an additional \n here
String expected = String.format("%s%s\n", SPECIAL_CHARS, SPECIAL_CHARS);
executeTemplateExpansion(expected, ImmutableList.of(Substitution.of("%key%", SPECIAL_CHARS)));
}
private String computeKey(TemplateExpansionAction action) {
Fingerprint fp = new Fingerprint();
action.computeKey(actionKeyContext, fp);
return fp.hexDigestAndReset();
}
}