Add disclaimer to test case summary output when all test cases pass but target does not.

Revamped tests to be deterministic (previously used a random number generator to determine number of passed and failed test cases).

RELNOTES: Adds a clarifying message to test case summary output when all test cases pass but the target fails.
PiperOrigin-RevId: 224647102
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 7ab653b..e355c0f 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
@@ -14,12 +14,17 @@
 
 package com.google.devtools.build.lib.runtime;
 
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.exec.ExecutionOptions;
 import com.google.devtools.build.lib.exec.TestStrategy.TestSummaryFormat;
 import com.google.devtools.build.lib.runtime.TerminalTestResultNotifier.TestSummaryOptions;
@@ -29,102 +34,238 @@
 import com.google.devtools.build.lib.view.test.TestStatus.TestCase;
 import com.google.devtools.build.lib.view.test.TestStatus.TestCase.Status;
 import com.google.devtools.common.options.OptionsParsingResult;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.Random;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
+import org.mockito.ArgumentCaptor;
 
-/** Tests {@link TerminalTestResultNotifier}. */
+/** Tests for {@link TerminalTestResultNotifier}. */
 @RunWith(JUnit4.class)
-public class TerminalTestResultNotifierTest {
+public final class TerminalTestResultNotifierTest {
 
-  private final OptionsParsingResult optionsParsingResult =
-      Mockito.mock(OptionsParsingResult.class);
-  private final AnsiTerminalPrinter ansiTerminalPrinter = Mockito.mock(AnsiTerminalPrinter.class);
-  private final Random random = new Random();
+  private static final String ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER =
+      "however note that at least one target failed";
 
-  private void givenExecutionOption(TestSummaryFormat format) {
+  private final OptionsParsingResult optionsParsingResult = mock(OptionsParsingResult.class);
+  private final AnsiTerminalPrinter ansiTerminalPrinter = mock(AnsiTerminalPrinter.class);
+
+  private BlazeTestStatus targetStatus;
+  private int numFailedTestCases;
+  private int numTotalTestCases;
+  private TestSummaryFormat testSummaryFormat;
+
+  @Test
+  public void testCaseOption_allPass() throws Exception {
+    testSummaryFormat = TestSummaryFormat.TESTCASE;
+    numFailedTestCases = 0;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.PASSED;
+
+    printTestCaseSummary();
+
+    String printed = getPrintedMessage();
+    assertThat(printed).contains(info("10 passing"));
+    assertThat(printed).contains("0 failing");
+    assertThat(printed).contains("out of 10 test cases");
+    assertThat(printed).doesNotContain(ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER);
+    assertThat(printed).doesNotContain(AnsiTerminalPrinter.Mode.ERROR.toString());
+  }
+
+  @Test
+  public void testCaseOption_allPassButTargetFails() throws Exception {
+    testSummaryFormat = TestSummaryFormat.TESTCASE;
+    numFailedTestCases = 0;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    String printed = getPrintedMessage();
+    assertThat(printed).contains(info("10 passing"));
+    assertThat(printed).contains("0 failing");
+    assertThat(printed).contains("out of 10 test cases");
+    assertThat(printed).contains(ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER);
+    assertThat(printed).doesNotContain(AnsiTerminalPrinter.Mode.ERROR.toString());
+  }
+
+  @Test
+  public void testCaseOption_someFail() throws Exception {
+    testSummaryFormat = TestSummaryFormat.TESTCASE;
+    numFailedTestCases = 2;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    String printed = getPrintedMessage();
+    assertThat(printed).contains(info("8 passing"));
+    assertThat(printed).contains(error("2 failing"));
+    assertThat(printed).contains("out of 10 test cases");
+    assertThat(printed).doesNotContain(ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER);
+  }
+
+  @Test
+  public void testCaseOption_allFail() throws Exception {
+    testSummaryFormat = TestSummaryFormat.TESTCASE;
+    numFailedTestCases = 10;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    String printed = getPrintedMessage();
+    assertThat(printed).contains("0 passing");
+    assertThat(printed).contains(error("10 failing"));
+    assertThat(printed).contains("out of 10 test cases");
+    assertThat(printed).doesNotContain(ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER);
+    assertThat(printed).doesNotContain(AnsiTerminalPrinter.Mode.INFO.toString());
+  }
+
+  @Test
+  public void detailedOption_allPass() throws Exception {
+    testSummaryFormat = TestSummaryFormat.DETAILED;
+    numFailedTestCases = 0;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.PASSED;
+
+    printTestCaseSummary();
+
+    String printed = getPrintedMessage();
+    assertThat(printed).contains(info("10 passing"));
+    assertThat(printed).contains("0 failing");
+    assertThat(printed).contains("out of 10 test cases");
+    assertThat(printed).doesNotContain(ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER);
+    assertThat(printed).doesNotContain(AnsiTerminalPrinter.Mode.ERROR.toString());
+  }
+
+  @Test
+  public void detailedOption_allPassButTargetFails() throws Exception {
+    testSummaryFormat = TestSummaryFormat.DETAILED;
+    numFailedTestCases = 0;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    String printed = getPrintedMessage();
+    assertThat(printed).contains(info("10 passing"));
+    assertThat(printed).contains("0 failing");
+    assertThat(printed).contains("out of 10 test cases");
+    assertThat(printed).contains(ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER);
+    assertThat(printed).doesNotContain(AnsiTerminalPrinter.Mode.ERROR.toString());
+  }
+
+  @Test
+  public void detailedOption_someFail() throws Exception {
+    testSummaryFormat = TestSummaryFormat.DETAILED;
+    numFailedTestCases = 2;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    String printed = getPrintedMessage();
+    assertThat(printed).contains(info("8 passing"));
+    assertThat(printed).contains(error("2 failing"));
+    assertThat(printed).contains("out of 10 test cases");
+    assertThat(printed).doesNotContain(ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER);
+  }
+
+  @Test
+  public void detailedOption_allFail() throws Exception {
+    testSummaryFormat = TestSummaryFormat.DETAILED;
+    numFailedTestCases = 10;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    String printed = getPrintedMessage();
+    assertThat(printed).contains("0 passing");
+    assertThat(printed).contains(error("10 failing"));
+    assertThat(printed).contains("out of 10 test cases");
+    assertThat(printed).doesNotContain(ALL_TEST_CASES_PASSED_BUT_TARGET_FAILED_DISCLAIMER);
+    assertThat(printed).doesNotContain(AnsiTerminalPrinter.Mode.INFO.toString());
+  }
+
+  @Test
+  public void shortOption_noSummaryPrinted() throws Exception {
+    testSummaryFormat = TestSummaryFormat.SHORT;
+    numFailedTestCases = 2;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    verifyNoSummaryPrinted();
+  }
+
+  @Test
+  public void terseOption_noSummaryPrinted() throws Exception {
+    testSummaryFormat = TestSummaryFormat.TERSE;
+    numFailedTestCases = 2;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    verifyNoSummaryPrinted();
+  }
+
+  @Test
+  public void noneOption_noSummaryPrinted() throws Exception {
+    testSummaryFormat = TestSummaryFormat.NONE;
+    numFailedTestCases = 2;
+    numTotalTestCases = 10;
+    targetStatus = BlazeTestStatus.FAILED;
+
+    printTestCaseSummary();
+
+    verifyNoSummaryPrinted();
+  }
+
+  private void printTestCaseSummary() throws LabelSyntaxException {
     ExecutionOptions executionOptions = ExecutionOptions.DEFAULTS;
-    executionOptions.testSummary = format;
+    executionOptions.testSummary = testSummaryFormat;
     when(optionsParsingResult.getOptions(ExecutionOptions.class)).thenReturn(executionOptions);
 
     TestSummaryOptions testSummaryOptions = new TestSummaryOptions();
     testSummaryOptions.verboseSummary = true;
     when(optionsParsingResult.getOptions(TestSummaryOptions.class)).thenReturn(testSummaryOptions);
-  }
 
-  private void runTest(Boolean shouldPrintTestCaseSummary) throws Exception {
-    int numOfTotalTestCases = random.nextInt(10) + 1;
-    int numOfFailedCases = random.nextInt(numOfTotalTestCases);
-    int numOfSuccessfulTestCases = numOfTotalTestCases - numOfFailedCases;
-
-    TerminalTestResultNotifier terminalTestResultNotifier = new TerminalTestResultNotifier(
-        ansiTerminalPrinter, Path::getPathString, optionsParsingResult);
-
-    TestSummary testSummary = Mockito.mock(TestSummary.class);
-    when(testSummary.getTotalTestCases()).thenReturn(numOfTotalTestCases);
+    TestSummary testSummary = mock(TestSummary.class);
+    when(testSummary.getTotalTestCases()).thenReturn(numTotalTestCases);
     TestCase failedTestCase = TestCase.newBuilder().setStatus(Status.FAILED).build();
-    ArrayList<TestCase> testCases =
-        new ArrayList<>(Collections.nCopies(numOfFailedCases, failedTestCase));
+    List<TestCase> failedTestCases = Collections.nCopies(numFailedTestCases, failedTestCase);
 
     Label labelA = Label.parseAbsolute("//foo/bar:baz", ImmutableMap.of());
-    when(testSummary.getFailedTestCases()).thenReturn(testCases);
-    when(testSummary.getStatus()).thenReturn(BlazeTestStatus.FAILED);
+    when(testSummary.getFailedTestCases()).thenReturn(failedTestCases);
+    when(testSummary.getStatus()).thenReturn(targetStatus);
     when(testSummary.getLabel()).thenReturn(labelA);
 
-    HashSet<TestSummary> testSummaries = new HashSet<>();
-    testSummaries.add(testSummary);
-    terminalTestResultNotifier.notify(testSummaries, 1);
-
-    String summaryMessage =
-        String.format(
-            "Test cases: finished with %s%d passing%s and %s%d failing%s out of %d test cases",
-            numOfSuccessfulTestCases > 0 ? AnsiTerminalPrinter.Mode.INFO : "",
-            numOfSuccessfulTestCases,
-            AnsiTerminalPrinter.Mode.DEFAULT,
-            numOfFailedCases > 0 ? AnsiTerminalPrinter.Mode.ERROR : "",
-            numOfFailedCases,
-            AnsiTerminalPrinter.Mode.DEFAULT,
-            numOfTotalTestCases);
-
-    if (shouldPrintTestCaseSummary) {
-      verify(ansiTerminalPrinter).printLn(summaryMessage);
-    } else {
-      verify(ansiTerminalPrinter, never()).printLn(summaryMessage);
-    }
+    TerminalTestResultNotifier terminalTestResultNotifier =
+        new TerminalTestResultNotifier(
+            ansiTerminalPrinter, Path::getPathString, optionsParsingResult);
+    terminalTestResultNotifier.notify(ImmutableSet.of(testSummary), 1);
   }
 
-  @Test
-  public void testCasesDataVisibleInTestCaseOption() throws Exception {
-    givenExecutionOption(TestSummaryFormat.TESTCASE);
-    runTest(true);
+  private String getPrintedMessage() {
+    ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
+    verify(ansiTerminalPrinter).printLn(messageCaptor.capture());
+    return messageCaptor.getValue();
   }
 
-  @Test
-  public void testCasesDataVisibleInDetailedOption() throws Exception {
-    givenExecutionOption(TestSummaryFormat.DETAILED);
-    runTest(true);
+  private void verifyNoSummaryPrinted() {
+    verify(ansiTerminalPrinter, never()).printLn(any());
   }
 
-  @Test
-  public void testCasesDataInVisibleInShortOption() throws Exception {
-    givenExecutionOption(TestSummaryFormat.SHORT);
-    runTest(false);
+  private static String info(String message) {
+    return AnsiTerminalPrinter.Mode.INFO + message + AnsiTerminalPrinter.Mode.DEFAULT;
   }
 
-  @Test
-  public void testCasesDataInVisibleInTerseOption() throws Exception {
-    givenExecutionOption(TestSummaryFormat.TERSE);
-    runTest(false);
-  }
-
-  @Test
-  public void testCasesDataInVisibleInNoneOption() throws Exception {
-    givenExecutionOption(TestSummaryFormat.NONE);
-    runTest(false);
+  private static String error(String message) {
+    return AnsiTerminalPrinter.Mode.ERROR + message + AnsiTerminalPrinter.Mode.DEFAULT;
   }
 }