blob: cbfdbfb18f3337c9ad7dbcfc2973167bec62f1e8 [file] [log] [blame]
// 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.attr;
import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
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.fail;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassNamePredicate;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests for the {@link RuleClass.Builder}.
*/
@RunWith(JUnit4.class)
public class RuleClassBuilderTest extends PackageLoadingTestCase {
private static final RuleClass.ConfiguredTargetFactory<Object, Object, Exception>
DUMMY_CONFIGURED_TARGET_FACTORY =
new RuleClass.ConfiguredTargetFactory<Object, Object, Exception>() {
@Override
public Object create(Object ruleContext)
throws InterruptedException, RuleErrorException, ActionConflictException {
throw new IllegalStateException();
}
};
@Test
public void testRuleClassBuilderBasics() throws Exception {
RuleClass ruleClassA =
new RuleClass.Builder("ruleA", RuleClassType.NORMAL, false)
.factory(DUMMY_CONFIGURED_TARGET_FACTORY)
.add(attr("srcs", BuildType.LABEL_LIST).legacyAllowAnyFileType())
.add(attr("tags", STRING_LIST))
.add(attr("X", com.google.devtools.build.lib.syntax.Type.INTEGER).mandatory())
.build();
assertThat(ruleClassA.getName()).isEqualTo("ruleA");
assertThat(ruleClassA.getAttributeCount()).isEqualTo(3);
assertThat(ruleClassA.hasBinaryOutput()).isTrue();
assertThat((int) ruleClassA.getAttributeIndex("srcs")).isEqualTo(0);
assertThat(ruleClassA.getAttributeByName("srcs")).isEqualTo(ruleClassA.getAttribute(0));
assertThat((int) ruleClassA.getAttributeIndex("tags")).isEqualTo(1);
assertThat(ruleClassA.getAttributeByName("tags")).isEqualTo(ruleClassA.getAttribute(1));
assertThat((int) ruleClassA.getAttributeIndex("X")).isEqualTo(2);
assertThat(ruleClassA.getAttributeByName("X")).isEqualTo(ruleClassA.getAttribute(2));
}
@Test
public void testRuleClassBuilderTestIsBinary() throws Exception {
RuleClass ruleClassA =
new RuleClass.Builder("rule_test", RuleClassType.TEST, false)
.factory(DUMMY_CONFIGURED_TARGET_FACTORY)
.add(attr("tags", STRING_LIST))
.add(attr("size", STRING).value("medium"))
.add(attr("timeout", STRING))
.add(attr("flaky", BOOLEAN).value(false))
.add(attr("shard_count", INTEGER).value(-1))
.add(attr("local", BOOLEAN))
.build();
assertThat(ruleClassA.hasBinaryOutput()).isTrue();
}
@Test
public void testRuleClassBuilderGenruleIsNotBinary() throws Exception {
RuleClass ruleClassA =
new RuleClass.Builder("ruleA", RuleClassType.NORMAL, false)
.factory(DUMMY_CONFIGURED_TARGET_FACTORY)
.setOutputToGenfiles()
.add(attr("tags", STRING_LIST))
.build();
assertThat(ruleClassA.hasBinaryOutput()).isFalse();
}
@Test
public void testRuleClassTestNameValidity() throws Exception {
try {
new RuleClass.Builder("ruleA", RuleClassType.TEST, false).build();
fail();
} catch (IllegalArgumentException e) {
// Expected exception.
}
}
@Test
public void testRuleClassNormalNameValidity() throws Exception {
try {
new RuleClass.Builder("ruleA_test", RuleClassType.NORMAL, false).build();
fail();
} catch (IllegalArgumentException e) {
// Expected exception.
}
}
@Test
public void testDuplicateAttribute() throws Exception {
RuleClass.Builder builder =
new RuleClass.Builder("ruleA", RuleClassType.NORMAL, false).add(attr("a", STRING));
try {
builder.add(attr("a", STRING));
fail();
} catch (IllegalStateException e) {
// Expected exception.
}
}
@Test
public void testPropertiesOfAbstractRuleClass() throws Exception {
try {
new RuleClass.Builder("$ruleA", RuleClassType.ABSTRACT, false).setOutputToGenfiles();
fail();
} catch (IllegalStateException e) {
// Expected exception.
}
try {
new RuleClass.Builder("$ruleB", RuleClassType.ABSTRACT, false)
.setImplicitOutputsFunction(null);
fail();
} catch (IllegalStateException e) {
// Expected exception.
}
}
@Test
public void testDuplicateInheritedAttribute() throws Exception {
RuleClass a =
new RuleClass.Builder("ruleA", RuleClassType.NORMAL, false)
.factory(DUMMY_CONFIGURED_TARGET_FACTORY)
.add(attr("a", STRING).value("A"))
.add(attr("tags", STRING_LIST))
.build();
RuleClass b =
new RuleClass.Builder("ruleB", RuleClassType.NORMAL, false)
.factory(DUMMY_CONFIGURED_TARGET_FACTORY)
.add(attr("a", STRING).value("B"))
.add(attr("tags", STRING_LIST))
.build();
try {
// In case of multiple attribute inheritance the attributes must equal
new RuleClass.Builder("ruleC", RuleClassType.NORMAL, false, a, b).build();
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessage("Attribute a is inherited multiple times in ruleC ruleclass");
}
}
@Test
public void testRemoveAttribute() throws Exception {
RuleClass a =
new RuleClass.Builder("rule", RuleClassType.NORMAL, false)
.factory(DUMMY_CONFIGURED_TARGET_FACTORY)
.add(attr("a", STRING))
.add(attr("b", STRING))
.add(attr("tags", STRING_LIST))
.build();
RuleClass.Builder builder =
new RuleClass.Builder("c", RuleClassType.NORMAL, false, a)
.factory(DUMMY_CONFIGURED_TARGET_FACTORY);
RuleClass c = builder.removeAttribute("a").add(attr("a", INTEGER)).removeAttribute("b").build();
assertThat(c.hasAttr("a", STRING)).isFalse();
assertThat(c.hasAttr("a", INTEGER)).isTrue();
assertThat(c.hasAttr("b", STRING)).isFalse();
try {
builder.removeAttribute("c");
fail();
} catch (IllegalStateException e) {
// Expected exception.
}
}
@Test
public void testRequiredToolchainsAreInherited() throws Exception {
Label mockToolchainType = Label.parseAbsoluteUnchecked("//mock_toolchain_type");
RuleClass parent =
new RuleClass.Builder("$parent", RuleClassType.ABSTRACT, false)
.add(attr("tags", STRING_LIST))
.addRequiredToolchains(ImmutableList.of(mockToolchainType))
.build();
RuleClass child =
new RuleClass.Builder("child", RuleClassType.NORMAL, false, parent)
.factory(DUMMY_CONFIGURED_TARGET_FACTORY)
.add(attr("attr", STRING))
.build();
assertThat(child.getRequiredToolchains()).contains(mockToolchainType);
}
@Test
public void testBasicRuleNamePredicates() throws Exception {
Predicate<String> abcdef = nothingBut("abc", "def").asPredicateOfRuleClassName();
assertThat(abcdef.test("abc")).isTrue();
assertThat(abcdef.test("def")).isTrue();
assertThat(abcdef.test("ghi")).isFalse();
}
@Test
public void testTwoRuleNamePredicateFactoriesEquivalent() throws Exception {
RuleClassNamePredicate a = nothingBut("abc", "def");
RuleClassNamePredicate b = RuleClassNamePredicate.only(ImmutableList.of("abc", "def"));
assertThat(a.asPredicateOfRuleClassName()).isEqualTo(b.asPredicateOfRuleClassName());
assertThat(a.asPredicateOfRuleClass()).isEqualTo(b.asPredicateOfRuleClass());
}
@Test
public void testEverythingButRuleNamePredicates() throws Exception {
Predicate<String> abcdef = allBut("abc", "def").asPredicateOfRuleClassName();
assertThat(abcdef.test("abc")).isFalse();
assertThat(abcdef.test("def")).isFalse();
assertThat(abcdef.test("ghi")).isTrue();
}
@Test
public void testRuleClassNamePredicateIntersection() {
// two positives intersect iff they contain any of the same items
assertThat(nothingBut("abc", "def").consideredOverlapping(nothingBut("abc"))).isTrue();
assertThat(nothingBut("abc", "def").consideredOverlapping(nothingBut("ghi"))).isFalse();
// negatives are never considered to overlap...
assertThat(allBut("abc", "def").consideredOverlapping(allBut("abc", "def"))).isFalse();
assertThat(allBut("abc", "def").consideredOverlapping(allBut("ghi", "jkl"))).isFalse();
assertThat(allBut("abc", "def").consideredOverlapping(nothingBut("abc", "def"))).isFalse();
assertThat(nothingBut("abc", "def").consideredOverlapping(allBut("abc", "def"))).isFalse();
assertThat(allBut("abc", "def").consideredOverlapping(nothingBut("abc"))).isFalse();
assertThat(allBut("abc").consideredOverlapping(nothingBut("abc", "def"))).isFalse();
}
private RuleClassNamePredicate nothingBut(String... excludedRuleClasses) {
return RuleClassNamePredicate.only(excludedRuleClasses);
}
private RuleClassNamePredicate allBut(String... excludedRuleClasses) {
return RuleClassNamePredicate.allExcept(excludedRuleClasses);
}
}