Write stdout and stderr to the remote cache

Only write a cache entry when the spawn executed successfully, and with a 0
exit code. In the test, we only check that uploadFileContents is called exactly
twice.

Progress on #1413.

PiperOrigin-RevId: 153458240
diff --git a/src/main/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunner.java
index 3a10aa4..fb7241e 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunner.java
@@ -126,8 +126,10 @@
         }
       }
       SpawnResult spawnResult = delegate.exec(spawn, policy);
-      if (options.remoteLocalExecUploadResults && spawnResult.setupSuccess()) {
-        writeCacheEntry(spawn, actionKey);
+      if (options.remoteLocalExecUploadResults
+          && spawnResult.status() == Status.SUCCESS
+          && spawnResult.exitCode() == 0) {
+        writeCacheEntry(spawn, policy.getFileOutErr(), actionKey);
       }
       return spawnResult;
     } catch (StatusRuntimeException e) {
@@ -172,7 +174,7 @@
     outErr.printErr(new String(streams.get(1), UTF_8));
   }
 
-  private void writeCacheEntry(Spawn spawn, ActionKey actionKey)
+  private void writeCacheEntry(Spawn spawn, FileOutErr outErr, ActionKey actionKey)
           throws IOException, InterruptedException {
     ArrayList<Path> outputFiles = new ArrayList<>();
     for (ActionInput output : spawn.getOutputFiles()) {
@@ -185,6 +187,10 @@
     }
     ActionResult.Builder result = ActionResult.newBuilder();
     actionCache.uploadAllResults(execRoot, outputFiles, result);
+    ContentDigest stderr = actionCache.uploadFileContents(outErr.getErrorPath());
+    ContentDigest stdout = actionCache.uploadFileContents(outErr.getOutputPath());
+    result.setStderrDigest(stderr);
+    result.setStdoutDigest(stdout);
     actionCache.setCachedActionResult(actionKey, result.build());
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunnerTest.java
index 1456239..8fe2f79 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunnerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunnerTest.java
@@ -66,6 +66,8 @@
     }
   };
 
+  private static final ContentDigest DIGEST_FOR_EMPTY = ContentDigests.computeDigest(new byte[0]);
+
   private FileSystem fs;
   private Path execRoot;
   private SimpleSpawn simpleSpawn;
@@ -219,6 +221,7 @@
     scratch(simpleSpawn.getInputFiles().get(0), "xyz");
 
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
+    when(cache.uploadFileContents(any(Path.class))).thenReturn(DIGEST_FOR_EMPTY);
     SpawnResult delegateResult = new SpawnResult.Builder()
         .setExitCode(0)
         .setStatus(Status.SUCCESS)
@@ -227,9 +230,11 @@
         .thenReturn(delegateResult);
 
     SpawnResult result = runner.exec(simpleSpawn, simplePolicy);
-    // We use verify to check that each method is called exactly once.
+    // We use verify to check that each method is called the correct number of times.
     verify(cache)
         .uploadAllResults(any(Path.class), any(Collection.class), any(ActionResult.Builder.class));
+    // Two additional uploads for stdout and stderr.
+    verify(cache, Mockito.times(2)).uploadFileContents(any(Path.class));
     verify(cache).setCachedActionResult(any(ActionKey.class), any(ActionResult.class));
     assertThat(result.setupSuccess()).isTrue();
     assertThat(result.exitCode()).isEqualTo(0);