Improve printing test results summary.

Print up to 5 first failed-to-build tests.
This will help to locate the problems with tests themselves, while keeping the amount of output small.
(We do not want to print all names of failed-to-build tests in case when some common intermediate target is a problem.)

Currently, the user has to scroll a verbose output to locate the failed-to-build target.

Closes #10511.

PiperOrigin-RevId: 288677606
diff --git a/src/test/java/com/google/devtools/build/lib/runtime/TerminalTestResultNotifierTest.java b/src/test/java/com/google/devtools/build/lib/runtime/TerminalTestResultNotifierTest.java
index 79c932b..37293cf 100644
--- a/src/test/java/com/google/devtools/build/lib/runtime/TerminalTestResultNotifierTest.java
+++ b/src/test/java/com/google/devtools/build/lib/runtime/TerminalTestResultNotifierTest.java
@@ -18,11 +18,14 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.exec.ExecutionOptions;
@@ -35,6 +38,7 @@
 import com.google.devtools.build.lib.view.test.TestStatus.TestCase.Status;
 import com.google.devtools.common.options.OptionsParsingResult;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -107,6 +111,34 @@
   }
 
   @Test
+  public void shortOption_someFailToBuild() throws Exception {
+    testSummaryFormat = TestSummaryFormat.SHORT;
+    numFailedTestCases = 0;
+    int numFailedToBuildTestCases = TerminalTestResultNotifier.NUM_FAILED_TO_BUILD + 1;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED_TO_BUILD;
+
+    printFailedToBuildSummaries();
+
+    String skippedMessage = getPrintedMessage();
+    assertThat(skippedMessage).isEqualTo("(Skipping other failed to build tests)");
+
+    ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
+    verify(ansiTerminalPrinter, times(numFailedToBuildTestCases)).print(messageCaptor.capture());
+    List<String> values = messageCaptor.getAllValues();
+
+    for (int i = 0; i < numFailedToBuildTestCases - 1; i++) {
+      String message = values.get(i);
+      assertThat(message).contains("//foo/bar:baz");
+      assertThat(message).contains(BlazeTestStatus.FAILED_TO_BUILD.toString().replace('_', ' '));
+    }
+
+    String last = values.get(numFailedToBuildTestCases - 1);
+    assertThat(last).contains("Executed 0 out of 6 tests");
+    assertThat(last).contains(numFailedToBuildTestCases + " fail to build");
+  }
+
+  @Test
   public void testCaseOption_allFail() throws Exception {
     testSummaryFormat = TestSummaryFormat.TESTCASE;
     numFailedTestCases = 10;
@@ -226,11 +258,38 @@
     verifyNoSummaryPrinted();
   }
 
+  private void printFailedToBuildSummaries() throws LabelSyntaxException {
+    ExecutionOptions executionOptions = ExecutionOptions.DEFAULTS;
+    executionOptions.testSummary = testSummaryFormat;
+    when(optionsParsingResult.getOptions(ExecutionOptions.class)).thenReturn(executionOptions);
+    TestSummaryOptions testSummaryOptions = new TestSummaryOptions();
+    testSummaryOptions.verboseSummary = true;
+    when(optionsParsingResult.getOptions(TestSummaryOptions.class)).thenReturn(testSummaryOptions);
+
+    ImmutableSortedSet.Builder<TestSummary> builder =
+        ImmutableSortedSet.orderedBy(Comparator.comparing(o -> o.getLabel().getName()));
+    for (int i = 0; i < TerminalTestResultNotifier.NUM_FAILED_TO_BUILD + 1; i++) {
+      TestSummary testSummary = mock(TestSummary.class);
+      when(testSummary.getTotalTestCases()).thenReturn(0);
+
+      Label labelA = Label.parseAbsolute("//foo/bar:baz" + i, ImmutableMap.of());
+      when(testSummary.getFailedTestCases()).thenReturn(ImmutableList.of());
+      when(testSummary.getStatus()).thenReturn(BlazeTestStatus.FAILED_TO_BUILD);
+      when(testSummary.getLabel()).thenReturn(labelA);
+
+      builder.add(testSummary);
+    }
+
+    TerminalTestResultNotifier terminalTestResultNotifier =
+        new TerminalTestResultNotifier(
+            ansiTerminalPrinter, Path::getPathString, optionsParsingResult);
+    terminalTestResultNotifier.notify(builder.build(), 0);
+  }
+
   private void printTestCaseSummary() throws LabelSyntaxException {
     ExecutionOptions executionOptions = ExecutionOptions.DEFAULTS;
     executionOptions.testSummary = testSummaryFormat;
     when(optionsParsingResult.getOptions(ExecutionOptions.class)).thenReturn(executionOptions);
-
     TestSummaryOptions testSummaryOptions = new TestSummaryOptions();
     testSummaryOptions.verboseSummary = true;
     when(optionsParsingResult.getOptions(TestSummaryOptions.class)).thenReturn(testSummaryOptions);