// 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 org.junit.Assert.fail; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.testing.NullPointerTester; import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; 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.analysis.actions.CommandLine; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.CustomArgv; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.CustomMultiArgv; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.testutil.Scratch; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Collection; import javax.annotation.Nullable; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for CustomCommandLine. */ @RunWith(JUnit4.class) public class CustomCommandLineTest { private Scratch scratch; private Root rootDir; private Artifact artifact1; private Artifact artifact2; @Before public void createArtifacts() throws Exception { scratch = new Scratch(); rootDir = Root.asDerivedRoot(scratch.dir("/exec/root")); artifact1 = new Artifact(scratch.file("/exec/root/dir/file1.txt"), rootDir); artifact2 = new Artifact(scratch.file("/exec/root/dir/file2.txt"), rootDir); } @Test public void testStringArgs() { CustomCommandLine cl = CustomCommandLine.builder().add("--arg1").add("--arg2").build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("--arg1", "--arg2")); } @Test public void testLabelArgs() throws LabelSyntaxException { CustomCommandLine cl = CustomCommandLine.builder().add(Label.parseAbsolute("//a:b")).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("//a:b")); } @Test public void testStringsArgs() { CustomCommandLine cl = CustomCommandLine.builder().add("--arg", ImmutableList.of("a", "b")).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("--arg", "a", "b")); } @Test public void testArtifactJoinStringArgs() { CustomCommandLine cl = CustomCommandLine.builder().addJoinStrings("--path", ":", ImmutableList.of("foo", "bar")).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("--path", "foo:bar")); } @Test public void testJoinValues() { CustomCommandLine cl = CustomCommandLine.builder() .addJoinValues( "--path", ":", ImmutableList.of("foo", "bar", "baz"), new Function() { @Nullable @Override public String apply(@Nullable String s) { return s.toUpperCase(); } }) .build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("--path", "FOO:BAR:BAZ")); } @Test public void testArtifactExecPathArgs() { CustomCommandLine cl = CustomCommandLine.builder().addExecPath("--path", artifact1).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("--path", "dir/file1.txt")); } @Test public void testArtifactExecPathsArgs() { CustomCommandLine cl = CustomCommandLine.builder().addExecPaths("--path", ImmutableList.of(artifact1, artifact2)).build(); assertThat(cl.arguments()) .isEqualTo(ImmutableList.of("--path", "dir/file1.txt", "dir/file2.txt")); } @Test public void testNestedSetArtifactExecPathsArgs() { CustomCommandLine cl = CustomCommandLine.builder().addExecPaths( NestedSetBuilder.stableOrder().add(artifact1).add(artifact2).build()).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("dir/file1.txt", "dir/file2.txt")); } @Test public void testArtifactJoinExecPathArgs() { CustomCommandLine cl = CustomCommandLine.builder().addJoinExecPaths("--path", ":", ImmutableList.of(artifact1, artifact2)).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("--path", "dir/file1.txt:dir/file2.txt")); } @Test public void testPathArgs() { CustomCommandLine cl = CustomCommandLine.builder().addPath(artifact1.getExecPath()).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("dir/file1.txt")); } @Test public void testJoinPathArgs() { CustomCommandLine cl = CustomCommandLine.builder().addJoinPaths(":", ImmutableList.of(artifact1.getExecPath(), artifact2.getExecPath())).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("dir/file1.txt:dir/file2.txt")); } @Test public void testPathsArgs() { CustomCommandLine cl = CustomCommandLine.builder().addPaths("%s:%s", artifact1.getExecPath(), artifact1.getRootRelativePath()).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("dir/file1.txt:dir/file1.txt")); } @Test public void testCustomArgs() { CustomCommandLine cl = CustomCommandLine.builder().add(new CustomArgv() { @Override public String argv() { return "--arg"; } }).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("--arg")); } @Test public void testCustomMultiArgs() { CustomCommandLine cl = CustomCommandLine.builder().add(new CustomMultiArgv() { @Override public ImmutableList argv() { return ImmutableList.of("--arg1", "--arg2"); } }).build(); assertThat(cl.arguments()).isEqualTo(ImmutableList.of("--arg1", "--arg2")); } @Test public void testCombinedArgs() { CustomCommandLine cl = CustomCommandLine.builder() .add("--arg") .add("--args", ImmutableList.of("abc")) .addExecPaths("--path1", ImmutableList.of(artifact1)) .addExecPath("--path2", artifact2) .build(); assertThat(cl.arguments()) .isEqualTo( ImmutableList.of( "--arg", "--args", "abc", "--path1", "dir/file1.txt", "--path2", "dir/file2.txt")); } @Test public void testAddNulls() throws Exception { Artifact treeArtifact = createTreeArtifact("myTreeArtifact"); assertThat(treeArtifact).isNotNull(); CustomCommandLine cl = CustomCommandLine.builder() .add((CharSequence) null) .add((Label) null) .add("foo", null) .add("foo", ImmutableList.of()) .add((Iterable) null) .add(ImmutableList.of()) .addExecPath("foo", null) .addExecPaths("foo", (Iterable) null) .addExecPaths("foo", ImmutableList.of()) .addExecPaths(null) .addExecPaths(ImmutableList.of()) .addPlaceholderTreeArtifactExecPath("foo", null) .addJoinStrings("foo", "bar", null) .addJoinStrings("foo", "bar", ImmutableList.of()) .addJoinValues("foo", "bar", null, String::toString) .addJoinValues("foo", "bar", ImmutableList.of(), String::toString) .addJoinExecPaths("foo", "bar", null) .addJoinExecPaths("foo", "bar", ImmutableList.of()) .addPath(null) .addPlaceholderTreeArtifactFormattedExecPath("foo", null) .addJoinPaths("foo", null) .addJoinPaths("foo", ImmutableList.of()) .addBeforeEachPath("foo", null) .addBeforeEachPath("foo", ImmutableList.of()) .addBeforeEach("foo", null) .addBeforeEach("foo", ImmutableList.of()) .addBeforeEachExecPath("foo", null) .addBeforeEachExecPath("foo", ImmutableList.of()) .addFormatEach("%s", null) .addFormatEach("%s", ImmutableList.of()) .add((CustomArgv) null) .add((CustomMultiArgv) null) .build(); assertThat(cl.arguments()).isEmpty(); assertThat( CustomCommandLine.builder() .addPaths("foo") .addPaths("bar", (PathFragment[]) null) .addPaths("baz", new PathFragment[0]) .build() .arguments()) .containsExactly("foo", "baz") .inOrder(); CustomCommandLine.Builder obj = CustomCommandLine.builder(); Class clazz = CustomCommandLine.Builder.class; NullPointerTester npt = new NullPointerTester() .setDefault(Artifact.class, artifact1) .setDefault(String.class, "foo") .setDefault(PathFragment[].class, new PathFragment[] {PathFragment.create("foo")}); npt.testMethod(obj, clazz.getMethod("add", String.class, Iterable.class)); npt.testMethod(obj, clazz.getMethod("addExecPath", String.class, Artifact.class)); npt.testMethod(obj, clazz.getMethod("addExecPaths", String.class, Iterable.class)); npt.testMethod( obj, clazz.getMethod("addPlaceholderTreeArtifactExecPath", String.class, Artifact.class)); npt.testMethod( obj, clazz.getMethod( "addPlaceholderTreeArtifactFormattedExecPath", String.class, Artifact.class)); npt.testMethod(obj, clazz.getMethod("addParamFile", String.class, Artifact.class)); npt.testMethod(obj, clazz.getMethod("addPaths", String.class, PathFragment[].class)); npt.testMethod( obj, clazz.getMethod("addJoinExpandedTreeArtifactExecPath", String.class, Artifact.class)); npt.testMethod(obj, clazz.getMethod("addExpandedTreeArtifactExecPaths", Artifact.class)); npt.setDefault(Iterable.class, ImmutableList.of("foo")); npt.testMethod( obj, clazz.getMethod("addJoinStrings", String.class, String.class, Iterable.class)); npt.testMethod( obj, clazz.getMethod( "addJoinValues", String.class, String.class, Iterable.class, Function.class)); npt.testMethod(obj, clazz.getMethod("addBeforeEach", String.class, Iterable.class)); npt.testMethod(obj, clazz.getMethod("addFormatEach", String.class, Iterable.class)); npt.setDefault(Iterable.class, ImmutableList.of(artifact1)); npt.testMethod( obj, clazz.getMethod("addJoinExecPaths", String.class, String.class, Iterable.class)); npt.testMethod(obj, clazz.getMethod("addBeforeEachExecPath", String.class, Iterable.class)); npt.setDefault(Iterable.class, ImmutableList.of(PathFragment.create("foo"))); npt.testMethod(obj, clazz.getMethod("addJoinPaths", String.class, Iterable.class)); npt.testMethod(obj, clazz.getMethod("addBeforeEachPath", String.class, Iterable.class)); npt.setDefault(Artifact.class, treeArtifact); npt.testMethod( obj, clazz.getMethod("addJoinExpandedTreeArtifactExecPath", String.class, Artifact.class)); npt.testMethod(obj, clazz.getMethod("addExpandedTreeArtifactExecPaths", Artifact.class)); } @Test public void testTreeFileArtifactExecPathArgs() { Artifact treeArtifactOne = createTreeArtifact("myArtifact/treeArtifact1"); Artifact treeArtifactTwo = createTreeArtifact("myArtifact/treeArtifact2"); CustomCommandLine commandLineTemplate = CustomCommandLine.builder() .addPlaceholderTreeArtifactExecPath("--argOne", treeArtifactOne) .addPlaceholderTreeArtifactExecPath("--argTwo", treeArtifactTwo) .build(); TreeFileArtifact treeFileArtifactOne = createTreeFileArtifact( treeArtifactOne, "children/child1"); TreeFileArtifact treeFileArtifactTwo = createTreeFileArtifact( treeArtifactTwo, "children/child2"); CustomCommandLine commandLine = commandLineTemplate.evaluateTreeFileArtifacts( ImmutableList.of(treeFileArtifactOne, treeFileArtifactTwo)); assertThat(commandLine.arguments()) .containsExactly( "--argOne", "myArtifact/treeArtifact1/children/child1", "--argTwo", "myArtifact/treeArtifact2/children/child2") .inOrder(); } @Test public void testTreeFileArtifactExecPathWithTemplateArgs() { Artifact treeArtifact = createTreeArtifact("myArtifact/treeArtifact1"); CustomCommandLine commandLineTemplate = CustomCommandLine.builder() .addPlaceholderTreeArtifactFormattedExecPath("path:%s", treeArtifact) .build(); TreeFileArtifact treeFileArtifact = createTreeFileArtifact( treeArtifact, "children/child1"); CustomCommandLine commandLine = commandLineTemplate.evaluateTreeFileArtifacts( ImmutableList.of(treeFileArtifact)); assertThat(commandLine.arguments()).containsExactly( "path:myArtifact/treeArtifact1/children/child1"); } @Test public void testTreeFileArtifactArgThrowWithoutSubstitution() { Artifact treeArtifactOne = createTreeArtifact("myArtifact/treeArtifact1"); Artifact treeArtifactTwo = createTreeArtifact("myArtifact/treeArtifact2"); CustomCommandLine commandLineTemplate = CustomCommandLine.builder() .addPlaceholderTreeArtifactExecPath("--argOne", treeArtifactOne) .addPlaceholderTreeArtifactExecPath("--argTwo", treeArtifactTwo) .build(); try { commandLineTemplate.arguments(); fail("No substitution map provided, expected NullPointerException"); } catch (NullPointerException e) { // expected } } @Test public void testJoinExpandedTreeArtifactExecPath() { Artifact treeArtifact = createTreeArtifact("myTreeArtifact"); CommandLine commandLine = CustomCommandLine.builder() .add("hello") .addJoinExpandedTreeArtifactExecPath(":", treeArtifact) .build(); assertThat(commandLine.arguments()).containsExactly( "hello", "JoinExpandedTreeArtifactExecPathsArg{ delimiter: :, treeArtifact: myTreeArtifact}"); final Iterable treeFileArtifacts = ImmutableList.of( createTreeFileArtifact(treeArtifact, "children/child1"), createTreeFileArtifact(treeArtifact, "children/child2")); ArtifactExpander artifactExpander = new ArtifactExpander() { @Override public void expand(Artifact artifact, Collection output) { for (TreeFileArtifact treeFileArtifact : treeFileArtifacts) { if (treeFileArtifact.getParent().equals(artifact)) { output.add(treeFileArtifact); } } } }; assertThat(commandLine.arguments(artifactExpander)).containsExactly( "hello", "myTreeArtifact/children/child1:myTreeArtifact/children/child2"); } private Artifact createTreeArtifact(String rootRelativePath) { PathFragment relpath = PathFragment.create(rootRelativePath); return new SpecialArtifact( rootDir.getPath().getRelative(relpath), rootDir, rootDir.getExecPath().getRelative(relpath), ArtifactOwner.NULL_OWNER, SpecialArtifactType.TREE); } private TreeFileArtifact createTreeFileArtifact( Artifact inputTreeArtifact, String parentRelativePath) { return ActionInputHelper.treeFileArtifact( inputTreeArtifact, PathFragment.create(parentRelativePath)); } }