// 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.analysis.constraints; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; import com.google.devtools.build.lib.cmdline.Label; import java.util.Collection; import java.util.List; import java.util.Set; /** * Common functionality for tests for the constraint enforcement system. */ public abstract class AbstractConstraintsTest extends BuildViewTestCase { /** * Creates an environment group on the scratch filesystem consisting of the specified * environments and specified defaults, set via a builder-style interface. The package name * is the same as the group name. */ protected class EnvironmentGroupMaker { private final String name; private Set<String> environments; private Set<String> defaults; private final Multimap<String, String> fulfillsMap = HashMultimap.create(); public EnvironmentGroupMaker(String name) { this.name = name; } public EnvironmentGroupMaker setEnvironments(String... environments) { this.environments = ImmutableSet.copyOf(environments); return this; } public EnvironmentGroupMaker setDefaults(String... environments) { this.defaults = ImmutableSet.copyOf(environments); return this; } /** * Declares that env1 fulfills env2. */ public EnvironmentGroupMaker setFulfills(String env1, String env2) { fulfillsMap.put(env1, env2); return this; } protected final void make() throws Exception { StringBuilder builder = new StringBuilder(); for (String env : environments) { builder.append("environment(name = '" + env + "',\n") .append(getAttrDef("fulfills", fulfillsMap.get(env).toArray(new String[0]))) .append(")\n"); } String envGroupName = name.contains("/") ? name.substring(name.lastIndexOf("/") + 1) : name; builder.append("environment_group(\n") .append(" name = '" + envGroupName + "',\n") .append(getAttrDef("environments", environments.toArray(new String[0])) + ",\n") .append(getAttrDef("defaults", defaults.toArray(new String[0])) + ",\n") .append(")"); scratch.file("" + name + "/BUILD", builder.toString()); } } /** * Returns a rule definition of the given name, type and custom attribute settings. */ protected static String getRuleDef(String ruleType, String ruleName, String... customAttributes) { String ruleDef = ruleType + "(\n" + " name = '" + ruleName + "',\n" + " srcs = ['" + ruleName + ".sh'],\n"; for (String customAttribute : customAttributes) { ruleDef += " " + customAttribute + ",\n"; } ruleDef += ")\n"; return ruleDef; } /** * Given the inputs, returns the string "attrName = [':label1', ':label2', etc.]" */ protected static String getAttrDef(String attrName, String... labels) { String attrDef = " " + attrName + " = ["; for (String label : labels) { attrDef += "'" + label + "', "; } attrDef += "]"; return attrDef; } /** * The core constraint semantics check that if rule A depends on rule B, B must support all of * A's environments. To model this in the tests below, we construct two rules: a "depending" * rule (i.e. A) that depends on a "dependency" rule (i.e. B). Each test can construct its * own instance of these rules with its own environments specifications by calling this method * and {@link #getDependencyRule} with appropriate environment settings passed in as custom * attributes. * * <p>This method constructs and returns the depending rule (i.e. A). */ protected static String getDependingRule(String... customAttributes) { List<String> attrsAsList = Lists.newArrayList(customAttributes); attrsAsList.add(getAttrDef("deps", "dep")); return getRuleDef("sh_library", "main", attrsAsList.toArray(new String[0])); } /** * Returns the rule that {@link #getDependingRule} depends on. This rule must support every * environment supported by the other one for their constraint relationship to be considered * valid. */ protected static String getDependencyRule(String... customAttributes) { return getRuleDef("sh_library", "dep", customAttributes); } /** * Returns the attribute definition that constrains a rule to the given environments. Inputs * are expected to be package-relative labels (e.g. {@code "foo_env"}). */ protected static String constrainedTo(String... environments) { return getAttrDef("restricted_to", environments); } /** * Returns the attribute definition that designates a rule compatible with the given environments. */ protected static String compatibleWith(String... environments) { return getAttrDef("compatible_with", environments); } /** * Returns the environments supported by a rule. */ protected Collection<Label> supportedEnvironments(String ruleName, String ruleDef) throws Exception { return ConstraintSemantics.getSupportedEnvironments( getRuleContext(scratchConfiguredTarget("hello", ruleName, ruleDef))).getEnvironments(); } }