blob: 2a56edd32d6ce7b1d240ab4de4c8cdd0243d7504 [file] [log] [blame]
shreyax246f0aa2018-02-23 07:46:54 -08001// Copyright 2018 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
16import static com.google.common.truth.Truth.assertThat;
17import static org.mockito.Mockito.when;
18
19import com.google.common.collect.ImmutableMap;
emilyguo91c1f9f2022-02-28 11:39:38 -080020import com.google.common.collect.ImmutableSet;
shreyax246f0aa2018-02-23 07:46:54 -080021import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
22import com.google.devtools.build.lib.cmdline.Label;
23import com.google.devtools.build.lib.cmdline.PackageIdentifier;
24import com.google.devtools.build.lib.packages.NoSuchPackageException;
25import com.google.devtools.build.lib.packages.NoSuchTargetException;
26import com.google.devtools.build.lib.packages.Package;
27import com.google.devtools.build.lib.skyframe.TransitiveBaseTraversalFunction.TargetAndErrorIfAnyImpl;
adonovanc2fe3d82020-09-29 13:49:55 -070028import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
shreyax246f0aa2018-02-23 07:46:54 -080029import com.google.devtools.build.lib.util.GroupedList;
30import com.google.devtools.build.lib.util.GroupedList.GroupedListHelper;
adonovanc2fe3d82020-09-29 13:49:55 -070031import com.google.devtools.build.skyframe.EvaluationResult;
shreyax246f0aa2018-02-23 07:46:54 -080032import com.google.devtools.build.skyframe.SkyFunction;
33import com.google.devtools.build.skyframe.SkyKey;
emilyguo91c1f9f2022-02-28 11:39:38 -080034import com.google.devtools.build.skyframe.SkyframeLookupResult;
janakrf091f9c2019-03-25 13:42:18 -070035import com.google.devtools.build.skyframe.ValueOrUntypedException;
shreyax246f0aa2018-02-23 07:46:54 -080036import java.util.concurrent.atomic.AtomicBoolean;
37import org.junit.Test;
38import org.junit.runner.RunWith;
39import org.junit.runners.JUnit4;
shreyax246f0aa2018-02-23 07:46:54 -080040import org.mockito.Mockito;
41
42/** Test for {@link TransitiveTraversalFunction}. */
43@RunWith(JUnit4.class)
44public class TransitiveTraversalFunctionTest extends BuildViewTestCase {
45
46 @Test
47 public void noRepeatedLabelVisitationForTransitiveTraversalFunction() throws Exception {
48 // Create a basic package with a target //foo:foo.
dannark90e2b4b2018-06-27 13:35:04 -070049 Label label = Label.parseAbsolute("//foo:foo", ImmutableMap.of());
adonovanc2fe3d82020-09-29 13:49:55 -070050 scratch.file("foo/BUILD", "sh_library(name = '" + label.getName() + "')");
51 Package pkg = loadPackage(label.getPackageIdentifier());
shreyax246f0aa2018-02-23 07:46:54 -080052 TargetAndErrorIfAnyImpl targetAndErrorIfAny =
53 new TargetAndErrorIfAnyImpl(
54 /*packageLoadedSuccessfully=*/ true,
55 /*errorLoadingTarget=*/ null,
56 pkg.getTarget(label.getName()));
57 TransitiveTraversalFunction function =
58 new TransitiveTraversalFunction() {
59 @Override
ulfjack731451f2019-05-09 04:30:35 -070060 TargetAndErrorIfAny loadTarget(Environment env, Label label) {
shreyax246f0aa2018-02-23 07:46:54 -080061 return targetAndErrorIfAny;
62 }
63 };
64 // Create the GroupedList saying we had already requested two targets the last time we called
65 // #compute.
66 GroupedListHelper<SkyKey> helper = new GroupedListHelper<>();
dannark90e2b4b2018-06-27 13:35:04 -070067 SkyKey fakeDep1 = function.getKey(Label.parseAbsolute("//foo:bar", ImmutableMap.of()));
68 SkyKey fakeDep2 = function.getKey(Label.parseAbsolute("//foo:baz", ImmutableMap.of()));
shreyax246f0aa2018-02-23 07:46:54 -080069 helper.add(PackageValue.key(label.getPackageIdentifier()));
70 helper.startGroup();
71 // Note that these targets don't actually exist in the package we created initially. It doesn't
72 // matter for the purpose of this test, the original package was just to create some objects
73 // that we needed.
74 helper.add(fakeDep1);
75 helper.add(fakeDep2);
76 helper.endGroup();
77 GroupedList<SkyKey> groupedList = new GroupedList<>();
78 groupedList.append(helper);
79 AtomicBoolean wasOptimizationUsed = new AtomicBoolean(false);
80 SkyFunction.Environment mockEnv = Mockito.mock(SkyFunction.Environment.class);
81 when(mockEnv.getTemporaryDirectDeps()).thenReturn(groupedList);
emilyguo91c1f9f2022-02-28 11:39:38 -080082 when(mockEnv.getValuesAndExceptions(groupedList.get(1)))
shreyax246f0aa2018-02-23 07:46:54 -080083 .thenAnswer(
84 (invocationOnMock) -> {
85 wasOptimizationUsed.set(true);
emilyguo91c1f9f2022-02-28 11:39:38 -080086 // It doesn't matter what this SkyframeLookupResult is, we'll return true in the
87 // valuesMissing() call.
88 return new SkyframeLookupResult(
89 /* valuesMissingCallback= */ () -> {},
90 k -> {
91 throw new IllegalStateException("Shouldn't have been called: " + k);
92 });
shreyax246f0aa2018-02-23 07:46:54 -080093 });
94 when(mockEnv.valuesMissing()).thenReturn(true);
95
96 // Run the compute function and check that we returned null.
97 assertThat(function.compute(function.getKey(label), mockEnv)).isNull();
98
99 // Verify that the mock was called with the arguments we expected.
100 assertThat(wasOptimizationUsed.get()).isTrue();
101 }
102
janakrf091f9c2019-03-25 13:42:18 -0700103 @Test
104 public void multipleErrorsForTransitiveTraversalFunction() throws Exception {
105 Label label = Label.parseAbsolute("//foo:foo", ImmutableMap.of());
adonovanc2fe3d82020-09-29 13:49:55 -0700106 scratch.file(
107 "foo/BUILD", "sh_library(name = '" + label.getName() + "', deps = [':bar', ':baz'])");
108 Package pkg = loadPackage(label.getPackageIdentifier());
janakrf091f9c2019-03-25 13:42:18 -0700109 TargetAndErrorIfAnyImpl targetAndErrorIfAny =
110 new TargetAndErrorIfAnyImpl(
111 /*packageLoadedSuccessfully=*/ true,
112 /*errorLoadingTarget=*/ null,
113 pkg.getTarget(label.getName()));
114 TransitiveTraversalFunction function =
115 new TransitiveTraversalFunction() {
116 @Override
ulfjack731451f2019-05-09 04:30:35 -0700117 TargetAndErrorIfAny loadTarget(Environment env, Label label) {
janakrf091f9c2019-03-25 13:42:18 -0700118 return targetAndErrorIfAny;
119 }
120 };
121 SkyKey dep1 = function.getKey(Label.parseAbsolute("//foo:bar", ImmutableMap.of()));
122 SkyKey dep2 = function.getKey(Label.parseAbsolute("//foo:baz", ImmutableMap.of()));
janakrf091f9c2019-03-25 13:42:18 -0700123 SkyFunction.Environment mockEnv = Mockito.mock(SkyFunction.Environment.class);
emilyguo91c1f9f2022-02-28 11:39:38 -0800124 NoSuchTargetException exp1 = new NoSuchTargetException("bad bar");
125 NoSuchTargetException exp2 = new NoSuchTargetException("bad baz");
126 SkyframeLookupResult returnedDeps =
127 new SkyframeLookupResult(
128 () -> {},
129 key ->
130 key.equals(dep1)
131 ? ValueOrUntypedException.ofExn(exp1)
132 : key.equals(dep2) ? ValueOrUntypedException.ofExn(exp2) : null);
133
134 when(mockEnv.getValuesAndExceptions(ImmutableSet.of(dep1, dep2))).thenReturn(returnedDeps);
janakrf091f9c2019-03-25 13:42:18 -0700135 when(mockEnv.valuesMissing()).thenReturn(false);
136
137 assertThat(
138 ((TransitiveTraversalValue) function.compute(function.getKey(label), mockEnv))
139 .getErrorMessage())
140 .isEqualTo("bad bar");
janakrf091f9c2019-03-25 13:42:18 -0700141 }
142
143 @Test
144 public void selfErrorWins() throws Exception {
145 Label label = Label.parseAbsolute("//foo:foo", ImmutableMap.of());
emilyguo91c1f9f2022-02-28 11:39:38 -0800146 scratch.file("foo/BUILD", "sh_library(name = '" + label.getName() + "', deps = [':bar'])");
adonovanc2fe3d82020-09-29 13:49:55 -0700147 Package pkg = loadPackage(label.getPackageIdentifier());
janakrf091f9c2019-03-25 13:42:18 -0700148 TargetAndErrorIfAnyImpl targetAndErrorIfAny =
149 new TargetAndErrorIfAnyImpl(
150 /*packageLoadedSuccessfully=*/ true,
151 /*errorLoadingTarget=*/ new NoSuchTargetException("self error is long and last"),
152 pkg.getTarget(label.getName()));
153 TransitiveTraversalFunction function =
154 new TransitiveTraversalFunction() {
155 @Override
ulfjack731451f2019-05-09 04:30:35 -0700156 TargetAndErrorIfAny loadTarget(Environment env, Label label) {
janakrf091f9c2019-03-25 13:42:18 -0700157 return targetAndErrorIfAny;
158 }
159 };
160 SkyKey dep = function.getKey(Label.parseAbsolute("//foo:bar", ImmutableMap.of()));
emilyguo91c1f9f2022-02-28 11:39:38 -0800161 NoSuchTargetException exp = new NoSuchTargetException("bad bar");
162 SkyframeLookupResult returnedDep =
163 new SkyframeLookupResult(
164 () -> {}, key -> key.equals(dep) ? ValueOrUntypedException.ofExn(exp) : null);
janakrf091f9c2019-03-25 13:42:18 -0700165 SkyFunction.Environment mockEnv = Mockito.mock(SkyFunction.Environment.class);
emilyguo91c1f9f2022-02-28 11:39:38 -0800166 when(mockEnv.getValuesAndExceptions(ImmutableSet.of(dep))).thenReturn(returnedDep);
janakrf091f9c2019-03-25 13:42:18 -0700167 when(mockEnv.valuesMissing()).thenReturn(false);
168
ulfjack731451f2019-05-09 04:30:35 -0700169 TransitiveTraversalValue transitiveTraversalValue =
170 (TransitiveTraversalValue) function.compute(function.getKey(label), mockEnv);
171 assertThat(transitiveTraversalValue.getErrorMessage()).isEqualTo("self error is long and last");
janakrf091f9c2019-03-25 13:42:18 -0700172 }
173
emilyguo91c1f9f2022-02-28 11:39:38 -0800174 @Test
175 public void getStrictLabelAspectKeys() throws Exception {
176 Label label = Label.parseAbsolute("//test:foo", ImmutableMap.of());
177 scratch.file(
178 "test/aspect.bzl",
179 "def _aspect_impl(target, ctx):",
180 " return struct()",
181 "def _rule_impl(ctx):",
182 " return struct()",
183 "",
184 "MyAspect = aspect(",
185 " implementation=_aspect_impl,",
186 " attr_aspects=['deps'],",
187 " attrs = { '_extra_deps' : attr.label(default = Label('//foo:bar'))},",
188 ")",
189 "my_rule = rule(",
190 " implementation=_rule_impl,",
191 " attrs = { 'attr' : ",
192 " attr.label_list(mandatory=True, aspects = [MyAspect]) ",
193 " },",
194 ")");
195 scratch.file(
196 "test/BUILD",
197 "load('//test:aspect.bzl', 'my_rule')",
198 "my_rule(name = 'foo',attr = [':bad'])");
199 Package pkg = loadPackage(label.getPackageIdentifier());
200 TargetAndErrorIfAnyImpl targetAndErrorIfAny =
201 new TargetAndErrorIfAnyImpl(
202 /*packageLoadedSuccessfully=*/ true,
203 /*errorLoadingTarget=*/ null,
204 pkg.getTarget(label.getName()));
205 TransitiveTraversalFunction function =
206 new TransitiveTraversalFunction() {
207 @Override
208 TargetAndErrorIfAny loadTarget(Environment env, Label label) {
209 return targetAndErrorIfAny;
210 }
211 };
212 SkyKey badDep = function.getKey(Label.parseAbsolute("//test:bad", ImmutableMap.of()));
213 NoSuchTargetException exp = new NoSuchTargetException("bad test");
214 AtomicBoolean valuesMissing = new AtomicBoolean(false);
215 SkyframeLookupResult returnedDep =
216 new SkyframeLookupResult(
217 () -> valuesMissing.set(true),
218 key -> key.equals(badDep) ? ValueOrUntypedException.ofExn(exp) : null);
219 SkyFunction.Environment mockEnv = Mockito.mock(SkyFunction.Environment.class);
220 when(mockEnv.getValuesAndExceptions(ImmutableSet.of(badDep))).thenReturn(returnedDep);
221
222 TransitiveTraversalValue transitiveTraversalValue =
223 (TransitiveTraversalValue) function.compute(function.getKey(label), mockEnv);
224 assertThat(transitiveTraversalValue.getErrorMessage()).isEqualTo("bad test");
225 assertThat(valuesMissing.get()).isFalse();
janakrf091f9c2019-03-25 13:42:18 -0700226 }
227
adonovanc2fe3d82020-09-29 13:49:55 -0700228 /* Invokes the loading phase, using Skyframe. */
229 private Package loadPackage(PackageIdentifier pkgid)
230 throws InterruptedException, NoSuchPackageException {
231 SkyKey key = PackageValue.key(pkgid);
232 EvaluationResult<PackageValue> result =
233 SkyframeExecutorTestUtils.evaluate(
234 getSkyframeExecutor(), key, /*keepGoing=*/ false, reporter);
235 if (result.hasError()) {
236 throw (NoSuchPackageException) result.getError(key).getException();
237 }
238 return result.get(key).getPackage();
shreyax246f0aa2018-02-23 07:46:54 -0800239 }
240}