Throw explicit exception if experimental_split_coverage_postprocessing is used without experimental_fetch_all_coverage_outputs

This closes https://github.com/bazelbuild/bazel/issues/13185
According to https://github.com/bazelbuild/bazel/commit/e411fa74176cdc54f84d62c189d042e9fb6d5c79,
The flag --experimental_split_coverage_postprocessing depends on the flag
--experimental_fetch_all_coverage_outputs being enabled, but if the former one
is set without the latter one, Bazel throws a quite confusing NullPointerException.
Now we throw an explicit exception with proper error message.

Closes #16140.

PiperOrigin-RevId: 479239693
Change-Id: I5afa0ae14a3f34a0a7b21fbf4badad3d1836da95
diff --git a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
index 1261a89..fc37650 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
@@ -102,14 +102,9 @@
       TestRunnerAction action, ActionExecutionContext actionExecutionContext)
       throws ExecException, InterruptedException {
     if (action.getExecutionSettings().getInputManifest() == null) {
-      String errorMessage = "cannot run local tests with --nobuild_runfile_manifests";
-      throw new TestExecException(
-          errorMessage,
-          FailureDetail.newBuilder()
-              .setTestAction(
-                  TestAction.newBuilder().setCode(TestAction.Code.LOCAL_TEST_PREREQ_UNMET))
-              .setMessage(errorMessage)
-              .build());
+      throw createTestExecException(
+          TestAction.Code.LOCAL_TEST_PREREQ_UNMET,
+          "cannot run local tests with --nobuild_runfile_manifests");
     }
     Map<String, String> testEnvironment =
         createEnvironment(
@@ -641,6 +636,16 @@
     }
   }
 
+  private static TestExecException createTestExecException(
+      TestAction.Code errorCode, String errorMessage) {
+    return new TestExecException(
+        errorMessage,
+        FailureDetail.newBuilder()
+            .setTestAction(TestAction.newBuilder().setCode(errorCode))
+            .setMessage(errorMessage)
+            .build());
+  }
+
   private final class BazelTestAttemptContinuation extends TestAttemptContinuation {
     private final TestRunnerAction testAction;
     private final ActionExecutionContext actionExecutionContext;
@@ -760,6 +765,18 @@
             .setHasCoverage(testAction.isCoverageMode());
 
         if (testAction.isCoverageMode() && testAction.getSplitCoveragePostProcessing()) {
+          if (testAction.getCoverageDirectoryTreeArtifact() == null) {
+            // Otherwise we'll get a NPE https://github.com/bazelbuild/bazel/issues/13185
+            TestExecException e =
+                createTestExecException(
+                    TestAction.Code.LOCAL_TEST_PREREQ_UNMET,
+                    "coverageDirectoryTreeArtifact is null:"
+                        + " --experimental_split_coverage_postprocessing depends on"
+                        + " --experimental_fetch_all_coverage_outputs being enabled");
+            closeSuppressed(e, streamed);
+            closeSuppressed(e, fileOutErr);
+            throw e;
+          }
           actionExecutionContext
               .getMetadataHandler()
               .getMetadata(testAction.getCoverageDirectoryTreeArtifact());