blob: b3387bd2d3dd9153c87ebbbbf1a3989b1e7e1b29 [file] [log] [blame]
ulfjack9274cba2017-08-11 23:19:48 +02001// Copyright 2017 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.remote;
15
16import static com.google.common.truth.Truth.assertThat;
17import static org.mockito.Matchers.any;
18import static org.mockito.Matchers.eq;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020019import static org.mockito.Mockito.doThrow;
ulfjack9274cba2017-08-11 23:19:48 +020020import static org.mockito.Mockito.never;
21import static org.mockito.Mockito.verify;
22import static org.mockito.Mockito.when;
23
olaolaf0aa55d2018-08-16 08:51:06 -070024import build.bazel.remote.execution.v2.Action;
25import build.bazel.remote.execution.v2.ActionResult;
26import build.bazel.remote.execution.v2.Command;
27import build.bazel.remote.execution.v2.RequestMetadata;
ulfjack9274cba2017-08-11 23:19:48 +020028import com.google.common.collect.ImmutableList;
29import com.google.common.collect.ImmutableMap;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020030import com.google.common.eventbus.EventBus;
ulfjack9274cba2017-08-11 23:19:48 +020031import com.google.devtools.build.lib.actions.ActionInput;
ulfjack9274cba2017-08-11 23:19:48 +020032import com.google.devtools.build.lib.actions.ActionInputHelper;
33import com.google.devtools.build.lib.actions.Artifact;
34import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
olaolaa22d0e92017-12-11 07:53:15 -080035import com.google.devtools.build.lib.actions.ExecutionRequirements;
shahan499503b2018-06-07 18:57:07 -070036import com.google.devtools.build.lib.actions.MetadataProvider;
ulfjack9274cba2017-08-11 23:19:48 +020037import com.google.devtools.build.lib.actions.ResourceSet;
38import com.google.devtools.build.lib.actions.SimpleSpawn;
rupertsda40fbf2017-09-22 05:59:42 +020039import com.google.devtools.build.lib.actions.SpawnResult;
40import com.google.devtools.build.lib.actions.SpawnResult.Status;
buchgr559a07d2017-11-30 11:09:35 -080041import com.google.devtools.build.lib.clock.JavaClock;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020042import com.google.devtools.build.lib.events.Event;
43import com.google.devtools.build.lib.events.EventKind;
44import com.google.devtools.build.lib.events.Reporter;
45import com.google.devtools.build.lib.events.StoredEventHandler;
ulfjack9274cba2017-08-11 23:19:48 +020046import com.google.devtools.build.lib.exec.SpawnCache.CacheHandle;
47import com.google.devtools.build.lib.exec.SpawnInputExpander;
ulfjack9274cba2017-08-11 23:19:48 +020048import com.google.devtools.build.lib.exec.SpawnRunner.ProgressStatus;
tomlu29e306d2018-04-19 05:41:44 -070049import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext;
ulfjack9274cba2017-08-11 23:19:48 +020050import com.google.devtools.build.lib.exec.util.FakeOwner;
ulfjack9274cba2017-08-11 23:19:48 +020051import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
Googler922d1e62018-03-05 14:49:00 -080052import com.google.devtools.build.lib.remote.util.DigestUtil;
53import com.google.devtools.build.lib.remote.util.DigestUtil.ActionKey;
54import com.google.devtools.build.lib.remote.util.TracingMetadataUtils;
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -070055import com.google.devtools.build.lib.util.Pair;
ulfjack9274cba2017-08-11 23:19:48 +020056import com.google.devtools.build.lib.util.io.FileOutErr;
ccalvarinbda12a12018-06-21 18:57:26 -070057import com.google.devtools.build.lib.vfs.DigestHashFunction;
ulfjack9274cba2017-08-11 23:19:48 +020058import com.google.devtools.build.lib.vfs.FileSystem;
59import com.google.devtools.build.lib.vfs.FileSystemUtils;
60import com.google.devtools.build.lib.vfs.Path;
61import com.google.devtools.build.lib.vfs.PathFragment;
62import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
63import com.google.devtools.common.options.Options;
ulfjack9274cba2017-08-11 23:19:48 +020064import java.io.IOException;
65import java.time.Duration;
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -070066import java.util.ArrayList;
ulfjack9274cba2017-08-11 23:19:48 +020067import java.util.Collection;
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -070068import java.util.List;
ulfjack9274cba2017-08-11 23:19:48 +020069import java.util.SortedMap;
70import org.junit.Before;
71import org.junit.Test;
72import org.junit.runner.RunWith;
73import org.junit.runners.JUnit4;
74import org.mockito.Mock;
olaolaba8b0b32017-10-20 09:48:56 +020075import org.mockito.Mockito;
ulfjack9274cba2017-08-11 23:19:48 +020076import org.mockito.MockitoAnnotations;
olaolaba8b0b32017-10-20 09:48:56 +020077import org.mockito.invocation.InvocationOnMock;
78import org.mockito.stubbing.Answer;
ulfjack9274cba2017-08-11 23:19:48 +020079
80/** Tests for {@link RemoteSpawnCache}. */
81@RunWith(JUnit4.class)
82public class RemoteSpawnCacheTest {
83 private static final ArtifactExpander SIMPLE_ARTIFACT_EXPANDER =
84 new ArtifactExpander() {
85 @Override
86 public void expand(Artifact artifact, Collection<? super Artifact> output) {
87 output.add(artifact);
88 }
89 };
90
91 private FileSystem fs;
buchgr559a07d2017-11-30 11:09:35 -080092 private DigestUtil digestUtil;
ulfjack9274cba2017-08-11 23:19:48 +020093 private Path execRoot;
94 private SimpleSpawn simpleSpawn;
95 private FakeActionInputFileCache fakeFileCache;
Hadrien Chauvin3d0a04d2017-12-20 08:45:45 -080096 @Mock private AbstractRemoteActionCache remoteCache;
ulfjack9274cba2017-08-11 23:19:48 +020097 private RemoteSpawnCache cache;
98 private FileOutErr outErr;
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -070099 private final List<Pair<ProgressStatus, String>> progressUpdates = new ArrayList();
ulfjack9274cba2017-08-11 23:19:48 +0200100
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200101 private StoredEventHandler eventHandler = new StoredEventHandler();
102
tomlu29e306d2018-04-19 05:41:44 -0700103 private final SpawnExecutionContext simplePolicy =
104 new SpawnExecutionContext() {
ulfjack9274cba2017-08-11 23:19:48 +0200105 @Override
106 public int getId() {
107 return 0;
108 }
109
110 @Override
111 public void prefetchInputs() {
112 // CachedLocalSpawnRunner should never prefetch itself, though the nested SpawnRunner may.
113 throw new UnsupportedOperationException();
114 }
115
116 @Override
117 public void lockOutputFiles() throws InterruptedException {
118 throw new UnsupportedOperationException();
119 }
120
121 @Override
122 public boolean speculating() {
123 return false;
124 }
125
126 @Override
shahan499503b2018-06-07 18:57:07 -0700127 public MetadataProvider getMetadataProvider() {
ulfjack9274cba2017-08-11 23:19:48 +0200128 return fakeFileCache;
129 }
130
131 @Override
132 public ArtifactExpander getArtifactExpander() {
133 throw new UnsupportedOperationException();
134 }
135
136 @Override
137 public Duration getTimeout() {
138 return Duration.ZERO;
139 }
140
141 @Override
142 public FileOutErr getFileOutErr() {
143 return outErr;
144 }
145
146 @Override
147 public SortedMap<PathFragment, ActionInput> getInputMapping() throws IOException {
tomlu1a19b622018-01-11 15:17:28 -0800148 return new SpawnInputExpander(execRoot, /*strict*/ false)
kush2ce45a22018-05-02 14:15:37 -0700149 .getInputMapping(simpleSpawn, SIMPLE_ARTIFACT_EXPANDER, fakeFileCache);
ulfjack9274cba2017-08-11 23:19:48 +0200150 }
151
152 @Override
153 public void report(ProgressStatus state, String name) {
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -0700154 progressUpdates.add(Pair.of(state, name));
ulfjack9274cba2017-08-11 23:19:48 +0200155 }
156 };
157
158 @Before
159 public final void setUp() throws Exception {
160 MockitoAnnotations.initMocks(this);
ccalvarinbda12a12018-06-21 18:57:26 -0700161 fs = new InMemoryFileSystem(new JavaClock(), DigestHashFunction.SHA256);
162 digestUtil = new DigestUtil(DigestHashFunction.SHA256);
ulfjack9274cba2017-08-11 23:19:48 +0200163 execRoot = fs.getPath("/exec/root");
164 FileSystemUtils.createDirectoryAndParents(execRoot);
165 fakeFileCache = new FakeActionInputFileCache(execRoot);
166 simpleSpawn =
167 new SimpleSpawn(
168 new FakeOwner("Mnemonic", "Progress Message"),
169 ImmutableList.of("/bin/echo", "Hi!"),
170 ImmutableMap.of("VARIABLE", "value"),
171 /*executionInfo=*/ ImmutableMap.<String, String>of(),
172 /*inputs=*/ ImmutableList.of(ActionInputHelper.fromPath("input")),
Benjamin Petersondd3ddb02018-05-03 09:20:08 -0700173 /*outputs=*/ ImmutableList.of(ActionInputHelper.fromPath("/random/file")),
ulfjack9274cba2017-08-11 23:19:48 +0200174 ResourceSet.ZERO);
175
176 Path stdout = fs.getPath("/tmp/stdout");
177 Path stderr = fs.getPath("/tmp/stderr");
178 FileSystemUtils.createDirectoryAndParents(stdout.getParentDirectory());
179 FileSystemUtils.createDirectoryAndParents(stderr.getParentDirectory());
180 outErr = new FileOutErr(stdout, stderr);
181 RemoteOptions options = Options.getDefaults(RemoteOptions.class);
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200182 Reporter reporter = new Reporter(new EventBus());
183 eventHandler = new StoredEventHandler();
184 reporter.addHandler(eventHandler);
olaola6f32d5a2017-09-20 17:12:19 +0200185 cache =
186 new RemoteSpawnCache(
buchgr559a07d2017-11-30 11:09:35 -0800187 execRoot,
188 options,
189 remoteCache,
190 "build-req-id",
191 "command-id",
buchgr559a07d2017-11-30 11:09:35 -0800192 reporter,
193 digestUtil);
ulfjack9274cba2017-08-11 23:19:48 +0200194 fakeFileCache.createScratchInput(simpleSpawn.getInputFiles().get(0), "xyz");
195 }
196
197 @SuppressWarnings("unchecked")
198 @Test
199 public void cacheHit() throws Exception {
200 ActionResult actionResult = ActionResult.getDefaultInstance();
olaolaba8b0b32017-10-20 09:48:56 +0200201 when(remoteCache.getCachedActionResult(any(ActionKey.class)))
202 .thenAnswer(
203 new Answer<ActionResult>() {
204 @Override
205 public ActionResult answer(InvocationOnMock invocation) {
206 RequestMetadata meta = TracingMetadataUtils.fromCurrentContext();
207 assertThat(meta.getCorrelatedInvocationsId()).isEqualTo("build-req-id");
208 assertThat(meta.getToolInvocationId()).isEqualTo("command-id");
209 return actionResult;
210 }
211 });
212 Mockito.doAnswer(
213 new Answer<Void>() {
214 @Override
215 public Void answer(InvocationOnMock invocation) {
216 RequestMetadata meta = TracingMetadataUtils.fromCurrentContext();
217 assertThat(meta.getCorrelatedInvocationsId()).isEqualTo("build-req-id");
218 assertThat(meta.getToolInvocationId()).isEqualTo("command-id");
219 return null;
220 }
221 })
222 .when(remoteCache)
223 .download(actionResult, execRoot, outErr);
ulfjack9274cba2017-08-11 23:19:48 +0200224
225 CacheHandle entry = cache.lookup(simpleSpawn, simplePolicy);
226 assertThat(entry.hasResult()).isTrue();
227 SpawnResult result = entry.getResult();
228 // All other methods on RemoteActionCache have side effects, so we verify all of them.
229 verify(remoteCache).download(actionResult, execRoot, outErr);
230 verify(remoteCache, never())
231 .ensureInputsPresent(
232 any(TreeNodeRepository.class),
233 any(Path.class),
234 any(TreeNode.class),
olaolaf0aa55d2018-08-16 08:51:06 -0700235 any(Action.class),
ulfjack9274cba2017-08-11 23:19:48 +0200236 any(Command.class));
237 verify(remoteCache, never())
238 .upload(
olaola7744b862017-09-18 23:04:33 +0200239 any(ActionKey.class),
olaolaf0aa55d2018-08-16 08:51:06 -0700240 any(Action.class),
241 any(Command.class),
olaola7744b862017-09-18 23:04:33 +0200242 any(Path.class),
243 any(Collection.class),
244 any(FileOutErr.class),
245 any(Boolean.class));
ulfjack9274cba2017-08-11 23:19:48 +0200246 assertThat(result.setupSuccess()).isTrue();
247 assertThat(result.exitCode()).isEqualTo(0);
olaolae5c9bdf2018-02-20 05:29:19 -0800248 assertThat(result.isCacheHit()).isTrue();
ulfjack9274cba2017-08-11 23:19:48 +0200249 // We expect the CachedLocalSpawnRunner to _not_ write to outErr at all.
250 assertThat(outErr.hasRecordedOutput()).isFalse();
251 assertThat(outErr.hasRecordedStderr()).isFalse();
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -0700252 assertThat(progressUpdates)
253 .containsExactly(Pair.of(ProgressStatus.CHECKING_CACHE, "remote-cache"));
ulfjack9274cba2017-08-11 23:19:48 +0200254 }
255
256 @Test
257 public void cacheMiss() throws Exception {
258 CacheHandle entry = cache.lookup(simpleSpawn, simplePolicy);
259 assertThat(entry.hasResult()).isFalse();
Googler4dd6f002018-03-27 08:15:39 -0700260 SpawnResult result =
261 new SpawnResult.Builder()
262 .setExitCode(0)
263 .setStatus(Status.SUCCESS)
264 .setRunnerName("test")
265 .build();
ulfjack9274cba2017-08-11 23:19:48 +0200266 ImmutableList<Path> outputFiles = ImmutableList.of(fs.getPath("/random/file"));
olaolaba8b0b32017-10-20 09:48:56 +0200267 Mockito.doAnswer(
268 new Answer<Void>() {
269 @Override
270 public Void answer(InvocationOnMock invocation) {
271 RequestMetadata meta = TracingMetadataUtils.fromCurrentContext();
272 assertThat(meta.getCorrelatedInvocationsId()).isEqualTo("build-req-id");
273 assertThat(meta.getToolInvocationId()).isEqualTo("command-id");
274 return null;
275 }
276 })
277 .when(remoteCache)
olaolaf0aa55d2018-08-16 08:51:06 -0700278 .upload(
279 any(ActionKey.class),
280 any(Action.class),
281 any(Command.class),
282 any(Path.class),
283 eq(outputFiles),
284 eq(outErr),
285 eq(true));
Benjamin Petersondd3ddb02018-05-03 09:20:08 -0700286 entry.store(result);
ulfjack9274cba2017-08-11 23:19:48 +0200287 verify(remoteCache)
olaolaf0aa55d2018-08-16 08:51:06 -0700288 .upload(
289 any(ActionKey.class),
290 any(Action.class),
291 any(Command.class),
292 any(Path.class),
293 eq(outputFiles),
294 eq(outErr),
295 eq(true));
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -0700296 assertThat(progressUpdates)
297 .containsExactly(Pair.of(ProgressStatus.CHECKING_CACHE, "remote-cache"));
ulfjack9274cba2017-08-11 23:19:48 +0200298 }
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200299
300 @Test
olaolaa22d0e92017-12-11 07:53:15 -0800301 public void noCacheSpawns() throws Exception {
302 // Checks that spawns that have mayBeCached false are not looked up in the remote cache,
303 // and also that their result is not uploaded to the remote cache. The artifacts, however,
304 // are uploaded.
Benjamin Petersondd3ddb02018-05-03 09:20:08 -0700305 SimpleSpawn uncacheableSpawn =
306 new SimpleSpawn(
307 new FakeOwner("foo", "bar"),
308 /*arguments=*/ ImmutableList.of(),
309 /*environment=*/ ImmutableMap.of(),
310 ImmutableMap.of(ExecutionRequirements.NO_CACHE, ""),
311 /*inputs=*/ ImmutableList.of(),
312 /*outputs=*/ ImmutableList.of(ActionInputHelper.fromPath("/random/file")),
313 ResourceSet.ZERO);
olaolaa22d0e92017-12-11 07:53:15 -0800314 CacheHandle entry = cache.lookup(uncacheableSpawn, simplePolicy);
315 verify(remoteCache, never())
316 .getCachedActionResult(any(ActionKey.class));
317 assertThat(entry.hasResult()).isFalse();
Googler4dd6f002018-03-27 08:15:39 -0700318 SpawnResult result =
319 new SpawnResult.Builder()
320 .setExitCode(0)
321 .setStatus(Status.SUCCESS)
322 .setRunnerName("test")
323 .build();
Benjamin Petersondd3ddb02018-05-03 09:20:08 -0700324 entry.store(result);
olaolaa22d0e92017-12-11 07:53:15 -0800325 ImmutableList<Path> outputFiles = ImmutableList.of(fs.getPath("/random/file"));
olaolaa22d0e92017-12-11 07:53:15 -0800326 verify(remoteCache)
olaolaf0aa55d2018-08-16 08:51:06 -0700327 .upload(
328 any(ActionKey.class),
329 any(Action.class),
330 any(Command.class),
331 any(Path.class),
332 eq(outputFiles),
333 eq(outErr),
334 eq(false));
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -0700335 assertThat(progressUpdates).containsExactly();
olaolaa22d0e92017-12-11 07:53:15 -0800336 }
337
338 @Test
339 public void noCacheSpawnsNoResultStore() throws Exception {
340 // Only successful action results are uploaded to the remote cache. The artifacts, however,
341 // are uploaded regardless.
342 CacheHandle entry = cache.lookup(simpleSpawn, simplePolicy);
343 verify(remoteCache).getCachedActionResult(any(ActionKey.class));
344 assertThat(entry.hasResult()).isFalse();
345 SpawnResult result =
Googler4dd6f002018-03-27 08:15:39 -0700346 new SpawnResult.Builder()
347 .setExitCode(1)
348 .setStatus(Status.NON_ZERO_EXIT)
349 .setRunnerName("test")
350 .build();
olaolaa22d0e92017-12-11 07:53:15 -0800351 ImmutableList<Path> outputFiles = ImmutableList.of(fs.getPath("/random/file"));
Benjamin Petersondd3ddb02018-05-03 09:20:08 -0700352 entry.store(result);
olaolaa22d0e92017-12-11 07:53:15 -0800353 verify(remoteCache)
olaolaf0aa55d2018-08-16 08:51:06 -0700354 .upload(
355 any(ActionKey.class),
356 any(Action.class),
357 any(Command.class),
358 any(Path.class),
359 eq(outputFiles),
360 eq(outErr),
361 eq(false));
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -0700362 assertThat(progressUpdates)
363 .containsExactly(Pair.of(ProgressStatus.CHECKING_CACHE, "remote-cache"));
olaolaa22d0e92017-12-11 07:53:15 -0800364 }
365
366 @Test
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200367 public void printWarningIfUploadFails() throws Exception {
368 CacheHandle entry = cache.lookup(simpleSpawn, simplePolicy);
369 assertThat(entry.hasResult()).isFalse();
Googler4dd6f002018-03-27 08:15:39 -0700370 SpawnResult result =
371 new SpawnResult.Builder()
372 .setExitCode(0)
373 .setStatus(Status.SUCCESS)
374 .setRunnerName("test")
375 .build();
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200376 ImmutableList<Path> outputFiles = ImmutableList.of(fs.getPath("/random/file"));
377
olaola7744b862017-09-18 23:04:33 +0200378 doThrow(new IOException("cache down"))
379 .when(remoteCache)
olaolaf0aa55d2018-08-16 08:51:06 -0700380 .upload(
381 any(ActionKey.class),
382 any(Action.class),
383 any(Command.class),
384 any(Path.class),
385 eq(outputFiles),
386 eq(outErr),
387 eq(true));
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200388
Benjamin Petersondd3ddb02018-05-03 09:20:08 -0700389 entry.store(result);
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200390 verify(remoteCache)
olaolaf0aa55d2018-08-16 08:51:06 -0700391 .upload(
392 any(ActionKey.class),
393 any(Action.class),
394 any(Command.class),
395 any(Path.class),
396 eq(outputFiles),
397 eq(outErr),
398 eq(true));
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200399
400 assertThat(eventHandler.getEvents()).hasSize(1);
401 Event evt = eventHandler.getEvents().get(0);
402 assertThat(evt.getKind()).isEqualTo(EventKind.WARNING);
Jakob Buchgraber321138f2018-05-07 05:24:53 -0700403 assertThat(evt.getMessage()).contains("Error");
404 assertThat(evt.getMessage()).contains("writing");
405 assertThat(evt.getMessage()).contains("cache down");
Benjamin Peterson7e1c7bc2018-05-03 04:30:19 -0700406 assertThat(progressUpdates)
407 .containsExactly(Pair.of(ProgressStatus.CHECKING_CACHE, "remote-cache"));
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200408 }
ulfjack9274cba2017-08-11 23:19:48 +0200409}