blob: 97894ae634938b9a7becd691ead065b95f90059e [file] [log] [blame]
// Copyright 2010 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;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.testutil.Suite;
import com.google.devtools.build.lib.testutil.TestSpec;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link LabelExpander}. */
@TestSpec(size = Suite.SMALL_TESTS)
@RunWith(JUnit4.class)
public class LabelExpanderTest extends BuildViewTestCase {
/**
* A dummy target that resolves labels and receives errors.
*/
private ConfiguredTarget dummyTarget;
/**
* Artifacts generated by {@code dummyTarget} identified by their
* root-relative paths; to be used for mock label-to-artifact mappings.
*/
private Map<String, Artifact> artifactsByName;
/**
* All characters that the heuristic considers to be part of a target.
* This is a subset of the allowed label characters. The ones left out may
* have a special meaning during expression expansion:
*
* <ul>
* <li>comma (",") - may separate labels
* <li>equals sign ("=") - may separate labels
* <li>colon (":") - can only appear in labels, not in target names
* </ul>
*/
private static final String allowedChars = "_/.-+" + PathFragment.SEPARATOR_CHAR
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// Helper methods -----------------------------------------------------------
private void setupDummy() throws Exception {
dummyTarget = scratchConfiguredTarget(
"foo", "foo",
"filegroup(name = 'foo',",
" srcs = ['x1','x2','bar/x3', '" + allowedChars + "', 'xx11', 'x11', 'xx1'])");
collectArtifacts();
}
/**
* Collects all generated mock artifacts for {@code dummyTarget} and assigns
* the result to {@code artifactsByName}.
*/
private void collectArtifacts() {
ImmutableMap.Builder<String, Artifact> builder = ImmutableMap.builder();
for (Artifact artifact : getFilesToBuild(dummyTarget).toList()) {
builder.put(artifact.getRootRelativePath().toString(), artifact);
}
artifactsByName = builder.build();
}
/**
* Gets a generated artifact object for a target in package "foo" from {@code
* artifactsByName}.
*/
private Artifact artifactFor(String targetName) {
return artifactsByName.get("foo/" + targetName);
}
/**
* Creates fake label in package "foo".
*/
private static Label labelFor(String targetName) throws LabelSyntaxException {
return Label.create("@//foo", targetName);
}
/**
* Asserts that an expansion with a given mapping produces the expected
* results.
*/
private void assertExpansion(String expectedResult, String expressionToExpand,
Map<Label, Iterable<Artifact>> mapping) throws Exception {
assertThat(LabelExpander.expand(expressionToExpand, mapping, dummyTarget.getLabel()))
.isEqualTo(expectedResult);
}
/**
* Asserts that an expansion with an empty mapping produces the expected
* results.
*/
private void assertExpansion(String expected, String original) throws Exception {
assertExpansion(expected, original, ImmutableMap.<Label, Iterable<Artifact>>of());
}
// Actual tests -------------------------------------------------------------
/**
* Tests that if no mapping is specified, then strings expand to themselves.
*/
@Test
public void testStringExpandsToItselfWhenNoMappingSpecified() throws Exception {
setupDummy();
assertExpansion("", null);
assertExpansion("cmd", "cmd");
assertExpansion("//x:y,:z,w", "//x:y,:z,w");
assertExpansion(allowedChars, allowedChars);
}
/**
* Tests that in case of a one-to-one label-to-artifact mapping the expansion
* produces the expected results.
*/
@Test
public void testExpansion() throws Exception {
setupDummy();
assertExpansion("foo/x1", "x1", ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
assertExpansion("foo/x1", ":x1", ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
assertExpansion("foo/x1", "//foo:x1", ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
}
/**
* Tests that label extraction works as expected - disallowed label characters
* are resolved to themselves.
*/
@Test
public void testLabelExtraction() throws Exception {
setupDummy();
assertExpansion("(foo/" + allowedChars + ")", "(//foo:" + allowedChars + ")",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor(allowedChars), ImmutableList.of(artifactFor(allowedChars))));
assertExpansion("foo/x1,foo/x2=foo/bar/x3", "x1,x2=bar/x3",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1")),
labelFor("x2"), ImmutableList.of(artifactFor("x2")),
labelFor("bar/x3"), ImmutableList.of(artifactFor("bar/x3"))));
}
/**
* Tests that an exception is thrown when the mapping is not one-to-one.
*/
@Test
public void testThrowsWhenMappingIsNotOneToOne() throws Exception {
setupDummy();
assertThrows(
LabelExpander.NotUniqueExpansionException.class,
() ->
LabelExpander.expand(
"x1",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.<Artifact>of()),
dummyTarget.getLabel()));
assertThrows(
LabelExpander.NotUniqueExpansionException.class,
() ->
LabelExpander.expand(
"x1",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"), artifactFor("x2"))),
dummyTarget.getLabel()));
}
/**
* Tests expanding labels that result in a SyntaxException.
*/
@Test
public void testIllFormedLabels() throws Exception {
setupDummy();
assertExpansion("x1:x2:x3", "x1:x2:x3",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1")),
labelFor("x2"), ImmutableList.of(artifactFor("x2")),
labelFor("bar/x3"), ImmutableList.of(artifactFor("bar/x3"))));
assertExpansion("foo://x1 x1/../x2", "foo://x1 x1/../x2",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1")),
labelFor("x2"), ImmutableList.of(artifactFor("x2")),
labelFor("bar/x3"), ImmutableList.of(artifactFor("bar/x3"))));
assertExpansion("//foo:/x1", "//foo:/x1",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
assertExpansion("//foo:../x1", "//foo:../x1",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
assertExpansion("//foo:x1/../x2", "//foo:x1/../x2",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1")),
labelFor("x2"), ImmutableList.of(artifactFor("x2"))));
assertExpansion("//foo:x1/./x2", "//foo:x1/./x2",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1")),
labelFor("x2"), ImmutableList.of(artifactFor("x2"))));
assertExpansion("//foo:x1//x2", "//foo:x1//x2",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1")),
labelFor("x2"), ImmutableList.of(artifactFor("x2"))));
assertExpansion("//foo:x1/..", "//foo:x1/..",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
assertExpansion("//foo:x1/", "//foo:x1/",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
assertExpansion(":", ":");
}
/**
* Tests that label parsing is greedy (always extracting the longest
* possible label). This means that if a label is a substring of another
* label, it should not be expanded but be treated as part of the longer one.
*/
@Test
public void testLabelIsSubstringOfValidLabel() throws Exception {
setupDummy();
assertExpansion("x3=foo/bar/x3", "x3=bar/x3",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("bar/x3"), ImmutableList.of(artifactFor("bar/x3"))));
assertExpansion("foo/x1,foo/x11,foo/xx1,foo/xx11", "x1,x11,xx1,xx11",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1")),
labelFor("x11"), ImmutableList.of(artifactFor("x11")),
labelFor("xx1"), ImmutableList.of(artifactFor("xx1")),
labelFor("xx11"), ImmutableList.of(artifactFor("xx11"))));
assertExpansion("//x1", "//x1",
ImmutableMap.<Label, Iterable<Artifact>>of(
labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
}
}