// 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.packages; import static com.google.common.truth.Truth.assertThat; import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.SPLIT; import static com.google.devtools.build.lib.packages.Attribute.attr; import static com.google.devtools.build.lib.packages.BuildType.LABEL; import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; import static com.google.devtools.build.lib.syntax.Type.INTEGER; import static com.google.devtools.build.lib.syntax.Type.STRING; import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.util.TestAspects; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.Attribute.SplitTransition; import com.google.devtools.build.lib.packages.Attribute.SplitTransitionProvider; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.FileTypeSet; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Tests of Attribute code. */ @RunWith(JUnit4.class) public class AttributeTest { private void assertDefaultValue(Object expected, Attribute attr) { assertEquals(expected, attr.getDefaultValue(null)); } private void assertType(Type<?> expectedType, Attribute attr) { assertEquals(expectedType, attr.getType()); } @Test public void testBasics() throws Exception { Attribute attr = attr("foo", Type.INTEGER).mandatory().value(3).build(); assertEquals("foo", attr.getName()); assertEquals(3, attr.getDefaultValue(null)); assertEquals(Type.INTEGER, attr.getType()); assertTrue(attr.isMandatory()); assertTrue(attr.isDocumented()); attr = attr("$foo", Type.INTEGER).build(); assertFalse(attr.isDocumented()); } @Test public void testNonEmptyReqiresListType() throws Exception { try { attr("foo", Type.INTEGER).nonEmpty().value(3).build(); fail(); } catch (NullPointerException e) { assertThat(e).hasMessage("attribute 'foo' must be a list"); } } @Test public void testNonEmpty() throws Exception { Attribute attr = attr("foo", BuildType.LABEL_LIST).nonEmpty().legacyAllowAnyFileType().build(); assertEquals("foo", attr.getName()); assertEquals(BuildType.LABEL_LIST, attr.getType()); assertTrue(attr.isNonEmpty()); } @Test public void testSingleArtifactReqiresLabelType() throws Exception { try { attr("foo", Type.INTEGER).singleArtifact().value(3).build(); fail(); } catch (IllegalStateException e) { assertThat(e).hasMessage("attribute 'foo' must be a label-valued type"); } } @Test public void testDoublePropertySet() { Attribute.Builder<String> builder = attr("x", STRING).mandatory().cfg(HOST).undocumented("") .value("y"); try { builder.mandatory(); fail(); } catch (IllegalStateException expected) { // expected } try { builder.cfg(HOST); fail(); } catch (IllegalStateException expected) { // expected } try { builder.undocumented(""); fail(); } catch (IllegalStateException expected) { // expected } try { builder.value("z"); fail(); } catch (IllegalStateException expected) { // expected } builder = attr("$x", STRING); try { builder.undocumented(""); fail(); } catch (IllegalStateException expected) { // expected } } /** * Tests the "convenience factories" (string, label, etc) for default * values. */ @Test public void testConvenienceFactoriesDefaultValues() throws Exception { assertDefaultValue(0, attr("x", INTEGER).build()); assertDefaultValue(42, attr("x", INTEGER).value(42).build()); assertDefaultValue("", attr("x", STRING).build()); assertDefaultValue("foo", attr("x", STRING).value("foo").build()); Label label = Label.parseAbsolute("//foo:bar"); assertDefaultValue(null, attr("x", LABEL).legacyAllowAnyFileType().build()); assertDefaultValue(label, attr("x", LABEL).legacyAllowAnyFileType().value(label).build()); List<String> slist = Arrays.asList("foo", "bar"); assertDefaultValue(Collections.emptyList(), attr("x", STRING_LIST).build()); assertDefaultValue(slist, attr("x", STRING_LIST).value(slist).build()); List<Label> llist = Arrays.asList(Label.parseAbsolute("//foo:bar"), Label.parseAbsolute("//foo:wiz")); assertDefaultValue(Collections.emptyList(), attr("x", LABEL_LIST).legacyAllowAnyFileType().build()); assertDefaultValue(llist, attr("x", LABEL_LIST).legacyAllowAnyFileType().value(llist).build()); } /** * Tests the "convenience factories" (string, label, etc) for types. */ @Test public void testConvenienceFactoriesTypes() throws Exception { assertType(INTEGER, attr("x", INTEGER).build()); assertType(INTEGER, attr("x", INTEGER).value(42).build()); assertType(STRING, attr("x", STRING).build()); assertType(STRING, attr("x", STRING).value("foo").build()); Label label = Label.parseAbsolute("//foo:bar"); assertType(LABEL, attr("x", LABEL).legacyAllowAnyFileType().build()); assertType(LABEL, attr("x", LABEL).legacyAllowAnyFileType().value(label).build()); List<String> slist = Arrays.asList("foo", "bar"); assertType(STRING_LIST, attr("x", STRING_LIST).build()); assertType(STRING_LIST, attr("x", STRING_LIST).value(slist).build()); List<Label> llist = Arrays.asList(Label.parseAbsolute("//foo:bar"), Label.parseAbsolute("//foo:wiz")); assertType(LABEL_LIST, attr("x", LABEL_LIST).legacyAllowAnyFileType().build()); assertType(LABEL_LIST, attr("x", LABEL_LIST).legacyAllowAnyFileType().value(llist).build()); } @Test public void testCloneBuilder() { FileTypeSet txtFiles = FileTypeSet.of(FileType.of("txt")); RuleClass.Builder.RuleClassNamePredicate ruleClasses = new RuleClass.Builder.RuleClassNamePredicate("mock_rule"); Attribute parentAttr = attr("x", LABEL_LIST) .allowedFileTypes(txtFiles) .mandatory() .aspect(TestAspects.SIMPLE_ASPECT) .build(); { Attribute childAttr1 = parentAttr.cloneBuilder().build(); assertEquals("x", childAttr1.getName()); assertEquals(txtFiles, childAttr1.getAllowedFileTypesPredicate()); assertEquals(Predicates.alwaysTrue(), childAttr1.getAllowedRuleClassesPredicate()); assertTrue(childAttr1.isMandatory()); assertFalse(childAttr1.isNonEmpty()); assertThat(childAttr1.getAspects(null /* rule */)).hasSize(1); } { Attribute childAttr2 = parentAttr .cloneBuilder() .nonEmpty() .allowedRuleClasses(ruleClasses) .aspect(TestAspects.ERROR_ASPECT) .build(); assertEquals("x", childAttr2.getName()); assertEquals(txtFiles, childAttr2.getAllowedFileTypesPredicate()); assertEquals(ruleClasses, childAttr2.getAllowedRuleClassesPredicate()); assertTrue(childAttr2.isMandatory()); assertTrue(childAttr2.isNonEmpty()); assertThat(childAttr2.getAspects(null /* rule */)).hasSize(2); } //Check if the parent attribute is unchanged assertFalse(parentAttr.isNonEmpty()); assertEquals(Predicates.alwaysTrue(), parentAttr.getAllowedRuleClassesPredicate()); } /** * Tests that configurability settings are properly received. */ @Test public void testConfigurability() { assertTrue(attr("foo_configurable", BuildType.LABEL_LIST).legacyAllowAnyFileType().build() .isConfigurable()); assertFalse(attr("foo_nonconfigurable", BuildType.LABEL_LIST).legacyAllowAnyFileType() .nonconfigurable("test").build().isConfigurable()); } @Test public void testSplitTransition() throws Exception { TestSplitTransition splitTransition = new TestSplitTransition(); Attribute attr = attr("foo", LABEL).cfg(splitTransition).allowedFileTypes().build(); assertThat(attr.getConfigurationTransition()).isEqualTo(SPLIT); assertTrue(attr.hasSplitConfigurationTransition()); assertThat(attr.getSplitTransition(null)).isEqualTo(splitTransition); } @Test public void testSplitTransitionProvider() throws Exception { TestSplitTransitionProvider splitTransitionProvider = new TestSplitTransitionProvider(); Attribute attr = attr("foo", LABEL).cfg(splitTransitionProvider).allowedFileTypes().build(); assertThat(attr.getConfigurationTransition()).isEqualTo(SPLIT); assertTrue(attr.hasSplitConfigurationTransition()); assertTrue(attr.getSplitTransition(null) instanceof TestSplitTransition); } @Test public void testHostTransition() throws Exception { Attribute attr = attr("foo", LABEL).cfg(HOST).allowedFileTypes().build(); assertThat(attr.getConfigurationTransition()).isEqualTo(HOST); assertFalse(attr.hasSplitConfigurationTransition()); } private static class TestSplitTransition implements SplitTransition<BuildOptions> { @Override public boolean defaultsToSelf() { return true; } @Override public List<BuildOptions> split(BuildOptions buildOptions) { return ImmutableList.of(buildOptions.clone(), buildOptions.clone()); } } private static class TestSplitTransitionProvider implements SplitTransitionProvider { @Override public SplitTransition<?> apply(Rule fromRule) { return new TestSplitTransition(); } } }