| // 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.cmdline; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.assertThrows; |
| |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.testing.EqualsTester; |
| import com.google.devtools.build.lib.cmdline.Label.PackageContext; |
| import com.google.devtools.build.lib.cmdline.Label.RepoContext; |
| import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import java.util.regex.Pattern; |
| import net.starlark.java.eval.Starlark; |
| import net.starlark.java.eval.StarlarkSemantics; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests for {@link Label}. */ |
| @RunWith(JUnit4.class) |
| public class LabelTest { |
| |
| private static final String INVALID_TARGET_NAME = "invalid target name"; |
| private static final String INVALID_PACKAGE_NAME = "invalid package name"; |
| |
| @Test |
| public void testAbsolute() throws Exception { |
| { |
| Label l = Label.parseCanonical("//foo/bar:baz"); |
| assertThat(l.getPackageName()).isEqualTo("foo/bar"); |
| assertThat(l.getName()).isEqualTo("baz"); |
| } |
| { |
| Label l = Label.parseCanonical("//foo/bar"); |
| assertThat(l.getPackageName()).isEqualTo("foo/bar"); |
| assertThat(l.getName()).isEqualTo("bar"); |
| } |
| { |
| Label l = Label.parseCanonical("//:bar"); |
| assertThat(l.getPackageName()).isEmpty(); |
| assertThat(l.getName()).isEqualTo("bar"); |
| } |
| { |
| Label l = Label.parseCanonical("@foo"); |
| assertThat(l.getRepository().getName()).isEqualTo("foo"); |
| assertThat(l.getPackageName()).isEmpty(); |
| assertThat(l.getName()).isEqualTo("foo"); |
| } |
| { |
| Label l = Label.parseCanonical("@foo//bar"); |
| assertThat(l.getRepository().getName()).isEqualTo("foo"); |
| assertThat(l.getPackageName()).isEqualTo("bar"); |
| assertThat(l.getName()).isEqualTo("bar"); |
| } |
| { |
| Label l = Label.parseCanonical("@@foo//bar"); |
| assertThat(l.getRepository().getName()).isEqualTo("foo"); |
| assertThat(l.getPackageName()).isEqualTo("bar"); |
| assertThat(l.getName()).isEqualTo("bar"); |
| } |
| { |
| Label l = Label.parseCanonical("//@foo"); |
| assertThat(l.getRepository()).isEqualTo(RepositoryName.MAIN); |
| assertThat(l.getPackageName()).isEqualTo("@foo"); |
| assertThat(l.getName()).isEqualTo("@foo"); |
| } |
| { |
| Label l = Label.parseCanonical("//xyz/@foo:abc"); |
| assertThat(l.getRepository()).isEqualTo(RepositoryName.MAIN); |
| assertThat(l.getPackageName()).isEqualTo("xyz/@foo"); |
| assertThat(l.getName()).isEqualTo("abc"); |
| } |
| } |
| |
| @Test |
| public void parseWithRepoContext() throws Exception { |
| RepositoryName foo = RepositoryName.createUnvalidated("foo"); |
| RepositoryName bar = RepositoryName.createUnvalidated("bar"); |
| RepositoryName quux = RepositoryName.createUnvalidated("quux"); |
| RepoContext repoContext = |
| RepoContext.of(foo, RepositoryMapping.create(ImmutableMap.of("bar", quux), foo)); |
| { |
| Label l = Label.parseWithRepoContext("//lol:kek", repoContext); |
| assertThat(l.getRepository()).isEqualTo(foo); |
| assertThat(l.getPackageName()).isEqualTo("lol"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| { |
| Label l = Label.parseWithRepoContext("@bar//lol:kek", repoContext); |
| assertThat(l.getRepository()).isEqualTo(quux); |
| assertThat(l.getPackageName()).isEqualTo("lol"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| { |
| Label l = Label.parseWithRepoContext("@@bar//lol:kek", repoContext); |
| assertThat(l.getRepository()).isEqualTo(bar); |
| assertThat(l.getPackageName()).isEqualTo("lol"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| { |
| Label l = Label.parseWithRepoContext("@quux//lol:kek", repoContext); |
| assertThat(l.getRepository()).isEqualTo(quux.toNonVisible(foo)); |
| assertThat(l.getPackageName()).isEqualTo("lol"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| } |
| |
| @Test |
| public void parseWithPackageContext() throws Exception { |
| RepositoryName foo = RepositoryName.createUnvalidated("foo"); |
| RepositoryName bar = RepositoryName.createUnvalidated("bar"); |
| RepositoryName quux = RepositoryName.createUnvalidated("quux"); |
| PackageContext packageContext = |
| PackageContext.of( |
| PackageIdentifier.create(foo, PathFragment.create("hah")), |
| RepositoryMapping.create(ImmutableMap.of("bar", quux), foo)); |
| { |
| Label l = Label.parseWithPackageContext(":kek", packageContext); |
| assertThat(l.getRepository()).isEqualTo(foo); |
| assertThat(l.getPackageName()).isEqualTo("hah"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| { |
| Label l = Label.parseWithPackageContext("//lol:kek", packageContext); |
| assertThat(l.getRepository()).isEqualTo(foo); |
| assertThat(l.getPackageName()).isEqualTo("lol"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| { |
| Label l = Label.parseWithPackageContext("@bar//lol:kek", packageContext); |
| assertThat(l.getRepository()).isEqualTo(quux); |
| assertThat(l.getPackageName()).isEqualTo("lol"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| { |
| Label l = Label.parseWithPackageContext("@@bar//lol:kek", packageContext); |
| assertThat(l.getRepository()).isEqualTo(bar); |
| assertThat(l.getPackageName()).isEqualTo("lol"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| { |
| Label l = Label.parseWithPackageContext("@quux//lol:kek", packageContext); |
| assertThat(l.getRepository()).isEqualTo(quux.toNonVisible(foo)); |
| assertThat(l.getPackageName()).isEqualTo("lol"); |
| assertThat(l.getName()).isEqualTo("kek"); |
| } |
| } |
| |
| @Test |
| public void testFactory() throws Exception { |
| Label l = Label.create("foo/bar", "quux"); |
| assertThat(l.getPackageName()).isEqualTo("foo/bar"); |
| assertThat(l.getName()).isEqualTo("quux"); |
| } |
| |
| @Test |
| public void testIdentities() throws Exception { |
| |
| Label l1 = Label.parseCanonical("//foo/bar:baz"); |
| Label l2 = Label.parseCanonical("//foo/bar:baz"); |
| Label l3 = Label.parseCanonical("//foo/bar:quux"); |
| |
| new EqualsTester().addEqualityGroup(l1, l2).addEqualityGroup(l3).testEquals(); |
| } |
| |
| @Test |
| public void testToString() throws Exception { |
| { |
| String s = "@//foo/bar:baz"; |
| Label l = Label.parseCanonical(s); |
| assertThat(l.toString()).isEqualTo("//foo/bar:baz"); |
| } |
| { |
| Label l = Label.parseCanonical("//foo/bar"); |
| assertThat(l.toString()).isEqualTo("//foo/bar:bar"); |
| } |
| { |
| Label l = Label.parseCanonical("@foo"); |
| assertThat(l.toString()).isEqualTo("@foo//:foo"); |
| } |
| } |
| |
| @Test |
| public void testToShorthandString() throws Exception { |
| { |
| Label l = Label.parseCanonical("//bar/baz:baz"); |
| assertThat(l.toShorthandString()).isEqualTo("//bar/baz"); |
| } |
| { |
| Label l = Label.parseCanonical("//bar/baz:bat"); |
| assertThat(l.toShorthandString()).isEqualTo("//bar/baz:bat"); |
| } |
| { |
| Label l = Label.parseCanonical("@foo//bar/baz:baz"); |
| assertThat(l.toShorthandString()).isEqualTo("@foo//bar/baz"); |
| } |
| { |
| Label l = Label.parseCanonical("@foo//bar/baz:bat"); |
| assertThat(l.toShorthandString()).isEqualTo("@foo//bar/baz:bat"); |
| } |
| } |
| |
| @Test |
| public void testDotDot() throws Exception { |
| Label.parseCanonical("//foo/bar:baz..gif"); |
| } |
| |
| /** |
| * Asserts that creating a label throws a SyntaxException. |
| * |
| * @param label the label to create. |
| */ |
| private static void assertSyntaxError(String expectedError, String label) { |
| LabelSyntaxException e = |
| assertThrows( |
| "Label '" + label + "' did not contain a syntax error, but was expected to", |
| LabelSyntaxException.class, |
| () -> Label.parseCanonical(label)); |
| assertThat(e).hasMessageThat().containsMatch(Pattern.quote(expectedError)); |
| } |
| |
| @Test |
| public void testBadCharacters() throws Exception { |
| assertSyntaxError("target names may not contain ':'", "//foo:bar:baz"); |
| assertSyntaxError("target names may not contain ':'", "//foo:bar:"); |
| assertSyntaxError("target names may not contain ':'", "//foo/bar::"); |
| } |
| |
| @Test |
| public void testUplevelReferences() throws Exception { |
| assertSyntaxError(INVALID_PACKAGE_NAME, "//foo/bar/..:baz"); |
| assertSyntaxError(INVALID_PACKAGE_NAME, "//foo/../baz:baz"); |
| assertSyntaxError(INVALID_PACKAGE_NAME, "//../bar/baz:baz"); |
| assertSyntaxError(INVALID_PACKAGE_NAME, "//..:foo"); |
| assertSyntaxError(INVALID_TARGET_NAME, "//foo:bar/../baz"); |
| assertSyntaxError(INVALID_TARGET_NAME, "//foo:../bar/baz"); |
| assertSyntaxError(INVALID_TARGET_NAME, "//foo:bar/baz/.."); |
| assertSyntaxError(INVALID_TARGET_NAME, "//foo:.."); |
| } |
| |
| @Test |
| public void testDotAsAPathSegment() throws Exception { |
| assertSyntaxError(INVALID_PACKAGE_NAME, "//foo/bar/.:baz"); |
| assertSyntaxError(INVALID_PACKAGE_NAME, "//foo/./baz:baz"); |
| assertSyntaxError(INVALID_PACKAGE_NAME, "//./bar/baz:baz"); |
| assertSyntaxError(INVALID_TARGET_NAME, "//foo:bar/./baz"); |
| assertSyntaxError(INVALID_TARGET_NAME, "//foo:./bar/baz"); |
| // TODO(bazel-team): enable when we have removed the "Workaround" in Label |
| // that rewrites broken Labels by removing the trailing '.' |
| // assertSyntaxError(INVALID_PACKAGE_NAME, |
| // "//foo:bar/baz/."); |
| // assertSyntaxError(INVALID_PACKAGE_NAME, |
| // "//foo:."); |
| } |
| |
| @Test |
| public void testTrailingDotSegment() throws Exception { |
| assertThat(Label.parseCanonical("//foo:dir")).isEqualTo(Label.parseCanonical("//foo:dir/.")); |
| } |
| |
| @Test |
| public void testSomeOtherBadLabels() throws Exception { |
| assertSyntaxError("package names may not end with '/'", "//foo/:bar"); |
| assertSyntaxError("package names may not start with '/'", "///p:foo"); |
| assertSyntaxError("package names may not contain '//' path separators", "//a//b:foo"); |
| } |
| |
| @Test |
| public void testSomeGoodLabels() throws Exception { |
| Label.parseCanonical("//foo:..bar"); |
| Label.parseCanonical("//Foo:..bar"); |
| Label.parseCanonical("//-Foo:..bar"); |
| Label.parseCanonical("//00:..bar"); |
| Label.parseCanonical("//package:foo+bar"); |
| Label.parseCanonical("//package:foo_bar"); |
| Label.parseCanonical("//package:foo=bar"); |
| Label.parseCanonical("//package:foo-bar"); |
| Label.parseCanonical("//package:foo.bar"); |
| Label.parseCanonical("//package:foo@bar"); |
| Label.parseCanonical("//package:foo~bar"); |
| Label.parseCanonical("//$( ):$( )"); |
| } |
| |
| @Test |
| public void testDoubleSlashPathSeparator() throws Exception { |
| assertSyntaxError("package names may not contain '//' path separators", "//foo//bar:baz"); |
| assertSyntaxError("target names may not contain '//' path separator", "//foo:bar//baz"); |
| } |
| |
| @Test |
| public void testNonPrintableCharacters() throws Exception { |
| assertSyntaxError( |
| "target names may not contain non-printable characters: '\\x02'", "//foo:..\002bar"); |
| } |
| |
| /** Make sure that control characters - such as CR - are escaped on output. */ |
| @Test |
| public void testInvalidLineEndings() throws Exception { |
| assertSyntaxError( |
| "invalid target name '..bar\\r': " + "target names may not end with carriage returns", |
| "//foo:..bar\r"); |
| } |
| |
| @Test |
| public void testEmptyName() throws Exception { |
| assertSyntaxError("invalid target name '': empty target name", "//foo/bar:"); |
| } |
| |
| @Test |
| public void testRepoLabel() throws Exception { |
| Label label = Label.parseCanonical("@foo//bar/baz:bat/boo"); |
| assertThat(label.toString()).isEqualTo("@foo//bar/baz:bat/boo"); |
| } |
| |
| @Test |
| public void testNoRepo() throws Exception { |
| Label label = Label.parseCanonical("//bar/baz:bat/boo"); |
| assertThat(label.toString()).isEqualTo("//bar/baz:bat/boo"); |
| } |
| |
| @Test |
| public void testInvalidRepo() throws Exception { |
| LabelSyntaxException e = |
| assertThrows( |
| LabelSyntaxException.class, () -> Label.parseCanonical("foo//bar/baz:bat/boo")); |
| assertThat(e) |
| .hasMessageThat() |
| .isEqualTo( |
| "invalid package name 'foo//bar/baz': package names may not contain '//' path" |
| + " separators"); |
| } |
| |
| @Test |
| public void testInvalidRepoWithColon() throws Exception { |
| LabelSyntaxException e = |
| assertThrows(LabelSyntaxException.class, () -> Label.parseCanonical("@foo:xyz")); |
| assertThat(e) |
| .hasMessageThat() |
| .containsMatch("invalid repository name '@foo:xyz': repo names may contain only"); |
| } |
| |
| @Test |
| public void testGetWorkspaceRoot() throws Exception { |
| Label label = Label.parseCanonical("//bar/baz"); |
| assertThat(label.getWorkspaceRootForStarlarkOnly(StarlarkSemantics.DEFAULT)).isEmpty(); |
| label = Label.parseCanonical("@repo//bar/baz"); |
| assertThat(label.getWorkspaceRootForStarlarkOnly(StarlarkSemantics.DEFAULT)) |
| .isEqualTo("external/repo"); |
| } |
| |
| @Test |
| public void testGetContainingDirectory() { |
| assertThat(Label.getContainingDirectory(Label.parseCanonicalUnchecked("//a:b"))) |
| .isEqualTo(PathFragment.create("a")); |
| assertThat(Label.getContainingDirectory(Label.parseCanonicalUnchecked("//a/b:c"))) |
| .isEqualTo(PathFragment.create("a/b")); |
| assertThat(Label.getContainingDirectory(Label.parseCanonicalUnchecked("//a:b/c"))) |
| .isEqualTo(PathFragment.create("a/b")); |
| assertThat(Label.getContainingDirectory(Label.parseCanonicalUnchecked("//a/b/c"))) |
| .isEqualTo(PathFragment.create("a/b/c")); |
| } |
| |
| @Test |
| public void testWorkspaceName() throws Exception { |
| assertThat(Label.parseCanonical("@foo//bar:baz").getWorkspaceName()).isEqualTo("foo"); |
| assertThat(Label.parseCanonical("//bar:baz").getWorkspaceName()).isEmpty(); |
| assertThat(Label.parseCanonical("@//bar:baz").getWorkspaceName()).isEmpty(); |
| } |
| |
| @Test |
| public void testUnambiguousCanonicalForm() throws Exception { |
| assertThat(Label.parseCanonical("//foo/bar:baz").getUnambiguousCanonicalForm()) |
| .isEqualTo("@@//foo/bar:baz"); |
| assertThat(Label.parseCanonical("@foo//bar:baz").getUnambiguousCanonicalForm()) |
| .isEqualTo("@@foo//bar:baz"); |
| assertThat( |
| Label.create( |
| PackageIdentifier.create( |
| RepositoryName.create("foo").toNonVisible(RepositoryName.create("bar")), |
| PathFragment.create("baz")), |
| "quux") |
| .getUnambiguousCanonicalForm()) |
| .isEqualTo("@@[unknown repo 'foo' requested from @bar]//baz:quux"); |
| } |
| |
| @Test |
| public void starlarkStrAndRepr() throws Exception { |
| Label label = Label.parseCanonical("//x"); |
| assertThat(Starlark.str(label, StarlarkSemantics.DEFAULT)).isEqualTo("@//x:x"); |
| assertThat(Starlark.repr(label)).isEqualTo("Label(\"//x:x\")"); |
| |
| label = Label.parseCanonical("@hello//x"); |
| assertThat(Starlark.str(label, StarlarkSemantics.DEFAULT)).isEqualTo("@hello//x:x"); |
| assertThat(Starlark.repr(label)).isEqualTo("Label(\"@hello//x:x\")"); |
| } |
| |
| @Test |
| public void starlarkStr_ambiguous() throws Exception { |
| StarlarkSemantics semantics = |
| StarlarkSemantics.builder() |
| .setBool(BuildLanguageOptions.INCOMPATIBLE_UNAMBIGUOUS_LABEL_STRINGIFICATION, false) |
| .build(); |
| assertThat(Starlark.str(Label.parseCanonical("//x"), semantics)).isEqualTo("//x:x"); |
| assertThat(Starlark.str(Label.parseCanonical("@x//y"), semantics)).isEqualTo("@x//y:y"); |
| } |
| |
| @Test |
| public void starlarkStr_canonicalLabelLiteral() throws Exception { |
| StarlarkSemantics semantics = |
| StarlarkSemantics.builder().setBool(BuildLanguageOptions.ENABLE_BZLMOD, true).build(); |
| assertThat(Starlark.str(Label.parseCanonical("//x"), semantics)).isEqualTo("@@//x:x"); |
| assertThat(Starlark.str(Label.parseCanonical("@x//y"), semantics)).isEqualTo("@@x//y:y"); |
| } |
| |
| @Test |
| public void starlarkStr_ambiguousAndCanonicalLabelLiteral() throws Exception { |
| StarlarkSemantics semantics = |
| StarlarkSemantics.builder() |
| .setBool(BuildLanguageOptions.INCOMPATIBLE_UNAMBIGUOUS_LABEL_STRINGIFICATION, false) |
| .setBool(BuildLanguageOptions.ENABLE_BZLMOD, true) |
| .build(); |
| assertThat(Starlark.str(Label.parseCanonical("//x"), semantics)).isEqualTo("//x:x"); |
| assertThat(Starlark.str(Label.parseCanonical("@x//y"), semantics)).isEqualTo("@@x//y:y"); |
| } |
| } |