remote: refactor test to reduce boilerplate

this removes about 500 lines of boilerplate code by moving it to a
method.

Closes #7995.

PiperOrigin-RevId: 243017622
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
index 18bd23f..7870c86 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
@@ -29,9 +29,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
-import build.bazel.remote.execution.v2.Action;
 import build.bazel.remote.execution.v2.ActionResult;
-import build.bazel.remote.execution.v2.Command;
 import build.bazel.remote.execution.v2.Digest;
 import build.bazel.remote.execution.v2.ExecuteRequest;
 import build.bazel.remote.execution.v2.ExecuteResponse;
@@ -91,12 +89,12 @@
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.Nullable;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -122,16 +120,14 @@
   private FakeActionInputFileCache fakeFileCache;
   private FileOutErr outErr;
 
-  private RemoteOptions options;
+  private RemoteOptions remoteOptions;
   private RemoteRetrier retrier;
 
   @Mock private GrpcRemoteCache cache;
 
-  @Mock
-  private GrpcRemoteExecutor executor;
+  @Mock private GrpcRemoteExecutor executor;
 
-  @Mock
-  private SpawnRunner localRunner;
+  @Mock private SpawnRunner localRunner;
 
   // The action key of the Spawn returned by newSimpleSpawn().
   private final String simpleActionId =
@@ -158,8 +154,8 @@
     FileSystemUtils.createDirectoryAndParents(stderr.getParentDirectory());
     outErr = new FileOutErr(stdout, stderr);
 
-    options = Options.getDefaults(RemoteOptions.class);
-    retrier = RemoteModule.createExecuteRetrier(options, retryService);
+    remoteOptions = Options.getDefaults(RemoteOptions.class);
+    retrier = RemoteModule.createExecuteRetrier(remoteOptions, retryService);
   }
 
   @AfterClass
@@ -168,46 +164,34 @@
   }
 
   @Test
-  @SuppressWarnings("unchecked")
   public void nonCachableSpawnsShouldNotBeCached_remote() throws Exception {
     // Test that if a spawn is marked "NO_CACHE" then it's not fetched from a remote cache.
     // It should be executed remotely, but marked non-cacheable to remote execution, so that
     // the action result is not saved in the remote cache.
 
-    options.remoteAcceptCached = true;
-    options.remoteLocalFallback = false;
-    options.remoteUploadLocalResults = true;
-    options.remoteResultCachePriority = 1;
-    options.remoteExecutionPriority = 2;
+    remoteOptions.remoteAcceptCached = true;
+    remoteOptions.remoteLocalFallback = false;
+    remoteOptions.remoteUploadLocalResults = true;
+    remoteOptions.remoteResultCachePriority = 1;
+    remoteOptions.remoteExecutionPriority = 2;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
-    ExecuteResponse succeeded = ExecuteResponse.newBuilder().setResult(
-        ActionResult.newBuilder().setExitCode(0).build()).build();
+    ExecuteResponse succeeded =
+        ExecuteResponse.newBuilder()
+            .setResult(ActionResult.newBuilder().setExitCode(0).build())
+            .build();
     when(executor.executeRemotely(any(ExecuteRequest.class))).thenReturn(succeeded);
 
-    Spawn spawn = new SimpleSpawn(
-        new FakeOwner("foo", "bar"),
-        /*arguments=*/ ImmutableList.of(),
-        /*environment=*/ ImmutableMap.of(),
-        NO_CACHE,
-        /*inputs=*/ ImmutableList.of(),
-        /*outputs=*/ ImmutableList.<ActionInput>of(),
-        ResourceSet.ZERO);
+    Spawn spawn =
+        new SimpleSpawn(
+            new FakeOwner("foo", "bar"),
+            /*arguments=*/ ImmutableList.of(),
+            /*environment=*/ ImmutableMap.of(),
+            NO_CACHE,
+            /*inputs=*/ ImmutableList.of(),
+            /*outputs=*/ ImmutableList.<ActionInput>of(),
+            ResourceSet.ZERO);
 
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
 
@@ -220,56 +204,34 @@
     assertThat(requestCaptor.getValue().getExecutionPolicy().getPriority()).isEqualTo(2);
     // TODO(olaola): verify that the uploaded action has the doNotCache set.
 
-    verify(cache, never())
-        .getCachedActionResult(any(ActionKey.class));
-    verify(cache, never())
-        .upload(
-            any(ActionKey.class),
-            any(Action.class),
-            any(Command.class),
-            any(Path.class),
-            any(Collection.class),
-            any(FileOutErr.class));
+    verify(cache, never()).getCachedActionResult(any(ActionKey.class));
+    verify(cache, never()).upload(any(), any(), any(), any(), any(), any());
     verifyZeroInteractions(localRunner);
   }
 
   @Test
