| // Copyright 2014 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.rules.test; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.Runfiles; |
| import com.google.devtools.build.lib.analysis.RunfilesProvider; |
| import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; |
| import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; |
| import com.google.devtools.build.lib.analysis.test.TestProvider; |
| import com.google.devtools.build.lib.packages.BuildType; |
| import com.google.devtools.build.lib.packages.TestTargetUtils; |
| 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.List; |
| |
| /** |
| * Implementation for the "test_suite" rule. |
| */ |
| public class TestSuite implements RuleConfiguredTargetFactory { |
| |
| @Override |
| public ConfiguredTarget create(RuleContext ruleContext) |
| throws InterruptedException, RuleErrorException, ActionConflictException { |
| checkTestsAndSuites(ruleContext, "tests"); |
| if (ruleContext.hasErrors()) { |
| return null; |
| } |
| |
| // |
| // CAUTION! Keep this logic consistent with lib.query2.TestsExpression! |
| // |
| |
| List<String> tagsAttribute = new ArrayList<>( |
| ruleContext.attributes().get("tags", Type.STRING_LIST)); |
| // TODO(ulfjack): This is inconsistent with the other places that do test_suite expansion. |
| tagsAttribute.remove("manual"); |
| Pair<Collection<String>, Collection<String>> requiredExcluded = |
| TestTargetUtils.sortTagsBySense(tagsAttribute); |
| |
| List<TransitiveInfoCollection> directTestsAndSuitesBuilder = new ArrayList<>(); |
| |
| // The set of implicit tests is determined in |
| // {@link com.google.devtools.build.lib.packages.Package}. |
| // Manual tests are already filtered out there. That is what $implicit_tests is about. |
| for (TransitiveInfoCollection dep : |
| Iterables.concat( |
| getPrerequisites(ruleContext, "tests"), |
| getPrerequisites(ruleContext, "$implicit_tests"))) { |
| if (dep.getProvider(TestProvider.class) != null) { |
| // getTestTags maps to Rule.getRuleTags. |
| List<String> tags = dep.getProvider(TestProvider.class).getTestTags(); |
| if (!TestTargetUtils.testMatchesFilters( |
| tags, requiredExcluded.first, requiredExcluded.second)) { |
| // This test does not match our filter. Ignore it. |
| continue; |
| } |
| } |
| directTestsAndSuitesBuilder.add(dep); |
| } |
| |
| Runfiles runfiles = new Runfiles.Builder( |
| ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles()) |
| .addTargets(directTestsAndSuitesBuilder, RunfilesProvider.DATA_RUNFILES) |
| .build(); |
| |
| return new RuleConfiguredTargetBuilder(ruleContext) |
| .add(RunfilesProvider.class, |
| RunfilesProvider.withData(Runfiles.EMPTY, runfiles)) |
| .add(TransitiveTestsProvider.class, new TransitiveTestsProvider()) |
| .build(); |
| } |
| |
| private Iterable<? extends TransitiveInfoCollection> getPrerequisites( |
| RuleContext ruleContext, String attributeName) { |
| if (ruleContext.attributes().has(attributeName, BuildType.LABEL_LIST)) { |
| return ruleContext.getPrerequisites(attributeName, Mode.TARGET); |
| } else { |
| return ImmutableList.<TransitiveInfoCollection>of(); |
| } |
| } |
| |
| private void checkTestsAndSuites(RuleContext ruleContext, String attributeName) { |
| if (!ruleContext.attributes().has(attributeName, BuildType.LABEL_LIST)) { |
| return; |
| } |
| for (TransitiveInfoCollection dep : ruleContext.getPrerequisites(attributeName, Mode.TARGET)) { |
| // TODO(bazel-team): Maybe convert the TransitiveTestsProvider into an inner interface. |
| TransitiveTestsProvider provider = dep.getProvider(TransitiveTestsProvider.class); |
| TestProvider testProvider = dep.getProvider(TestProvider.class); |
| if (provider == null && testProvider == null) { |
| ruleContext.attributeError(attributeName, |
| "expecting a test or a test_suite rule but '" + dep.getLabel() + "' is not one"); |
| } |
| } |
| } |
| } |