// 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.skyframe; import com.google.common.base.Predicate; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.cmdline.ResolvedTargets; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.packages.AttributeMap; import com.google.devtools.build.lib.packages.BuildFileNotFoundException; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper; import com.google.devtools.build.lib.packages.Package; 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.TestTargetUtils; import com.google.devtools.build.lib.skyframe.TestsInSuiteValue.TestsInSuiteKey; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.skyframe.SkyFunction; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import com.google.devtools.build.skyframe.ValueOrException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nullable; /** * TestsInSuiteFunction takes a single test_suite target and expands all of the tests it contains, * possibly recursively. */ // TODO(ulfjack): What about test_suite rules that include each other. final class TestsInSuiteFunction implements SkyFunction { @Override public SkyValue compute(SkyKey key, Environment env) throws InterruptedException { TestsInSuiteKey expansion = (TestsInSuiteKey) key.argument(); ResolvedTargets result = computeTestsInSuite(env, expansion.getTestSuite(), expansion.isStrict()); if (env.valuesMissing()) { return null; } return new TestsInSuiteValue(result); } /** * Populates 'result' with all the tests associated with the specified 'testSuite'. Throws an * exception if any target is missing. * *

CAUTION! Keep this logic consistent with {@code TestSuite}! */ private static ResolvedTargets computeTestsInSuite( Environment env, Rule testSuite, boolean strict) throws InterruptedException { ResolvedTargets.Builder builder = ResolvedTargets.builder(); List testsAndSuites = new ArrayList<>(); // Note that testsAndSuites can contain input file targets; the test_suite rule does not // restrict the set of targets that can appear in tests or suites. builder.mergeError(getPrerequisites(env, testSuite, "tests", testsAndSuites)); // 1. Add all tests for (Target test : testsAndSuites) { if (TargetUtils.isTestRule(test)) { builder.add(test); } else if (strict && !TargetUtils.isTestSuiteRule(test)) { // If strict mode is enabled, then give an error for any non-test, non-test-suite targets. // TODO(ulfjack): We need to throw to end the process if we happen to be in --nokeep_going, // but we can't know whether or not we are at this point. env.getListener().handle(Event.error(testSuite.getLocation(), "in test_suite rule '" + testSuite.getLabel() + "': expecting a test or a test_suite rule but '" + test.getLabel() + "' is not one.")); builder.setError(); } } // 2. Add implicit dependencies on tests in same package, if any. List implicitTests = new ArrayList<>(); builder.mergeError(getPrerequisites(env, testSuite, "$implicit_tests", implicitTests)); for (Target target : implicitTests) { // The Package construction of $implicit_tests ensures that this check never fails, but we // add it here anyway for compatibility with future code. if (TargetUtils.isTestRule(target)) { builder.add(target); } } // 3. Filter based on tags, size, env. filterTests(testSuite, builder); // 4. Expand all suites recursively. for (Target suite : testsAndSuites) { if (TargetUtils.isTestSuiteRule(suite)) { TestsInSuiteValue value = (TestsInSuiteValue) env.getValue(TestsInSuiteValue.key(suite, strict)); if (value == null) { continue; } builder.merge(value.getTargets()); } } return builder.build(); } /** * Adds the set of targets found in the attribute named {@code attrName}, which must be of label * list type, of the {@code test_suite} rule named {@code testSuite}. Returns true if the method * found a problem during the lookup process; the actual error message is reported to the * environment. */ private static boolean getPrerequisites( Environment env, Rule testSuite, String attrName, List targets) throws InterruptedException { List