diff options
author | 2015-11-17 21:07:28 +0000 | |
---|---|---|
committer | 2015-11-18 15:30:23 +0000 | |
commit | e624fb9ef4e966251b9f0c193f37d2d7f39d9a5e (patch) | |
tree | 8e96e9c16c9beb67c9150cce0220a937c876b03a /src/test/java/com/google/devtools/build/lib/generatedprojecttest/util | |
parent | 0a89fff21a26d1bd19bf4a122b45c2af0c3d7567 (diff) |
Open source test project generation utils
--
MOS_MIGRATED_REVID=108070691
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/generatedprojecttest/util')
4 files changed, 436 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/BuildFileContentsGenerator.java b/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/BuildFileContentsGenerator.java new file mode 100644 index 0000000000..18460358de --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/BuildFileContentsGenerator.java @@ -0,0 +1,128 @@ +// 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.generatedprojecttest.util; + +import com.google.common.base.Joiner; +import com.google.devtools.build.lib.testutil.BuildRuleBuilder; + +/** + * A generator of the contents of a Build File. It contains functions to aid in generating build + * files for specific purposes. + */ +public final class BuildFileContentsGenerator implements FileContentsGenerator { + + /** + * Builds a string of the file contents. + */ + private final StringBuilder contents = new StringBuilder(); + + /** + * Boolean to track if default package visibility has been set - it may only happen once. + */ + private boolean defaultPackageVisibilityIsSet = false; + + /** + * Index for generated rule names, so they are all unique within a file. + */ + private int index = 0; + + /** + * @return a new unique rule name + */ + public String uniqueRuleName() { + index++; + return "rule" + index; + } + + /** + * Set the default package visibility for this build file. If this function is never called, the + * default package visibility is ['//visibility:public']. + */ + public FileContentsGenerator setDefaultPackageVisibility(String... visibilityLabelList) { + if (defaultPackageVisibilityIsSet) { + throw new IllegalStateException("setDefaultPackageVisibility was called twice."); + } + contents.insert(0, + "package(default_visibility = ['" + Joiner.on("', '").join(visibilityLabelList) + "'])\n"); + defaultPackageVisibilityIsSet = true; + return this; + } + + /** + * Appends the rule built from the provided BuildRuleBuilder along with the other rules + * generated in order to this rule to be able to build. + */ + public FileContentsGenerator addRule(BuildRuleBuilder ruleBuilder) { + contents.append(ruleBuilder.build()); + for (BuildRuleBuilder generatedRuleBuilder : ruleBuilder.getRulesToGenerate()) { + contents.append(generatedRuleBuilder.build()); + } + return this; + } + + /** + * Appends a chain of ruleClass rules, each depending on the one before it. + * + * @param ruleClass Name of the rule class to instantiate. + * @param chainLength Number of rules to create in the chain. + * @return this + */ + public FileContentsGenerator addDependencyChainOfRule(String ruleClass, int chainLength) { + BuildRuleBuilder previous; + BuildRuleBuilder current = new BuildRuleBuilder(ruleClass, uniqueRuleName()); + contents.append(current.build()); + + while (chainLength > 1) { + previous = current; + current = new BuildRuleBuilder(ruleClass, uniqueRuleName()); + if (ruleClass.equals("java_library")) { + current.dependsVia("exports").on(previous); + } else { + current.dependsVia("deps").on(previous); + } + contents.append(current.build()); + chainLength--; + } + return this; + } + + /** + * Appends a chain of ruleClass rules, and one master rule which depends on all the other rules. + * + * @param ruleClass Name of the rule class to instantiate. + * @param noOfDeps Number of rules to create as dependencies for the first rule. + * @return this + */ + public FileContentsGenerator addRuleWithDependencies(String ruleClass, int noOfDeps) { + BuildRuleBuilder masterRule = new BuildRuleBuilder(ruleClass, uniqueRuleName()); + for (int i = 0; i < noOfDeps; i++) { + BuildRuleBuilder dependentRule = new BuildRuleBuilder(ruleClass, uniqueRuleName()); + masterRule.dependsVia("deps").on(dependentRule); + contents.append(dependentRule.build()); + } + contents.append(masterRule.build()); + return this; + } + + @Override + public String getContents() { + try { + setDefaultPackageVisibility("//visibility:public"); + } catch (IllegalStateException e) { + // Default Package Visibility already set, do nothing. + } + return contents.toString(); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/FileContentsGenerator.java b/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/FileContentsGenerator.java new file mode 100644 index 0000000000..e3ff1002b9 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/FileContentsGenerator.java @@ -0,0 +1,28 @@ +// 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.generatedprojecttest.util; + +/** + * Interface to generate the contents of useful files for tests. + * It should be implemented to generate the contents of BUILD files, and any other + * required files, for use in generated test projects. + */ +public interface FileContentsGenerator { + + /** + * @return The generated file contents for the implementation. + */ + String getContents(); +} diff --git a/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/RuleSetUtils.java b/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/RuleSetUtils.java new file mode 100644 index 0000000000..a9dbbc8aa8 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/RuleSetUtils.java @@ -0,0 +1,188 @@ +// 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.generatedprojecttest.util; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.BuildType; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; +import com.google.devtools.build.lib.syntax.Type; +import com.google.devtools.build.lib.util.Pair; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Utility class for providing static predicates for rules, to help filter the rules. + */ +public class RuleSetUtils { + + /** + * Predicate for checking if a rule is hidden. + */ + public static final Predicate<String> HIDDEN_RULE = new Predicate<String>() { + @Override + public boolean apply(final String input) { + try { + RuleClassType.INVISIBLE.checkName(input); + return true; + } catch (IllegalArgumentException e) { + return input.equals("testing_dummy_rule"); + } + } + }; + + /** + * Predicate for checking if a rule has any mandatory attributes. + */ + public static final Predicate<RuleClass> MANDATORY_ATTRIBUTES = new Predicate<RuleClass>() { + @Override + public boolean apply(final RuleClass input) { + List<Attribute> li = new ArrayList<>(input.getAttributes()); + return Iterables.any(li, MANDATORY); + } + }; + + /** + * Predicate for checking if a rule allows an empty srcs attribute. + */ + public static final Predicate<RuleClass> EMPTY_SOURCES_ALLOWED = new Predicate<RuleClass>() { + @Override + public boolean apply(final RuleClass input) { + return !input.getAttributeByName("srcs").isNonEmpty(); + } + }; + + /** + * Predicate for checking that the rule can have a deps attribute, and does not have any + * other mandatory attributes besides deps. + */ + public static final Predicate<RuleClass> DEPS_ONLY_ALLOWED = new Predicate<RuleClass>() { + @Override + public boolean apply(final RuleClass input) { + List<Attribute> li = new ArrayList<>(input.getAttributes()); + // TODO(bazel-team): after the API migration we shouldn't check srcs separately + boolean emptySrcsAllowed = input.hasAttr("srcs", BuildType.LABEL_LIST) + ? !input.getAttributeByName("srcs").isNonEmpty() : true; + if (!(emptySrcsAllowed && Iterables.any(li, DEPS))) { + return false; + } + + Iterator<Attribute> it = li.iterator(); + boolean mandatoryAttributesBesidesDeps = + Iterables.any(Lists.newArrayList(Iterators.filter(it, MANDATORY)), Predicates.not(DEPS)); + return !mandatoryAttributesBesidesDeps; + } + }; + + /** + * Predicate for checking if a RuleClass has certain attributes + */ + public static class HasAttributes implements Predicate<RuleClass> { + + private static enum Operator { + ANY, ALL + } + + private final List<Pair<String, Type<?>>> attributes; + private final Operator operator; + + public HasAttributes(Collection<Pair<String, Type<?>>> attributes, Operator operator) { + this.attributes = ImmutableList.copyOf(attributes); + this.operator = operator; + } + + @Override + public boolean apply(final RuleClass input) { + switch (operator) { + case ANY: + for (Pair<String, Type<?>> attribute : attributes) { + if (input.hasAttr(attribute.first, attribute.second)) { + return true; + } + } + return false; + case ALL: + for (Pair<String, Type<?>> attribute : attributes) { + if (!input.hasAttr(attribute.first, attribute.second)) { + return false; + } + } + return true; + } + return false; + } + } + + public static final Predicate<RuleClass> hasAnyAttributes( + Collection<Pair<String, Type<?>>> attributes) { + return new HasAttributes(attributes, HasAttributes.Operator.ANY); + } + + public static final Predicate<RuleClass> hasAllAttributes( + Collection<Pair<String, Type<?>>> attributes) { + return new HasAttributes(attributes, HasAttributes.Operator.ALL); + } + + /** + * Predicate for checking if an attribute is mandatory. + */ + private static final Predicate<Attribute> MANDATORY = new Predicate<Attribute>() { + @Override + public boolean apply(final Attribute input) { + return input.isMandatory(); + } + }; + + /** + * Predicate for checking if an attribute is the "deps" attribute. + */ + private static final Predicate<Attribute> DEPS = new Predicate<Attribute>() { + @Override + public boolean apply(final Attribute input) { + return input.getName().equals("deps"); + } + }; + + /** + * Predicate for checking if all the strings in a list of strings are different. + */ + public static final Predicate<List<String>> ELEMENTS_ALL_DIFFERENT = + new Predicate<List<String>>() { + @Override + public boolean apply(final List<String> input) { + Set<String> inputAsSet = new HashSet<>(input); + return input.size() == inputAsSet.size(); + } + }; + + /** + * Predicate for checking if a rule class is not in excluded. + */ + public static Predicate<String> notContainsAnyOf(final ImmutableSet<String> excluded) { + return Predicates.not(Predicates.in(excluded)); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/TestProjectBuilder.java b/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/TestProjectBuilder.java new file mode 100644 index 0000000000..ee32d724e6 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/generatedprojecttest/util/TestProjectBuilder.java @@ -0,0 +1,92 @@ +// 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.generatedprojecttest.util; + +import com.google.devtools.build.lib.testutil.BuildRuleBuilder; +import com.google.devtools.build.lib.testutil.Scratch; + +import java.io.IOException; + +/** + * A builder that generates whole test projects in a scratch file system. + */ + +// TODO(blaze-team): (2012) generate valid parameterized BUILD rules. +// TODO(blaze-team): (2012) generate any required src or data or other files. + +public final class TestProjectBuilder { + + // Default workspace name. + private static final String WORKSPACE = "workspace"; + + // The directory name to use for the workspace. + private final String workspace; + /** + * Provides functionality to create and manipulate a scratch file system. + */ + private final Scratch scratch = new Scratch(); + + /** + * Creates a builder that will use the default workspace name as the directory. + */ + public TestProjectBuilder() { + this(WORKSPACE); + } + + /** + * Creates a builder that will use the given workspace name as the directory. + */ + public TestProjectBuilder(String workspace) { + this.workspace = workspace; + } + + /** + * Creates a file in the specified directory with the given content. + * + * @param dirName directory to create a new file within + * @param fileName file Name of the new file (must be unique within the directory) + * @param generator FileContentsGenerator implementation + * @throws IOException if the input dirName was not valid, or the file already existed + */ + public void createFileInDir(String dirName, String fileName, FileContentsGenerator generator) + throws IOException { + scratch.file( + String.format("/%s/%s/%s", workspace, dirName, fileName), generator.getContents()); + } + + /** + * Returns the {@link Scratch} containing the Test Project that has been built. + */ + public Scratch getScratch() { + return this.scratch; + } + + /** + * Creates a dummy file with 'dummy' as content in the given package with the given name. + * @throws IOException + */ + public void createDummyFileInDir(String pkg, String fileName) throws IOException { + scratch.file(String.format("%s/%s", pkg, fileName), "dummy"); + } + + /** + * Generates the files necessary for the rule. + */ + public void createFilesToGenerate(BuildRuleBuilder ruleBuilder) throws IOException { + for (String file : ruleBuilder.getFilesToGenerate()) { + scratch.file(file, "dummy"); + } + } +} |