diff options
author | Michael Staib <mstaib@google.com> | 2017-02-23 19:06:31 +0000 |
---|---|---|
committer | Irina Iancu <elenairina@google.com> | 2017-02-24 08:29:51 +0000 |
commit | 5e9e1949f4c08ce09665b92aadf7ec7e518aab6a (patch) | |
tree | 16afc722aa4e692a311271e932e7806d5e1f8c7d /src/test/java/com/google/devtools/build | |
parent | 56d66ff8b8616b6ec07c2c604da5d717c0a91aff (diff) |
Add the LABEL_KEYED_STRING_DICT type for attributes.
This enables both native and Skylark rules to declare attributes which
have labels/Targets as keys, and have string values.
--
PiperOrigin-RevId: 148365033
MOS_MIGRATED_REVID=148365033
Diffstat (limited to 'src/test/java/com/google/devtools/build')
4 files changed, 546 insertions, 2 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java b/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java index 6f5012fa83..978f203e86 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java +++ b/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java @@ -53,6 +53,181 @@ public class BuildTypeTest { } @Test + public void testLabelKeyedStringDictConvertsToMapFromLabelToString() throws Exception { + Map<Object, String> input = new ImmutableMap.Builder<Object, String>() + .put("//absolute:label", "absolute value") + .put(":relative", "theory of relativity") + .put("nocolon", "colonial times") + .put("//current/package:explicit", "explicit content") + .put(Label.parseAbsolute("//i/was/already/a/label"), "and that's okay") + .build(); + Label context = Label.parseAbsolute("//current/package:this"); + + Map<Label, String> expected = new ImmutableMap.Builder<Label, String>() + .put(Label.parseAbsolute("//absolute:label"), "absolute value") + .put(Label.parseAbsolute("//current/package:relative"), "theory of relativity") + .put(Label.parseAbsolute("//current/package:nocolon"), "colonial times") + .put(Label.parseAbsolute("//current/package:explicit"), "explicit content") + .put(Label.parseAbsolute("//i/was/already/a/label"), "and that's okay") + .build(); + + assertThat(BuildType.LABEL_KEYED_STRING_DICT.convert(input, null, context)) + .containsExactlyEntriesIn(expected); + } + + @Test + public void testLabelKeyedStringDictConvertingStringShouldFail() throws Exception { + try { + BuildType.LABEL_KEYED_STRING_DICT.convert("//actually/a:label", null, currentRule); + fail("Expected a conversion exception to be thrown."); + } catch (ConversionException expected) { + assertThat(expected) + .hasMessage( + "expected value of type 'dict(label, string)', " + + "but got \"//actually/a:label\" (string)"); + } + } + + @Test + public void testLabelKeyedStringDictConvertingListShouldFail() throws Exception { + try { + BuildType.LABEL_KEYED_STRING_DICT.convert( + ImmutableList.of("//actually/a:label"), null, currentRule); + fail("Expected a conversion exception to be thrown."); + } catch (ConversionException expected) { + assertThat(expected) + .hasMessage( + "expected value of type 'dict(label, string)', " + + "but got [\"//actually/a:label\"] (List)"); + } + } + + @Test + public void testLabelKeyedStringDictConvertingMapWithNonStringKeyShouldFail() throws Exception { + try { + BuildType.LABEL_KEYED_STRING_DICT.convert(ImmutableMap.of(1, "OK"), null, currentRule); + fail("Expected a conversion exception to be thrown."); + } catch (ConversionException expected) { + assertThat(expected) + .hasMessage("expected value of type 'string' for dict key element, but got 1 (int)"); + } + } + + @Test + public void testLabelKeyedStringDictConvertingMapWithNonStringValueShouldFail() throws Exception { + try { + BuildType.LABEL_KEYED_STRING_DICT.convert( + ImmutableMap.of("//actually/a:label", 3), null, currentRule); + fail("Expected a conversion exception to be thrown."); + } catch (ConversionException expected) { + assertThat(expected) + .hasMessage("expected value of type 'string' for dict value element, but got 3 (int)"); + } + } + + @Test + public void testLabelKeyedStringDictConvertingMapWithInvalidLabelKeyShouldFail() + throws Exception { + try { + BuildType.LABEL_KEYED_STRING_DICT.convert( + ImmutableMap.of("//uplevel/references/are:../../forbidden", "OK"), null, currentRule); + fail("Expected a conversion exception to be thrown."); + } catch (ConversionException expected) { + assertThat(expected) + .hasMessage( + "invalid label '//uplevel/references/are:../../forbidden' in " + + "dict key element: invalid target name '../../forbidden': " + + "target names may not contain up-level references '..'"); + } + } + + @Test + public void testLabelKeyedStringDictConvertingMapWithMultipleEquivalentKeysShouldFail() + throws Exception { + Label context = Label.parseAbsolute("//current/package:this"); + Map<String, String> input = new ImmutableMap.Builder<String, String>() + .put(":reference", "value1") + .put("//current/package:reference", "value2") + .build(); + try { + BuildType.LABEL_KEYED_STRING_DICT.convert(input, null, context); + fail("Expected a conversion exception to be thrown."); + } catch (ConversionException expected) { + assertThat(expected) + .hasMessage( + "duplicate labels: //current/package:reference " + + "(as [\":reference\", \"//current/package:reference\"])"); + } + } + + @Test + public void testLabelKeyedStringDictConvertingMapWithMultipleSetsOfEquivalentKeysShouldFail() + throws Exception { + Label context = Label.parseAbsolute("//current/rule:sibling"); + Map<String, String> input = new ImmutableMap.Builder<String, String>() + .put(":rule", "first set") + .put("//current/rule:rule", "also first set") + .put("//other/package:package", "interrupting rule") + .put("//other/package", "interrupting rule's friend") + .put("//current/rule", "part of first set but non-contiguous in iteration order") + .put("//not/involved/in/any:collisions", "same value") + .put("//also/not/involved/in/any:collisions", "same value") + .build(); + try { + BuildType.LABEL_KEYED_STRING_DICT.convert(input, null, context); + fail("Expected a conversion exception to be thrown."); + } catch (ConversionException expected) { + assertThat(expected) + .hasMessage( + "duplicate labels: //current/rule:rule " + + "(as [\":rule\", \"//current/rule:rule\", \"//current/rule\"]), " + + "//other/package:package " + + "(as [\"//other/package:package\", \"//other/package\"])"); + } + } + + @Test + public void testLabelKeyedStringDictErrorConvertingMapWithMultipleEquivalentKeysIncludesContext() + throws Exception { + Label context = Label.parseAbsolute("//current/package:this"); + Map<String, String> input = new ImmutableMap.Builder<String, String>() + .put(":reference", "value1") + .put("//current/package:reference", "value2") + .build(); + try { + BuildType.LABEL_KEYED_STRING_DICT.convert(input, "flag map", context); + fail("Expected a conversion exception to be thrown."); + } catch (ConversionException expected) { + assertThat(expected) + .hasMessage( + "duplicate labels in flag map: //current/package:reference " + + "(as [\":reference\", \"//current/package:reference\"])"); + } + } + + @Test + public void testLabelKeyedStringDictCollectLabels() throws Exception { + Map<Label, String> input = new ImmutableMap.Builder<Label, String>() + .put(Label.parseAbsolute("//absolute:label"), "absolute value") + .put(Label.parseAbsolute("//current/package:relative"), "theory of relativity") + .put(Label.parseAbsolute("//current/package:nocolon"), "colonial times") + .put(Label.parseAbsolute("//current/package:explicit"), "explicit content") + .put(Label.parseAbsolute("//i/was/already/a/label"), "and that's okay") + .build(); + + ImmutableList<Label> expected = + ImmutableList.of( + Label.parseAbsolute("//absolute:label"), + Label.parseAbsolute("//current/package:relative"), + Label.parseAbsolute("//current/package:nocolon"), + Label.parseAbsolute("//current/package:explicit"), + Label.parseAbsolute("//i/was/already/a/label")); + + assertThat(collectLabels(BuildType.LABEL_KEYED_STRING_DICT, input)) + .containsExactlyElementsIn(expected); + } + + @Test public void testFilesetEntry() throws Exception { Label srcDir = Label.create("foo", "src"); Label entryLabel = Label.create("foo", "entry"); diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java index 18942dcb09..9088a52f71 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkAspectsTest.java @@ -459,6 +459,45 @@ public class SkylarkAspectsTest extends AnalysisTestCase { } @Test + public void labelKeyedStringDictAllowsAspects() throws Exception { + scratch.file( + "test/aspect.bzl", + "def _aspect_impl(target, ctx):", + " return struct(aspect_data=target.label.name)", + "", + "def _rule_impl(ctx):", + " return struct(", + " data=','.join(['{}:{}'.format(dep.aspect_data, val)", + " for dep, val in ctx.attr.attr.items()]))", + "", + "MyAspect = aspect(", + " implementation=_aspect_impl,", + ")", + "my_rule = rule(", + " implementation=_rule_impl,", + " attrs = { 'attr' : ", + " attr.label_keyed_string_dict(aspects = [MyAspect]) },", + ")"); + + scratch.file( + "test/BUILD", + "load('/test/aspect', 'my_rule')", + "java_library(", + " name = 'yyy',", + ")", + "my_rule(", + " name = 'xxx',", + " attr = {':yyy': 'zzz'},", + ")"); + + AnalysisResult analysisResult = update("//test:xxx"); + ConfiguredTarget target = analysisResult.getTargetsToBuild().iterator().next(); + SkylarkProviders skylarkProviders = target.getProvider(SkylarkProviders.class); + Object value = skylarkProviders.getValue("data"); + assertThat(value).isEqualTo("yyy:zzz"); + } + + @Test public void aspectsDoNotAttachToFiles() throws Exception { FileSystemUtils.appendIsoLatin1(scratch.resolve("WORKSPACE"), "bind(name = 'yyy', actual = '//test:zzz.jar')"); diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java index 578db1b98f..b5d7108dd7 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java @@ -775,6 +775,335 @@ public class SkylarkRuleContextTest extends SkylarkTestCase { } @Test + public void testLabelKeyedStringDictConvertsToTargetToStringMap() throws Exception { + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(),", + " }", + ")"); + + scratch.file( + "BUILD", + "filegroup(name='dep')", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r',", + " label_dict={':dep': 'value'})"); + + invalidatePackages(); + SkylarkRuleContext context = createRuleContext("//:r"); + Label keyLabel = + (Label) evalRuleContextCode(context, "ruleContext.attr.label_dict.keys()[0].label"); + assertThat(keyLabel).isEqualTo(Label.parseAbsolute("//:dep")); + String valueString = + (String) evalRuleContextCode(context, "ruleContext.attr.label_dict.values()[0]"); + assertThat(valueString).isEqualTo("value"); + } + + @Test + public void testLabelKeyedStringDictAcceptsDefaultValues() throws Exception { + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(default={Label('//:default'): 'defs'}),", + " }", + ")"); + + scratch.file( + "BUILD", + "filegroup(name='default')", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r')"); + + invalidatePackages(); + SkylarkRuleContext context = createRuleContext("//:r"); + Label keyLabel = + (Label) evalRuleContextCode(context, "ruleContext.attr.label_dict.keys()[0].label"); + assertThat(keyLabel).isEqualTo(Label.parseAbsolute("//:default")); + String valueString = + (String) evalRuleContextCode(context, "ruleContext.attr.label_dict.values()[0]"); + assertThat(valueString).isEqualTo("defs"); + } + + @Test + public void testLabelKeyedStringDictAllowsFilesWhenAllowFilesIsTrue() throws Exception { + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(allow_files=True),", + " }", + ")"); + + scratch.file("myfile.cc"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r',", + " label_dict={'myfile.cc': 'value'})"); + + invalidatePackages(); + createRuleContext("//:r"); + assertNoEvents(); + } + + @Test + public void testLabelKeyedStringDictAllowsFilesOfAppropriateTypes() throws Exception { + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(allow_files=['.cc']),", + " }", + ")"); + + scratch.file("myfile.cc"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r',", + " label_dict={'myfile.cc': 'value'})"); + + invalidatePackages(); + createRuleContext("//:r"); + assertNoEvents(); + } + + @Test + public void testLabelKeyedStringDictForbidsFilesOfIncorrectTypes() throws Exception { + reporter.removeHandler(failFastHandler); + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(allow_files=['.cc']),", + " }", + ")"); + + scratch.file("myfile.cpp"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r',", + " label_dict={'myfile.cpp': 'value'})"); + + invalidatePackages(); + getConfiguredTarget("//:r"); + assertContainsEvent("file '//:myfile.cpp' is misplaced here (expected .cc)"); + } + + @Test + public void testLabelKeyedStringDictForbidsFilesWhenAllowFilesIsFalse() throws Exception { + reporter.removeHandler(failFastHandler); + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(allow_files=False),", + " }", + ")"); + + scratch.file("myfile.cpp"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r',", + " label_dict={'myfile.cpp': 'value'})"); + + invalidatePackages(); + getConfiguredTarget("//:r"); + assertContainsEvent("in label_dict attribute of my_rule rule //:r: " + + "file '//:myfile.cpp' is misplaced here (expected no files)"); + } + + @Test + public void testLabelKeyedStringDictAllowsRulesWithRequiredProviders() throws Exception { + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(providers=[['my_provider']]),", + " }", + ")", + "def _dep_impl(ctx):", + " return struct(my_provider=5)", + "my_dep_rule = rule(", + " implementation = _dep_impl,", + " attrs = {}", + ")"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule', 'my_dep_rule')", + "my_dep_rule(name='dep')", + "my_rule(name='r',", + " label_dict={':dep': 'value'})"); + + invalidatePackages(); + createRuleContext("//:r"); + assertNoEvents(); + } + + @Test + public void testLabelKeyedStringDictForbidsRulesMissingRequiredProviders() throws Exception { + reporter.removeHandler(failFastHandler); + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(providers=[['my_provider']]),", + " }", + ")", + "def _dep_impl(ctx):", + " return", + "my_dep_rule = rule(", + " implementation = _dep_impl,", + " attrs = {}", + ")"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule', 'my_dep_rule')", + "my_dep_rule(name='dep')", + "my_rule(name='r',", + " label_dict={':dep': 'value'})"); + + invalidatePackages(); + getConfiguredTarget("//:r"); + assertContainsEvent("in label_dict attribute of my_rule rule //:r: " + + "'//:dep' does not have mandatory provider 'my_provider'"); + } + + @Test + public void testLabelKeyedStringDictForbidsEmptyDictWhenAllowEmptyIsFalse() throws Exception { + reporter.removeHandler(failFastHandler); + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(allow_empty=False),", + " }", + ")"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r',", + " label_dict={})"); + + invalidatePackages(); + getConfiguredTarget("//:r"); + assertContainsEvent("in label_dict attribute of my_rule rule //:r: " + + "attribute must be non empty"); + } + + @Test + public void testLabelKeyedStringDictAllowsEmptyDictWhenAllowEmptyIsTrue() throws Exception { + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(allow_empty=True),", + " }", + ")"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r',", + " label_dict={})"); + + invalidatePackages(); + createRuleContext("//:r"); + assertNoEvents(); + } + + @Test + public void testLabelKeyedStringDictForbidsMissingAttributeWhenMandatoryIsTrue() + throws Exception { + reporter.removeHandler(failFastHandler); + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(mandatory=True),", + " }", + ")"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r')"); + + invalidatePackages(); + getConfiguredTarget("//:r"); + assertContainsEvent("missing value for mandatory attribute 'label_dict' in 'my_rule' rule"); + } + + @Test + public void testLabelKeyedStringDictAllowsMissingAttributeWhenMandatoryIsFalse() + throws Exception { + scratch.file( + "my_rule.bzl", + "def _impl(ctx):", + " return", + "my_rule = rule(", + " implementation = _impl,", + " attrs = {", + " 'label_dict': attr.label_keyed_string_dict(mandatory=False),", + " }", + ")"); + + scratch.file( + "BUILD", + "load('//:my_rule.bzl', 'my_rule')", + "my_rule(name='r')"); + + invalidatePackages(); + createRuleContext("//:r"); + assertNoEvents(); + } + + @Test public void testLabelAttributeDefault() throws Exception { scratch.file( "my_rule.bzl", diff --git a/src/test/java/com/google/devtools/build/lib/syntax/TypeTest.java b/src/test/java/com/google/devtools/build/lib/syntax/TypeTest.java index a0e36fc7bd..a4e42c7a32 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/TypeTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/TypeTest.java @@ -493,7 +493,8 @@ public class TypeTest { Type.STRING_DICT.convert("some string", null); fail(); } catch (ConversionException e) { - assertThat(e).hasMessage("Expected a map for dictionary but got a java.lang.String"); + assertThat(e).hasMessage( + "expected value of type 'dict(string, string)', but got \"some string\" (string)"); } } @@ -509,4 +510,4 @@ public class TypeTest { }, value); return result.build(); } -}
\ No newline at end of file +} |