| // 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.packages; | |
| import com.google.devtools.build.lib.util.Pair; | |
| import java.util.Collection; | |
| import java.util.Collections; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Set; | |
| /** | |
| * Utility functions over test Targets that don't really belong in the base {@link Target} | |
| * interface. | |
| */ | |
| public final class TestTargetUtils { | |
| /** | |
| * Returns whether a test with the specified tags matches a filter (as specified by the set | |
| * of its positive and its negative filters). | |
| */ | |
| public static boolean testMatchesFilters( | |
| Collection<String> testTags, | |
| Collection<String> requiredTags, | |
| Collection<String> excludedTags, | |
| boolean mustMatchAllPositive) { | |
| for (String tag : excludedTags) { | |
| if (testTags.contains(tag)) { | |
| return false; | |
| } | |
| } | |
| // Check required tags, if there are any. | |
| if (requiredTags.isEmpty()) { | |
| return true; | |
| } else if (mustMatchAllPositive) { | |
| // Require all tags to be present. | |
| for (String tag : requiredTags) { | |
| if (!testTags.contains(tag)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } else { | |
| // Require at least one positive tag. If the two collections are not disjoint, then they have | |
| // at least one element in common. | |
| return !Collections.disjoint(requiredTags, testTags); | |
| } | |
| } | |
| /** | |
| * Decides whether to include a test in a test_suite or not. | |
| * @param testTags Collection of all tags exhibited by a given test. | |
| * @param requiredTags Tags declared by the suite. A Test must match ALL of these. | |
| * @param excludedTags Tags declared by the suite. A Test must match NONE of these. | |
| * @return false is the test is to be removed. | |
| */ | |
| public static boolean testMatchesFilters( | |
| Collection<String> testTags, | |
| Collection<String> requiredTags, | |
| Collection<String> excludedTags) { | |
| return testMatchesFilters( | |
| testTags, requiredTags, excludedTags, /* mustMatchAllPositive= */ true); | |
| } | |
| /** | |
| * Decides whether to include a test in a test_suite or not. | |
| * @param testTarget A given test target. | |
| * @param requiredTags Tags declared by the suite. A Test must match ALL of these. | |
| * @param excludedTags Tags declared by the suite. A Test must match NONE of these. | |
| * @return false is the test is to be removed. | |
| */ | |
| private static boolean testMatchesFilters( | |
| Rule testTarget, | |
| Collection<String> requiredTags, | |
| Collection<String> excludedTags) { | |
| AttributeMap nonConfigurableAttrs = NonconfigurableAttributeMapper.of(testTarget); | |
| Set<String> testTags = new HashSet<>(nonConfigurableAttrs.get("tags", Type.STRING_LIST)); | |
| testTags.add(nonConfigurableAttrs.get("size", Type.STRING)); | |
| return testMatchesFilters(testTags, requiredTags, excludedTags); | |
| } | |
| /** | |
| * Filters 'tests' (by mutation) according to the 'tags' attribute, specifically those that | |
| * match ALL of the tags in tagsAttribute. | |
| * | |
| * @precondition {@code env.getAccessor().isTestSuite(testSuite)} | |
| * @precondition {@code env.getAccessor().isTestRule(test)} for all test in tests | |
| */ | |
| public static void filterTests(Rule testSuite, Set<Target> tests) { | |
| List<String> tagsAttribute = | |
| NonconfigurableAttributeMapper.of(testSuite).get("tags", Type.STRING_LIST); | |
| // Split the tags list into positive and negative tags | |
| Pair<Collection<String>, Collection<String>> tagLists = sortTagsBySense(tagsAttribute); | |
| Collection<String> positiveTags = tagLists.first; | |
| Collection<String> negativeTags = tagLists.second; | |
| tests.removeIf((Target t) -> !testMatchesFilters((Rule) t, positiveTags, negativeTags)); | |
| } | |
| /** | |
| * Separates a list of text "tags" into a Pair of Collections, where | |
| * the first element are the required or positive tags and the second element | |
| * are the excluded or negative tags. | |
| * This should work on tag list provided from the command line | |
| * --test_tags_filters flag or on tag filters explicitly declared in the | |
| * suite. | |
| * | |
| * @param tagList A collection of text targets to separate. | |
| */ | |
| public static Pair<Collection<String>, Collection<String>> sortTagsBySense( | |
| Iterable<String> tagList) { | |
| Collection<String> requiredTags = new HashSet<>(); | |
| Collection<String> excludedTags = new HashSet<>(); | |
| for (String tag : tagList) { | |
| if (tag.startsWith("-")) { | |
| excludedTags.add(tag.substring(1)); | |
| } else if (tag.startsWith("+")) { | |
| requiredTags.add(tag.substring(1)); | |
| } else if (!tag.equals("manual")) { | |
| // Ignore manual attribute because it is an exception: it is not a filter but a property of | |
| // test_suite. | |
| requiredTags.add(tag); | |
| } | |
| } | |
| return Pair.of(requiredTags, excludedTags); | |
| } | |
| } |