// 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.pkgcache; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.ExtendedEventHandler; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.packages.TestSize; import com.google.devtools.build.lib.packages.TestTimeout; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import javax.annotation.Nullable; /** * Predicate that implements test filtering using the command-line options in {@link * LoadingOptions}. Implements {@link #hashCode} and {@link #equals} so it can be used as a Skyframe * key. */ @AutoCodec public final class TestFilter implements com.google.common.base.Predicate { private static final Predicate ALWAYS_TRUE = (t) -> true; /** Convert the options into a test filter. */ public static TestFilter forOptions( LoadingOptions options, ExtendedEventHandler eventHandler, Set ruleNames) { if (!options.testLangFilterList.isEmpty()) { checkLangFilters(options.testLangFilterList, eventHandler, ruleNames); } return new TestFilter( ImmutableSet.copyOf(options.testSizeFilterSet), ImmutableSet.copyOf(options.testTimeoutFilterSet), ImmutableList.copyOf(options.testTagFilterList), ImmutableList.copyOf(options.testLangFilterList)); } private final ImmutableSet testSizeFilterSet; private final ImmutableSet testTimeoutFilterSet; private final ImmutableList testTagFilterList; private final ImmutableList testLangFilterList; private final Predicate impl; @AutoCodec.VisibleForSerialization TestFilter( ImmutableSet testSizeFilterSet, ImmutableSet testTimeoutFilterSet, ImmutableList testTagFilterList, ImmutableList testLangFilterList) { this.testSizeFilterSet = testSizeFilterSet; this.testTimeoutFilterSet = testTimeoutFilterSet; this.testTagFilterList = testTagFilterList; this.testLangFilterList = testLangFilterList; Predicate testFilter = ALWAYS_TRUE; if (!testSizeFilterSet.isEmpty()) { testFilter = testFilter.and(testSizeFilter(testSizeFilterSet)); } if (!testTimeoutFilterSet.isEmpty()) { testFilter = testFilter.and(testTimeoutFilter(testTimeoutFilterSet)); } if (!testTagFilterList.isEmpty()) { testFilter = testFilter.and(TargetUtils.tagFilter(testTagFilterList)); } if (!testLangFilterList.isEmpty()) { testFilter = testFilter.and(testLangFilter(testLangFilterList)); } impl = testFilter; } @Override public boolean apply(@Nullable Target input) { return impl.test(input); } @Override public int hashCode() { return Objects.hash(testSizeFilterSet, testTimeoutFilterSet, testTagFilterList, testLangFilterList); } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof TestFilter)) { return false; } TestFilter f = (TestFilter) o; return f.testSizeFilterSet.equals(testSizeFilterSet) && f.testTimeoutFilterSet.equals(testTimeoutFilterSet) && f.testTagFilterList.equals(testTagFilterList) && f.testLangFilterList.equals(testLangFilterList); } /** * Returns a predicate to be used for test size filtering, i.e., that only accepts tests of the * given size. */ @VisibleForTesting public static Predicate testSizeFilter(final Set allowedSizes) { return target -> target instanceof Rule && allowedSizes.contains(TestSize.getTestSize((Rule) target)); } /** * Returns a predicate to be used for test timeout filtering, i.e., that only accepts tests of the * given timeout. */ @VisibleForTesting public static Predicate testTimeoutFilter(final Set allowedTimeouts) { return target -> target instanceof Rule && allowedTimeouts.contains(TestTimeout.getTestTimeout((Rule) target)); } /** Check languages specified in --test_lang_filters and warn if any of them are unknown. */ private static void checkLangFilters( List langFilterList, ExtendedEventHandler reporter, Set allRuleNames) { for (String lang : langFilterList) { if (lang.startsWith("-")) { lang = lang.substring(1); } if (!allRuleNames.contains(lang + "_test")) { reporter.handle( Event.warn("Unknown language '" + lang + "' in --test_lang_filters option")); } } } /** * Returns a predicate to be used for test language filtering, i.e., that only accepts tests of * the specified languages. */ private static Predicate testLangFilter(List langFilterList) { final Set requiredLangs = new HashSet<>(); final Set excludedLangs = new HashSet<>(); for (String lang : langFilterList) { if (lang.startsWith("-")) { lang = lang.substring(1); excludedLangs.add(lang); } else { requiredLangs.add(lang); } } return rule -> { String ruleLang = TargetUtils.getRuleLanguage(rule); return (requiredLangs.isEmpty() || requiredLangs.contains(ruleLang)) && !excludedLangs.contains(ruleLang); }; } }