-  @SuppressWarnings("unchecked")
   public void nonCachableSpawnsShouldNotBeCached_local() throws Exception {
     // Test that if a spawn is executed locally, due to the local fallback, that its result is not
     // uploaded to the remote cache.
 
-    options.remoteAcceptCached = true;
-    options.remoteLocalFallback = true;
-    options.remoteUploadLocalResults = true;
+    remoteOptions.remoteAcceptCached = true;
+    remoteOptions.remoteLocalFallback = true;
+    remoteOptions.remoteUploadLocalResults = true;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            null,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunnerWithoutExecutor();
 
     // Throw an IOException to trigger the local fallback.
     when(executor.executeRemotely(any(ExecuteRequest.class))).thenThrow(IOException.class);
 
-    Spawn spawn = new SimpleSpawn(
-        new FakeOwner("foo", "bar"),
-        /*arguments=*/ ImmutableList.of(),
-        /*environment=*/ ImmutableMap.of(),
-        NO_CACHE,
-        /*inputs=*/ ImmutableList.of(),
-        /*outputs=*/ ImmutableList.<ActionInput>of(),
-        ResourceSet.ZERO);
+    Spawn spawn =
+        new SimpleSpawn(
+            new FakeOwner("foo", "bar"),
+            /*arguments=*/ ImmutableList.of(),
+            /*environment=*/ ImmutableMap.of(),
+            NO_CACHE,
+            /*inputs=*/ ImmutableList.of(),
+            /*outputs=*/ ImmutableList.<ActionInput>of(),
+            ResourceSet.ZERO);
 
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
 
@@ -277,34 +239,17 @@
 
     verify(localRunner).exec(spawn, policy);
 
-    verify(cache, never())
-        .getCachedActionResult(any(ActionKey.class));
+    verify(cache, never()).getCachedActionResult(any(ActionKey.class));
     verifyNoMoreInteractions(cache);
   }
 
   @Test
-  @SuppressWarnings("unchecked")
   public void failedLocalActionShouldNotBeUploaded() throws Exception {
     // Test that the outputs of a locally executed action that failed are not uploaded.
 
-    options.remoteUploadLocalResults = true;
+    remoteOptions.remoteUploadLocalResults = true;
 
-    RemoteSpawnRunner runner =
-        spy(
-            new RemoteSpawnRunner(
-                execRoot,
-                options,
-                Options.getDefaults(ExecutionOptions.class),
-                new AtomicReference<>(localRunner),
-                true,
-                /*cmdlineReporter=*/ null,
-                "build-req-id",
-                "command-id",
-                cache,
-                null,
-                retrier,
-                digestUtil,
-                logDir));
+    RemoteSpawnRunner runner = spy(newSpawnRunnerWithoutExecutor());
 
     Spawn spawn = newSimpleSpawn();
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
@@ -321,48 +266,25 @@
         .execLocallyAndUpload(
             eq(spawn),
             eq(policy),
-            any(SortedMap.class),
+            any(),
             eq(cache),
-            any(ActionKey.class),
-            any(Action.class),
-            any(Command.class),
+            any(),
+            any(),
+            any(),
             /* uploadLocalResults= */ eq(true));
-    verify(cache, never())
-        .upload(
-            any(ActionKey.class),
-            any(Action.class),
-            any(Command.class),
-            any(Path.class),
-            any(Collection.class),
-            any(FileOutErr.class));
+    verify(cache, never()).upload(any(), any(), any(), any(), any(), any());
   }
 
   @Test
