blob: bbba3f434e169e1d700f2331f6dd45a826cac678 [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
Googlerad4c1332022-09-26 11:32:36 -070019import com.google.common.collect.ImmutableList;
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;
Googlerfa05a102022-09-15 05:53:13 -070027import com.google.devtools.build.lib.skyframe.TargetLoadingUtil.TargetAndErrorIfAny;
adonovanc2fe3d82020-09-29 13:49:55 -070028import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
adonovanc2fe3d82020-09-29 13:49:55 -070029import com.google.devtools.build.skyframe.EvaluationResult;
Googler9d3b3bf2023-02-27 13:20:23 -080030import com.google.devtools.build.skyframe.GroupedDeps;
Googlerd44d4ea2022-09-26 19:20:36 -070031import com.google.devtools.build.skyframe.SimpleSkyframeLookupResult;
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;
40import 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.
Googler876bf372022-07-05 04:56:30 -070049 Label label = Label.parseCanonical("//foo:foo");
adonovanc2fe3d82020-09-29 13:49:55 -070050 scratch.file("foo/BUILD", "sh_library(name = '" + label.getName() + "')");
51 Package pkg = loadPackage(label.getPackageIdentifier());
Googlerfa05a102022-09-15 05:53:13 -070052 TargetAndErrorIfAny targetAndErrorIfAny =
53 new TargetAndErrorIfAny(
Googler9d3b3bf2023-02-27 13:20:23 -080054 /* packageLoadedSuccessfully= */ true,
55 /* errorLoadingTarget= */ null,
shreyax246f0aa2018-02-23 07:46:54 -080056 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 };
Googler9d3b3bf2023-02-27 13:20:23 -080064 // Create the GroupedDeps saying we had already requested two targets the last time we called
shreyax246f0aa2018-02-23 07:46:54 -080065 // #compute.
Googler9d3b3bf2023-02-27 13:20:23 -080066 GroupedDeps groupedDeps = new GroupedDeps();
Googler31c5a722023-04-26 09:53:00 -070067 groupedDeps.appendSingleton(label.getPackageIdentifier());
shreyax246f0aa2018-02-23 07:46:54 -080068 // Note that these targets don't actually exist in the package we created initially. It doesn't
69 // matter for the purpose of this test, the original package was just to create some objects
70 // that we needed.
Googlerad4c1332022-09-26 11:32:36 -070071 SkyKey fakeDep1 = function.getKey(Label.parseCanonical("//foo:bar"));
72 SkyKey fakeDep2 = function.getKey(Label.parseCanonical("//foo:baz"));
Googler9d3b3bf2023-02-27 13:20:23 -080073 groupedDeps.appendGroup(ImmutableList.of(fakeDep1, fakeDep2));
Googlerad4c1332022-09-26 11:32:36 -070074
shreyax246f0aa2018-02-23 07:46:54 -080075 AtomicBoolean wasOptimizationUsed = new AtomicBoolean(false);
76 SkyFunction.Environment mockEnv = Mockito.mock(SkyFunction.Environment.class);
Googler9d3b3bf2023-02-27 13:20:23 -080077 when(mockEnv.getTemporaryDirectDeps()).thenReturn(groupedDeps);
Googler1b4472f2023-03-02 19:09:05 -080078 when(mockEnv.getValuesAndExceptions(groupedDeps.getDepGroup(1)))
shreyax246f0aa2018-02-23 07:46:54 -080079 .thenAnswer(
80 (invocationOnMock) -> {
81 wasOptimizationUsed.set(true);
emilyguo91c1f9f2022-02-28 11:39:38 -080082 // It doesn't matter what this SkyframeLookupResult is, we'll return true in the
83 // valuesMissing() call.
Googlerd44d4ea2022-09-26 19:20:36 -070084 return new SimpleSkyframeLookupResult(
emilyguo91c1f9f2022-02-28 11:39:38 -080085 /* valuesMissingCallback= */ () -> {},
86 k -> {
87 throw new IllegalStateException("Shouldn't have been called: " + k);
88 });
shreyax246f0aa2018-02-23 07:46:54 -080089 });
90 when(mockEnv.valuesMissing()).thenReturn(true);
91
92 // Run the compute function and check that we returned null.
93 assertThat(function.compute(function.getKey(label), mockEnv)).isNull();
94
95 // Verify that the mock was called with the arguments we expected.
96 assertThat(wasOptimizationUsed.get()).isTrue();
97 }
98
janakrf091f9c2019-03-25 13:42:18 -070099 @Test
100 public void multipleErrorsForTransitiveTraversalFunction() throws Exception {
Googler876bf372022-07-05 04:56:30 -0700101 Label label = Label.parseCanonical("//foo:foo");
adonovanc2fe3d82020-09-29 13:49:55 -0700102 scratch.file(
103 "foo/BUILD", "sh_library(name = '" + label.getName() + "', deps = [':bar', ':baz'])");
104 Package pkg = loadPackage(label.getPackageIdentifier());
Googlerfa05a102022-09-15 05:53:13 -0700105 TargetAndErrorIfAny targetAndErrorIfAny =
106 new TargetAndErrorIfAny(
Googler1b4472f2023-03-02 19:09:05 -0800107 /* packageLoadedSuccessfully= */ true,
108 /* errorLoadingTarget= */ null,
janakrf091f9c2019-03-25 13:42:18 -0700109 pkg.getTarget(label.getName()));
110 TransitiveTraversalFunction function =
111 new TransitiveTraversalFunction() {
112 @Override
ulfjack731451f2019-05-09 04:30:35 -0700113 TargetAndErrorIfAny loadTarget(Environment env, Label label) {
janakrf091f9c2019-03-25 13:42:18 -0700114 return targetAndErrorIfAny;
115 }
116 };
Googler876bf372022-07-05 04:56:30 -0700117 SkyKey dep1 = function.getKey(Label.parseCanonical("//foo:bar"));
118 SkyKey dep2 = function.getKey(Label.parseCanonical("//foo:baz"));
janakrf091f9c2019-03-25 13:42:18 -0700119 SkyFunction.Environment mockEnv = Mockito.mock(SkyFunction.Environment.class);
emilyguo91c1f9f2022-02-28 11:39:38 -0800120 NoSuchTargetException exp1 = new NoSuchTargetException("bad bar");
121 NoSuchTargetException exp2 = new NoSuchTargetException("bad baz");
122 SkyframeLookupResult returnedDeps =
Googlerd44d4ea2022-09-26 19:20:36 -0700123 new SimpleSkyframeLookupResult(
emilyguo91c1f9f2022-02-28 11:39:38 -0800124 () -> {},
125 key ->
126 key.equals(dep1)
127 ? ValueOrUntypedException.ofExn(exp1)
128 : key.equals(dep2) ? ValueOrUntypedException.ofExn(exp2) : null);
129
130 when(mockEnv.getValuesAndExceptions(ImmutableSet.of(dep1, dep2))).thenReturn(returnedDeps);
janakrf091f9c2019-03-25 13:42:18 -0700131 when(mockEnv.valuesMissing()).thenReturn(false);
132
133 assertThat(
134 ((TransitiveTraversalValue) function.compute(function.getKey(label), mockEnv))
135 .getErrorMessage())
136 .isEqualTo("bad bar");
janakrf091f9c2019-03-25 13:42:18 -0700137 }
138
139 @Test
140 public void selfErrorWins() throws Exception {
Googler876bf372022-07-05 04:56:30 -0700141 Label label = Label.parseCanonical("//foo:foo");
emilyguo91c1f9f2022-02-28 11:39:38 -0800142 scratch.file("foo/BUILD", "sh_library(name = '" + label.getName() + "', deps = [':bar'])");
adonovanc2fe3d82020-09-29 13:49:55 -0700143 Package pkg = loadPackage(label.getPackageIdentifier());
Googlerfa05a102022-09-15 05:53:13 -0700144 TargetAndErrorIfAny targetAndErrorIfAny =
145 new TargetAndErrorIfAny(
Googler1b4472f2023-03-02 19:09:05 -0800146 /* packageLoadedSuccessfully= */ true,
147 /* errorLoadingTarget= */ new NoSuchTargetException("self error is long and last"),
janakrf091f9c2019-03-25 13:42:18 -0700148 pkg.getTarget(label.getName()));
149 TransitiveTraversalFunction function =
150 new TransitiveTraversalFunction() {
151 @Override
ulfjack731451f2019-05-09 04:30:35 -0700152 TargetAndErrorIfAny loadTarget(Environment env, Label label) {
janakrf091f9c2019-03-25 13:42:18 -0700153 return targetAndErrorIfAny;
154 }
155 };
Googler876bf372022-07-05 04:56:30 -0700156 SkyKey dep = function.getKey(Label.parseCanonical("//foo:bar"));
emilyguo91c1f9f2022-02-28 11:39:38 -0800157 NoSuchTargetException exp = new NoSuchTargetException("bad bar");
158 SkyframeLookupResult returnedDep =
Googlerd44d4ea2022-09-26 19:20:36 -0700159 new SimpleSkyframeLookupResult(
emilyguo91c1f9f2022-02-28 11:39:38 -0800160 () -> {}, key -> key.equals(dep) ? ValueOrUntypedException.ofExn(exp) : null);
janakrf091f9c2019-03-25 13:42:18 -0700161 SkyFunction.Environment mockEnv = Mockito.mock(SkyFunction.Environment.class);
emilyguo91c1f9f2022-02-28 11:39:38 -0800162 when(mockEnv.getValuesAndExceptions(ImmutableSet.of(dep))).thenReturn(returnedDep);
janakrf091f9c2019-03-25 13:42:18 -0700163 when(mockEnv.valuesMissing()).thenReturn(false);
164
ulfjack731451f2019-05-09 04:30:35 -0700165 TransitiveTraversalValue transitiveTraversalValue =
166 (TransitiveTraversalValue) function.compute(function.getKey(label), mockEnv);
167 assertThat(transitiveTraversalValue.getErrorMessage()).isEqualTo("self error is long and last");
janakrf091f9c2019-03-25 13:42:18 -0700168 }
169
emilyguo91c1f9f2022-02-28 11:39:38 -0800170 @Test
171 public void getStrictLabelAspectKeys() throws Exception {
Googler876bf372022-07-05 04:56:30 -0700172 Label label = Label.parseCanonical("//test:foo");
emilyguo91c1f9f2022-02-28 11:39:38 -0800173 scratch.file(
174 "test/aspect.bzl",
Googler09bf5472024-03-28 10:08:10 -0700175 """
176 def _aspect_impl(target, ctx):
Googlerb9999f42024-10-02 07:49:19 -0700177 return []
Googler09bf5472024-03-28 10:08:10 -0700178
179 def _rule_impl(ctx):
Googlerb9999f42024-10-02 07:49:19 -0700180 return []
Googler09bf5472024-03-28 10:08:10 -0700181
182 MyAspect = aspect(
183 implementation = _aspect_impl,
184 attr_aspects = ["deps"],
185 attrs = {"_extra_deps": attr.label(default = Label("//foo:bar"))},
186 )
187 my_rule = rule(
188 implementation = _rule_impl,
189 attrs = {
190 "attr": attr.label_list(mandatory = True, aspects = [MyAspect]),
191 },
192 )
193 """);
emilyguo91c1f9f2022-02-28 11:39:38 -0800194 scratch.file(
195 "test/BUILD",
Googler09bf5472024-03-28 10:08:10 -0700196 """
197 load("//test:aspect.bzl", "my_rule")
198
199 my_rule(
200 name = "foo",
201 attr = [":bad"],
202 )
203 """);
emilyguo91c1f9f2022-02-28 11:39:38 -0800204 Package pkg = loadPackage(label.getPackageIdentifier());
Googlerfa05a102022-09-15 05:53:13 -0700205 TargetAndErrorIfAny targetAndErrorIfAny =
206 new TargetAndErrorIfAny(
Googler1b4472f2023-03-02 19:09:05 -0800207 /* packageLoadedSuccessfully= */ true,
208 /* errorLoadingTarget= */ null,
emilyguo91c1f9f2022-02-28 11:39:38 -0800209 pkg.getTarget(label.getName()));
210 TransitiveTraversalFunction function =
211 new TransitiveTraversalFunction() {
212 @Override
213 TargetAndErrorIfAny loadTarget(Environment env, Label label) {
214 return targetAndErrorIfAny;
215 }
216 };
Googler876bf372022-07-05 04:56:30 -0700217 SkyKey badDep = function.getKey(Label.parseCanonical("//test:bad"));
emilyguo91c1f9f2022-02-28 11:39:38 -0800218 NoSuchTargetException exp = new NoSuchTargetException("bad test");
219 AtomicBoolean valuesMissing = new AtomicBoolean(false);
220 SkyframeLookupResult returnedDep =
Googlerd44d4ea2022-09-26 19:20:36 -0700221 new SimpleSkyframeLookupResult(
emilyguo91c1f9f2022-02-28 11:39:38 -0800222 () -> valuesMissing.set(true),
223 key -> key.equals(badDep) ? ValueOrUntypedException.ofExn(exp) : null);
224 SkyFunction.Environment mockEnv = Mockito.mock(SkyFunction.Environment.class);
225 when(mockEnv.getValuesAndExceptions(ImmutableSet.of(badDep))).thenReturn(returnedDep);
226
227 TransitiveTraversalValue transitiveTraversalValue =
228 (TransitiveTraversalValue) function.compute(function.getKey(label), mockEnv);
229 assertThat(transitiveTraversalValue.getErrorMessage()).isEqualTo("bad test");
230 assertThat(valuesMissing.get()).isFalse();
janakrf091f9c2019-03-25 13:42:18 -0700231 }
232
adonovanc2fe3d82020-09-29 13:49:55 -0700233 /* Invokes the loading phase, using Skyframe. */
234 private Package loadPackage(PackageIdentifier pkgid)
235 throws InterruptedException, NoSuchPackageException {
adonovanc2fe3d82020-09-29 13:49:55 -0700236 EvaluationResult<PackageValue> result =
237 SkyframeExecutorTestUtils.evaluate(
Googler31c5a722023-04-26 09:53:00 -0700238 getSkyframeExecutor(), pkgid, /* keepGoing= */ false, reporter);
adonovanc2fe3d82020-09-29 13:49:55 -0700239 if (result.hasError()) {
Googler31c5a722023-04-26 09:53:00 -0700240 throw (NoSuchPackageException) result.getError(pkgid).getException();
adonovanc2fe3d82020-09-29 13:49:55 -0700241 }
Googler31c5a722023-04-26 09:53:00 -0700242 return result.get(pkgid).getPackage();
shreyax246f0aa2018-02-23 07:46:54 -0800243 }
244}