// 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 org.junit.Assert.assertEquals; import static org.junit.Assert.fail; 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 org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Map; /** * 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)) { 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 { assertEquals(expectedResult, LabelExpander.expand(expressionToExpand, mapping, dummyTarget.getLabel())); } /** * 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(); try { LabelExpander.expand("x1", ImmutableMap.<Label, Iterable<Artifact>>of( labelFor("x1"), ImmutableList.<Artifact>of()), dummyTarget.getLabel()); fail("Expected an exception."); } catch (LabelExpander.NotUniqueExpansionException nuee) { // was expected } try { LabelExpander.expand("x1", ImmutableMap.<Label, Iterable<Artifact>>of( labelFor("x1"), ImmutableList.of(artifactFor("x1"), artifactFor("x2"))), dummyTarget.getLabel()); fail("Expected an exception."); } catch (LabelExpander.NotUniqueExpansionException nuee) { // was expected } } /** * 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")))); } }