-  @SuppressWarnings("unchecked")
   public void treatFailedCachedActionAsCacheMiss_local() throws Exception {
     // Test that bazel treats failed cache action as a cache miss and attempts to execute action
     // locally
 
-    RemoteOptions options = Options.getDefaults(RemoteOptions.class);
     ActionResult failedAction = ActionResult.newBuilder().setExitCode(1).build();
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(failedAction);
 
-    RemoteSpawnRunner runner =
-        spy(
-            new RemoteSpawnRunner(
-                execRoot,
-                options,
-                Options.getDefaults(ExecutionOptions.class),
-                new AtomicReference<>(localRunner),
-                true,
-                /* cmdlineReporter= */ null,
-                "build-req-id",
-                "command-id",
-                cache,
-                null,
-                retrier,
-                digestUtil,
-                logDir));
+    RemoteSpawnRunner runner = spy(newSpawnRunnerWithoutExecutor());
+
     Spawn spawn = newSimpleSpawn();
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
 
@@ -381,20 +303,13 @@
         .execLocallyAndUpload(
             eq(spawn),
             eq(policy),
-            any(SortedMap.class),
+            any(),
             eq(cache),
-            any(ActionKey.class),
-            any(Action.class),
-            any(Command.class),
+            any(),
+            any(),
+            any(),
             /* uploadLocalResults= */ eq(true));
-    verify(cache)
-        .upload(
-            any(ActionKey.class),
-            any(Action.class),
-            any(Command.class),
-            any(Path.class),
-            any(Collection.class),
-            any(FileOutErr.class));
+    verify(cache).upload(any(), any(), any(), any(), any(), any());
     verify(cache, never()).download(any(ActionResult.class), any(Path.class), eq(outErr));
   }
 
@@ -406,21 +321,7 @@
     ActionResult failedAction = ActionResult.newBuilder().setExitCode(1).build();
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(failedAction);
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /* cmdlineReporter= */ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     ExecuteResponse succeeded =
         ExecuteResponse.newBuilder()
@@ -438,32 +339,17 @@
   }
 
   @Test
