// 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.common.truth.Truth.assertWithMessage; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.eventbus.EventBus; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.events.Reporter; import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap; import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.testutil.TestRuleClassProvider; import com.google.devtools.build.lib.vfs.Path; import java.util.HashMap; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class RuleFactoryTest extends PackageLoadingTestCase { private ConfiguredRuleClassProvider provider = TestRuleClassProvider.getRuleClassProvider(); private RuleFactory ruleFactory = new RuleFactory(provider, AttributeContainer::new); public static final Location LOCATION_42 = Location.fromFileAndOffsets(null, 42, 42); @Test public void testCreateRule() throws Exception { Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD"); Package.Builder pkgBuilder = packageFactory.newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING") .setFilename(myPkgPath); Map attributeValues = new HashMap<>(); attributeValues.put("name", "foo"); attributeValues.put("alwayslink", true); RuleClass ruleClass = provider.getRuleClassMap().get("cc_library"); Rule rule = RuleFactory.createAndAddRule( pkgBuilder, ruleClass, new BuildLangTypedAttributeValuesMap(attributeValues), new Reporter(new EventBus()), /*ast=*/ null, LOCATION_42, /*env=*/ null, new AttributeContainer(ruleClass)); assertThat(rule.getAssociatedRule()).isSameAs(rule); // pkg.getRules() = [rule] Package pkg = pkgBuilder.build(); assertThat(Sets.newHashSet(pkg.getTargets(Rule.class))).hasSize(1); assertThat(pkg.getTargets(Rule.class).iterator().next()).isEqualTo(rule); assertThat(pkg.getTarget("foo")).isSameAs(rule); assertThat(rule.getLabel()).isEqualTo(Label.parseAbsolute("//mypkg:foo", ImmutableMap.of())); assertThat(rule.getName()).isEqualTo("foo"); assertThat(rule.getRuleClass()).isEqualTo("cc_library"); assertThat(rule.getTargetKind()).isEqualTo("cc_library rule"); assertThat(rule.getLocation().getStartOffset()).isEqualTo(42); assertThat(rule.containsErrors()).isFalse(); // Attr with explicitly-supplied value: AttributeMap attributes = RawAttributeMapper.of(rule); assertThat(attributes.get("alwayslink", Type.BOOLEAN)).isTrue(); try { attributes.get("alwayslink", Type.STRING); // type error: boolean, not string! fail(); } catch (Exception e) { /* Class of exception and error message are not specified by API. */ } try { attributes.get("nosuchattr", Type.STRING); // no such attribute fail(); } catch (Exception e) { /* Class of exception and error message are not specified by API. */ } // Attrs with default values: // cc_library linkstatic default=0 according to build encyc. assertThat(attributes.get("linkstatic", Type.BOOLEAN)).isFalse(); assertThat(attributes.get("testonly", Type.BOOLEAN)).isFalse(); assertThat(attributes.get("srcs", BuildType.LABEL_LIST)).isEmpty(); } @Test public void testCreateWorkspaceRule() throws Exception { Path myPkgPath = scratch.resolve("/foo/workspace/WORKSPACE"); Package.Builder pkgBuilder = packageFactory.newExternalPackageBuilder(myPkgPath, "TESTING"); Map attributeValues = new HashMap<>(); attributeValues.put("name", "foo"); attributeValues.put("actual", "//foo:bar"); RuleClass ruleClass = provider.getRuleClassMap().get("bind"); Rule rule = RuleFactory.createAndAddRule( pkgBuilder, ruleClass, new BuildLangTypedAttributeValuesMap(attributeValues), new Reporter(new EventBus()), /*ast=*/ null, Location.fromFileAndOffsets(myPkgPath.asFragment(), 42, 42), /*env=*/ null, new AttributeContainer(ruleClass)); assertThat(rule.containsErrors()).isFalse(); } @Test public void testWorkspaceRuleFailsInBuildFile() throws Exception { Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD"); Package.Builder pkgBuilder = packageFactory.newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING") .setFilename(myPkgPath); Map attributeValues = new HashMap<>(); attributeValues.put("name", "foo"); attributeValues.put("actual", "//bar:baz"); RuleClass ruleClass = provider.getRuleClassMap().get("bind"); try { RuleFactory.createAndAddRule( pkgBuilder, ruleClass, new BuildLangTypedAttributeValuesMap(attributeValues), new Reporter(new EventBus()), /*ast=*/ null, LOCATION_42, /*env=*/ null, new AttributeContainer(ruleClass)); fail(); } catch (RuleFactory.InvalidRuleException e) { assertThat(e).hasMessageThat().contains("must be in the WORKSPACE file"); } } @Test public void testBuildRuleFailsInWorkspaceFile() throws Exception { Path myPkgPath = scratch.resolve("/foo/workspace/WORKSPACE"); Package.Builder pkgBuilder = packageFactory.newPackageBuilder(Label.EXTERNAL_PACKAGE_IDENTIFIER, "TESTING") .setFilename(myPkgPath); Map attributeValues = new HashMap<>(); attributeValues.put("name", "foo"); attributeValues.put("alwayslink", true); RuleClass ruleClass = provider.getRuleClassMap().get("cc_library"); try { RuleFactory.createAndAddRule( pkgBuilder, ruleClass, new BuildLangTypedAttributeValuesMap(attributeValues), new Reporter(new EventBus()), /*ast=*/ null, Location.fromFileAndOffsets(myPkgPath.asFragment(), 42, 42), /*env=*/ null, new AttributeContainer(ruleClass)); fail(); } catch (RuleFactory.InvalidRuleException e) { assertThat(e).hasMessageThat().contains("cannot be in the WORKSPACE file"); } } private void assertAttr(RuleClass ruleClass, String attrName, Type type) throws Exception { assertWithMessage( "Rule class '" + ruleClass.getName() + "' should have attribute '" + attrName + "' of type '" + type + "'") .that(ruleClass.hasAttr(attrName, type)) .isTrue(); } @Test public void testOutputFileNotEqualDot() throws Exception { Path myPkgPath = scratch.resolve("/foo"); Package.Builder pkgBuilder = packageFactory.newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING") .setFilename(myPkgPath); Map attributeValues = new HashMap<>(); attributeValues.put("outs", Lists.newArrayList(".")); attributeValues.put("name", "some"); RuleClass ruleClass = provider.getRuleClassMap().get("genrule"); try { RuleFactory.createAndAddRule( pkgBuilder, ruleClass, new BuildLangTypedAttributeValuesMap(attributeValues), new Reporter(new EventBus()), /*ast=*/ null, Location.fromFileAndOffsets(myPkgPath.asFragment(), 42, 42), /*env=*/ null, new AttributeContainer(ruleClass)); fail(); } catch (RuleFactory.InvalidRuleException e) { assertWithMessage(e.getMessage()) .that(e.getMessage().contains("output file name can't be equal '.'")) .isTrue(); } } /** * Tests mandatory attribute definitions for test rules. */ // TODO(ulfjack): Remove this check when we switch over to the builder // pattern, which will always guarantee that these attributes are present. @Test public void testTestRules() throws Exception { Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD"); Package pkg = packageFactory.newPackageBuilder(PackageIdentifier.createInMainRepo("mypkg"), "TESTING") .setFilename(myPkgPath) .build(); for (String name : ruleFactory.getRuleClassNames()) { // Create rule instance directly so we'll avoid mandatory attribute check yet will be able // to use TargetUtils.isTestRule() method to identify test rules. RuleClass ruleClass = ruleFactory.getRuleClass(name); Rule rule = new Rule( pkg, pkg.createLabel("myrule"), ruleClass, Location.fromFile(myPkgPath), new AttributeContainer(ruleClass)); if (TargetUtils.isTestRule(rule)) { assertAttr(ruleClass, "tags", Type.STRING_LIST); assertAttr(ruleClass, "size", Type.STRING); assertAttr(ruleClass, "flaky", Type.BOOLEAN); assertAttr(ruleClass, "shard_count", Type.INTEGER); assertAttr(ruleClass, "local", Type.BOOLEAN); } } } }