blob: 67574159c0faa19c529dc8de35e2eb2621352068 [file] [log] [blame]
Ulf Adamsd6fce442015-10-14 10:08:25 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.skyframe;
15
ulfjackaa2ff992018-06-07 07:05:37 -070016import com.google.common.collect.ImmutableSet;
Ulf Adamsd6fce442015-10-14 10:08:25 +000017import com.google.devtools.build.lib.cmdline.Label;
18import com.google.devtools.build.lib.cmdline.PackageIdentifier;
19import com.google.devtools.build.lib.cmdline.ResolvedTargets;
20import com.google.devtools.build.lib.events.Event;
Ulf Adamsd6fce442015-10-14 10:08:25 +000021import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
22import com.google.devtools.build.lib.packages.BuildType;
23import com.google.devtools.build.lib.packages.NoSuchTargetException;
24import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper;
25import com.google.devtools.build.lib.packages.Package;
26import com.google.devtools.build.lib.packages.Rule;
27import com.google.devtools.build.lib.packages.Target;
28import com.google.devtools.build.lib.packages.TargetUtils;
29import com.google.devtools.build.lib.packages.TestTargetUtils;
ulfjack72dfe942017-07-21 10:11:02 +020030import com.google.devtools.build.lib.skyframe.TestsInSuiteValue.TestsInSuiteKey;
Ulf Adamsd6fce442015-10-14 10:08:25 +000031import com.google.devtools.build.skyframe.SkyFunction;
32import com.google.devtools.build.skyframe.SkyKey;
33import com.google.devtools.build.skyframe.SkyValue;
34import com.google.devtools.build.skyframe.ValueOrException;
Ulf Adamsd6fce442015-10-14 10:08:25 +000035import java.util.ArrayList;
Ulf Adamsd6fce442015-10-14 10:08:25 +000036import java.util.HashMap;
ulfjackaa2ff992018-06-07 07:05:37 -070037import java.util.HashSet;
Ulf Adamsd6fce442015-10-14 10:08:25 +000038import java.util.LinkedHashSet;
39import java.util.List;
40import java.util.Map;
Ulf Adamsd6fce442015-10-14 10:08:25 +000041import java.util.Set;
cpeyser10453002018-05-16 08:31:01 -070042import java.util.stream.Collectors;
Ulf Adamsd6fce442015-10-14 10:08:25 +000043import javax.annotation.Nullable;
44
45/**
46 * TestsInSuiteFunction takes a single test_suite target and expands all of the tests it contains,
47 * possibly recursively.
48 */
49// TODO(ulfjack): What about test_suite rules that include each other.
50final class TestsInSuiteFunction implements SkyFunction {
51 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000052 public SkyValue compute(SkyKey key, Environment env) throws InterruptedException {
ulfjack72dfe942017-07-21 10:11:02 +020053 TestsInSuiteKey expansion = (TestsInSuiteKey) key.argument();
cpeyser807bdbc2018-05-03 12:41:33 -070054 SkyKey packageKey = PackageValue.key(expansion.getTestSuiteLabel().getPackageIdentifier());
55 PackageValue pkg = (PackageValue) env.getValue(packageKey);
56 if (env.valuesMissing()) {
57 return null;
58 }
59 Rule testSuite = pkg.getPackage().getRule(expansion.getTestSuiteLabel().getName());
cpeyser10453002018-05-16 08:31:01 -070060 ResolvedTargets<Label> result = computeTestsInSuite(env, testSuite, expansion.isStrict());
Ulf Adamsd6fce442015-10-14 10:08:25 +000061 if (env.valuesMissing()) {
62 return null;
63 }
64 return new TestsInSuiteValue(result);
65 }
66
cpeyser10453002018-05-16 08:31:01 -070067 private static Set<Label> toLabels(Set<Target> targets) {
68 return targets.stream().map(Target::getLabel).collect(Collectors.toSet());
69 }
70
Ulf Adamsd6fce442015-10-14 10:08:25 +000071 /**
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000072 * Populates 'result' with all the tests associated with the specified 'testSuite'. Throws an
73 * exception if any target is missing.
Ulf Adamsd6fce442015-10-14 10:08:25 +000074 *
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000075 * <p>CAUTION! Keep this logic consistent with {@code TestSuite}!
Ulf Adamsd6fce442015-10-14 10:08:25 +000076 */
cpeyser10453002018-05-16 08:31:01 -070077 private static ResolvedTargets<Label> computeTestsInSuite(
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000078 Environment env, Rule testSuite, boolean strict) throws InterruptedException {
ulfjackaa2ff992018-06-07 07:05:37 -070079 Set<Target> result = new HashSet<>();
80 boolean hasError = false;
81
Ulf Adamsd6fce442015-10-14 10:08:25 +000082 List<Target> testsAndSuites = new ArrayList<>();
83 // Note that testsAndSuites can contain input file targets; the test_suite rule does not
84 // restrict the set of targets that can appear in tests or suites.
ulfjackaa2ff992018-06-07 07:05:37 -070085 hasError |= getPrerequisites(env, testSuite, "tests", testsAndSuites);
Ulf Adamsd6fce442015-10-14 10:08:25 +000086
87 // 1. Add all tests
88 for (Target test : testsAndSuites) {
89 if (TargetUtils.isTestRule(test)) {
ulfjackaa2ff992018-06-07 07:05:37 -070090 result.add(test);
Ulf Adamsd6fce442015-10-14 10:08:25 +000091 } else if (strict && !TargetUtils.isTestSuiteRule(test)) {
92 // If strict mode is enabled, then give an error for any non-test, non-test-suite targets.
93 // TODO(ulfjack): We need to throw to end the process if we happen to be in --nokeep_going,
94 // but we can't know whether or not we are at this point.
95 env.getListener().handle(Event.error(testSuite.getLocation(),
96 "in test_suite rule '" + testSuite.getLabel()
97 + "': expecting a test or a test_suite rule but '" + test.getLabel()
98 + "' is not one."));
ulfjackaa2ff992018-06-07 07:05:37 -070099 hasError = true;
Ulf Adamsd6fce442015-10-14 10:08:25 +0000100 }
101 }
102
103 // 2. Add implicit dependencies on tests in same package, if any.
104 List<Target> implicitTests = new ArrayList<>();
ulfjackaa2ff992018-06-07 07:05:37 -0700105 hasError |= getPrerequisites(env, testSuite, "$implicit_tests", implicitTests);
Ulf Adamsd6fce442015-10-14 10:08:25 +0000106 for (Target target : implicitTests) {
107 // The Package construction of $implicit_tests ensures that this check never fails, but we
108 // add it here anyway for compatibility with future code.
109 if (TargetUtils.isTestRule(target)) {
ulfjackaa2ff992018-06-07 07:05:37 -0700110 result.add(target);
Ulf Adamsd6fce442015-10-14 10:08:25 +0000111 }
112 }
113
114 // 3. Filter based on tags, size, env.
ulfjackaa2ff992018-06-07 07:05:37 -0700115 TestTargetUtils.filterTests(testSuite, result);
Ulf Adamsd6fce442015-10-14 10:08:25 +0000116
cpeyser10453002018-05-16 08:31:01 -0700117 // 4. Expand all suites recursively, collecting labels.
cpeyser10453002018-05-16 08:31:01 -0700118 ResolvedTargets.Builder<Label> labelsBuilder = ResolvedTargets.builder();
ulfjackaa2ff992018-06-07 07:05:37 -0700119 // Don't set filtered targets; they would be removed from the containing test suite.
120 labelsBuilder.merge(new ResolvedTargets<>(toLabels(result), ImmutableSet.of(), hasError));
cpeyser10453002018-05-16 08:31:01 -0700121
Ulf Adamsd6fce442015-10-14 10:08:25 +0000122 for (Target suite : testsAndSuites) {
123 if (TargetUtils.isTestSuiteRule(suite)) {
124 TestsInSuiteValue value =
125 (TestsInSuiteValue) env.getValue(TestsInSuiteValue.key(suite, strict));
Ulf Adamsddb7c272016-02-09 18:01:37 +0000126 if (value == null) {
127 continue;
128 }
cpeyser10453002018-05-16 08:31:01 -0700129 labelsBuilder.merge(value.getLabels());
Ulf Adamsd6fce442015-10-14 10:08:25 +0000130 }
131 }
132
cpeyser10453002018-05-16 08:31:01 -0700133 return labelsBuilder.build();
Ulf Adamsd6fce442015-10-14 10:08:25 +0000134 }
135
136 /**
137 * Adds the set of targets found in the attribute named {@code attrName}, which must be of label
138 * list type, of the {@code test_suite} rule named {@code testSuite}. Returns true if the method
139 * found a problem during the lookup process; the actual error message is reported to the
140 * environment.
141 */
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000142 private static boolean getPrerequisites(
143 Environment env, Rule testSuite, String attrName, List<Target> targets)
144 throws InterruptedException {
Ulf Adamsd6fce442015-10-14 10:08:25 +0000145 List<Label> labels =
146 NonconfigurableAttributeMapper.of(testSuite).get(attrName, BuildType.LABEL_LIST);
147 Set<PackageIdentifier> pkgIdentifiers = new LinkedHashSet<>();
148 for (Label label : labels) {
149 pkgIdentifiers.add(label.getPackageIdentifier());
150 }
151 Map<SkyKey, ValueOrException<BuildFileNotFoundException>> packages = env.getValuesOrThrow(
152 PackageValue.keys(pkgIdentifiers), BuildFileNotFoundException.class);
153 if (env.valuesMissing()) {
154 return false;
155 }
156 boolean hasError = false;
157 Map<PackageIdentifier, Package> packageMap = new HashMap<>();
jcatercecb3a82018-05-01 14:37:48 -0700158 for (Map.Entry<SkyKey, ValueOrException<BuildFileNotFoundException>> entry :
159 packages.entrySet()) {
Ulf Adamsd6fce442015-10-14 10:08:25 +0000160 try {
161 packageMap.put(
162 (PackageIdentifier) entry.getKey().argument(),
163 ((PackageValue) entry.getValue().get()).getPackage());
164 } catch (BuildFileNotFoundException e) {
165 env.getListener().handle(Event.error(e.getMessage()));
166 hasError = true;
167 }
168 }
169
170 for (Label label : labels) {
171 Package pkg = packageMap.get(label.getPackageIdentifier());
172 if (pkg == null) {
173 continue;
174 }
175 try {
176 targets.add(pkg.getTarget(label.getName()));
177 } catch (NoSuchTargetException e) {
178 env.getListener().handle(Event.error(e.getMessage()));
179 hasError = true;
180 }
181 }
182 return hasError;
183 }
184
Ulf Adamsd6fce442015-10-14 10:08:25 +0000185 @Nullable
186 @Override
187 public String extractTag(SkyKey skyKey) {
188 return null;
189 }
190}