-  @SuppressWarnings("unchecked")
   public void printWarningIfCacheIsDown() throws Exception {
     // If we try to upload to a local cache, that is down a warning should be printed.
 
-    options.remoteUploadLocalResults = true;
-    options.remoteLocalFallback = true;
+    remoteOptions.remoteUploadLocalResults = true;
+    remoteOptions.remoteLocalFallback = true;
 
     Reporter reporter = new Reporter(new EventBus());
     StoredEventHandler eventHandler = new StoredEventHandler();
     reporter.addHandler(eventHandler);
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            false,
-            reporter,
-            "build-req-id",
-            "command-id",
-            cache,
-            null,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunnerWithoutExecutor(reporter);
 
     Spawn spawn = newSimpleSpawn();
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
@@ -473,13 +359,7 @@
 
     doThrow(new IOException("cache down"))
         .when(cache)
-        .upload(
-            any(ActionKey.class),
-            any(Action.class),
-            any(Command.class),
-            any(Path.class),
-            any(Collection.class),
-            any(FileOutErr.class));
+        .upload(any(), any(), any(), any(), any(), any());
 
     SpawnResult res =
         new SpawnResult.Builder()
@@ -505,24 +385,10 @@
   public void noRemoteExecutorFallbackFails() throws Exception {
     // Errors from the fallback runner should be propogated out of the remote runner.
 
-    options.remoteUploadLocalResults = true;
-    options.remoteLocalFallback = true;
+    remoteOptions.remoteUploadLocalResults = true;
+    remoteOptions.remoteLocalFallback = true;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            null,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunnerWithoutExecutor();
 
     Spawn spawn = newSimpleSpawn();
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
@@ -546,24 +412,10 @@
   public void remoteCacheErrorFallbackFails() throws Exception {
     // Errors from the fallback runner should be propogated out of the remote runner.
 
-    options.remoteUploadLocalResults = true;
-    options.remoteLocalFallback = true;
+    remoteOptions.remoteUploadLocalResults = true;
+    remoteOptions.remoteLocalFallback = true;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            null,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunnerWithoutExecutor();
 
     Spawn spawn = newSimpleSpawn();
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
@@ -585,23 +437,9 @@
 
   @Test
   public void testLocalFallbackFailureRemoteExecutorFailure() throws Exception {
-    options.remoteLocalFallback = true;
+    remoteOptions.remoteLocalFallback = true;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
     when(executor.executeRemotely(any(ExecuteRequest.class))).thenThrow(new IOException());
@@ -624,22 +462,7 @@
 
   @Test
   public void testHumanReadableServerLogsSavedForFailingAction() throws Exception {
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            Options.getDefaults(RemoteOptions.class),
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
-
+    RemoteSpawnRunner runner = newSpawnRunner();
     Digest logDigest = digestUtil.computeAsUtf8("bla");
     Path logPath = logDir.getRelative(simpleActionId).getRelative("logname");
     when(executor.executeRemotely(any(ExecuteRequest.class)))
@@ -666,22 +489,7 @@
 
   @Test
   public void testHumanReadableServerLogsSavedForFailingActionWithStatus() throws Exception {
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            Options.getDefaults(RemoteOptions.class),
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
-
+    RemoteSpawnRunner runner = newSpawnRunner();
     Digest logDigest = digestUtil.computeAsUtf8("bla");
     Path logPath = logDir.getRelative(simpleActionId).getRelative("logname");
     com.google.rpc.Status timeoutStatus =
@@ -710,30 +518,14 @@
 
   @Test
   public void testNonHumanReadableServerLogsNotSaved() throws Exception {
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            Options.getDefaults(RemoteOptions.class),
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     Digest logDigest = digestUtil.computeAsUtf8("bla");
     ActionResult result = ActionResult.newBuilder().setExitCode(31).build();
     when(executor.executeRemotely(any(ExecuteRequest.class)))
         .thenReturn(
             ExecuteResponse.newBuilder()
-                .putServerLogs(
-                    "logname",
-                    LogFile.newBuilder().setDigest(logDigest).build())
+                .putServerLogs("logname", LogFile.newBuilder().setDigest(logDigest).build())
                 .setResult(result)
                 .build());
 
@@ -750,21 +542,7 @@
 
   @Test
   public void testServerLogsNotSavedForSuccessfulAction() throws Exception {
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            Options.getDefaults(RemoteOptions.class),
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     Digest logDigest = digestUtil.computeAsUtf8("bla");
     ActionResult result = ActionResult.newBuilder().setExitCode(0).build();
@@ -792,21 +570,7 @@
   public void cacheDownloadFailureTriggersRemoteExecution() throws Exception {
     // If downloading a cached action fails, remote execution should be tried.
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(cachedResult);
@@ -835,21 +599,7 @@
     // If downloading an action result fails, remote execution should be retried
     // with skip cache lookup enabled
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
     ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
@@ -887,23 +637,9 @@
   public void testRemoteExecutionTimeout() throws Exception {
     // If remote execution times out the SpawnResult status should be TIMEOUT.
 
-    options.remoteLocalFallback = false;
+    remoteOptions.remoteLocalFallback = false;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
@@ -934,23 +670,9 @@
     // If remote execution times out the SpawnResult status should be TIMEOUT, regardess of local
     // fallback option.
 
-    options.remoteLocalFallback = true;
+    remoteOptions.remoteLocalFallback = true;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
@@ -979,28 +701,16 @@
 
   @Test
   public void testRemoteExecutionCommandFailureDoesNotTriggerFallback() throws Exception {
-    options.remoteLocalFallback = true;
+    remoteOptions.remoteLocalFallback = true;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     ActionResult cachedResult = ActionResult.newBuilder().setExitCode(0).build();
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
-    ExecuteResponse failed = ExecuteResponse.newBuilder().setResult(
-        ActionResult.newBuilder().setExitCode(33).build()).build();
+    ExecuteResponse failed =
+        ExecuteResponse.newBuilder()
+            .setResult(ActionResult.newBuilder().setExitCode(33).build())
+            .build();
     when(executor.executeRemotely(any(ExecuteRequest.class))).thenReturn(failed);
 
     Spawn spawn = newSimpleSpawn();
@@ -1021,23 +731,9 @@
     // If we get a failure due to the remote cache not working, the exit code should be
     // ExitCode.REMOTE_ERROR.
 
-    options.remoteLocalFallback = false;
+    remoteOptions.remoteLocalFallback = false;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(null);
     when(executor.executeRemotely(any(ExecuteRequest.class))).thenThrow(new IOException("reasons"));
@@ -1060,23 +756,9 @@
     // If we get a failure due to the remote executor not working, the exit code should be
     // ExitCode.REMOTE_ERROR.
 
-    options.remoteLocalFallback = false;
+    remoteOptions.remoteLocalFallback = false;
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /*cmdlineReporter=*/ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     when(cache.getCachedActionResult(any(ActionKey.class))).thenThrow(new IOException("reasons"));
 
@@ -1170,27 +852,12 @@
   @Test
   public void testDownloadMinimalOnCacheHit() throws Exception {
     // arrange
-    RemoteOptions options = Options.getDefaults(RemoteOptions.class);
-    options.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
+    remoteOptions.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
 
     ActionResult succeededAction = ActionResult.newBuilder().setExitCode(0).build();
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(succeededAction);
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /* cmdlineReporter= */ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     Spawn spawn = newSimpleSpawn();
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
@@ -1208,28 +875,13 @@
   @Test
   public void testDownloadMinimalOnCacheMiss() throws Exception {
     // arrange
-    RemoteOptions options = Options.getDefaults(RemoteOptions.class);
-    options.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
+    remoteOptions.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
 
     ActionResult succeededAction = ActionResult.newBuilder().setExitCode(0).build();
     ExecuteResponse succeeded = ExecuteResponse.newBuilder().setResult(succeededAction).build();
     when(executor.executeRemotely(any(ExecuteRequest.class))).thenReturn(succeeded);
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            true,
-            /* cmdlineReporter= */ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     Spawn spawn = newSimpleSpawn();
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
@@ -1248,8 +900,7 @@
   @Test
   public void testDownloadMinimalIoError() throws Exception {
     // arrange
-    RemoteOptions options = Options.getDefaults(RemoteOptions.class);
-    options.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
+    remoteOptions.remoteOutputsMode = RemoteOutputsMode.MINIMAL;
 
     ActionResult succeededAction = ActionResult.newBuilder().setExitCode(0).build();
     when(cache.getCachedActionResult(any(ActionKey.class))).thenReturn(succeededAction);
@@ -1257,21 +908,7 @@
     when(cache.downloadMinimal(any(), anyCollection(), any(), any(), any(), any()))
         .thenThrow(downloadFailure);
 
-    RemoteSpawnRunner runner =
-        new RemoteSpawnRunner(
-            execRoot,
-            options,
-            Options.getDefaults(ExecutionOptions.class),
-            new AtomicReference<>(localRunner),
-            /* verboseFailures= */ false,
-            /* cmdlineReporter= */ null,
-            "build-req-id",
-            "command-id",
-            cache,
-            executor,
-            retrier,
-            digestUtil,
-            logDir);
+    RemoteSpawnRunner runner = newSpawnRunner();
 
     Spawn spawn = newSimpleSpawn();
     SpawnExecutionContext policy = new FakeSpawnExecutionContext(spawn);
@@ -1292,19 +929,48 @@
   private static Spawn newSimpleSpawn() {
     return new SimpleSpawn(
         new FakeOwner("foo", "bar"),
-            /*arguments=*/ ImmutableList.of(),
-            /*environment=*/ ImmutableMap.of(),
-            /*executionInfo=*/ ImmutableMap.of(),
-            /*inputs=*/ ImmutableList.of(),
-            /*outputs=*/ ImmutableList.<ActionInput>of(),
+        /*arguments=*/ ImmutableList.of(),
+        /*environment=*/ ImmutableMap.of(),
+        /*executionInfo=*/ ImmutableMap.of(),
+        /*inputs=*/ ImmutableList.of(),
+        /*outputs=*/ ImmutableList.<ActionInput>of(),
         ResourceSet.ZERO);
   }
 
+  private RemoteSpawnRunner newSpawnRunner() {
+    return newSpawnRunner(/* verboseFailures= */ false, executor, /* reporter= */ null);
+  }
+
+  private RemoteSpawnRunner newSpawnRunner(
+      boolean verboseFailures, @Nullable GrpcRemoteExecutor executor, @Nullable Reporter reporter) {
+    return new RemoteSpawnRunner(
+        execRoot,
+        remoteOptions,
+        Options.getDefaults(ExecutionOptions.class),
+        new AtomicReference<>(localRunner),
+        verboseFailures,
+        reporter,
+        "build-req-id",
+        "command-id",
+        cache,
+        executor,
+        retrier,
+        digestUtil,
+        logDir);
+  }
+
+  private RemoteSpawnRunner newSpawnRunnerWithoutExecutor() {
+    return newSpawnRunner(/* verboseFailures= */ false, /* executor= */ null, /* reporter= */ null);
+  }
+
+  private RemoteSpawnRunner newSpawnRunnerWithoutExecutor(Reporter reporter) {
+    return newSpawnRunner(/* verboseFailures= */ false, /* executor= */ null, reporter);
+  }
+
   // TODO(buchgr): Extract a common class to be used for testing.
   class FakeSpawnExecutionContext implements SpawnExecutionContext {
 
-    private final ArtifactExpander artifactExpander =
-        (artifact, output) -> output.add(artifact);
+    private final ArtifactExpander artifactExpander = (artifact, output) -> output.add(artifact);
 
     private final Spawn spawn;
 
@@ -1356,8 +1022,8 @@
     public SortedMap<PathFragment, ActionInput> getInputMapping(
         boolean expandTreeArtifactsInRunfiles) throws IOException {
       return new SpawnInputExpander(execRoot, /*strict*/ false)
-          .getInputMapping(spawn, artifactExpander, ArtifactPathResolver.IDENTITY, fakeFileCache,
-              true);
+          .getInputMapping(
+              spawn, artifactExpander, ArtifactPathResolver.IDENTITY, fakeFileCache, true);
     }
 
     @Override