diff --git a/src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java b/src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java index 1090009da2ad01..50aba39681b4a2 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java @@ -51,6 +51,7 @@ import com.google.devtools.build.lib.pkgcache.PathPackageLocator; import com.google.devtools.build.lib.skyframe.ConfigurationPhaseStartedEvent; import com.google.devtools.build.lib.skyframe.LoadingPhaseStartedEvent; +import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TestAnalyzedEvent; import com.google.devtools.build.lib.util.io.AnsiTerminal; import com.google.devtools.build.lib.util.io.AnsiTerminal.Color; import com.google.devtools.build.lib.util.io.AnsiTerminalWriter; @@ -762,6 +763,12 @@ public void testFilteringComplete(TestFilteringCompleteEvent event) { refresh(); } + @Subscribe + public void singleTestAnalyzed(TestAnalyzedEvent event) { + stateTracker.singleTestAnalyzed(event); + refreshSoon(); + } + /** * Return true, if the test summary provides information that is both worth being shown in the * scroll-back buffer and new with respect to the alreay shown failure messages. diff --git a/src/main/java/com/google/devtools/build/lib/runtime/UiStateTracker.java b/src/main/java/com/google/devtools/build/lib/runtime/UiStateTracker.java index a6bc5c15071b67..7cad0534a7c088 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/UiStateTracker.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/UiStateTracker.java @@ -48,6 +48,7 @@ import com.google.devtools.build.lib.skyframe.ConfiguredTargetProgressReceiver; import com.google.devtools.build.lib.skyframe.LoadingPhaseStartedEvent; import com.google.devtools.build.lib.skyframe.PackageProgressReceiver; +import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TestAnalyzedEvent; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.io.AnsiTerminalWriter; import com.google.devtools.build.lib.util.io.PositionAwareAnsiTerminalWriter; @@ -952,6 +953,15 @@ synchronized void testFilteringComplete(TestFilteringCompleteEvent event) { } } + synchronized void singleTestAnalyzed(TestAnalyzedEvent event) { + ConfiguredTarget target = event.configuredTarget(); + // Only register the count towards totalTests once. + if (target.getLabel() != null + && testActions.putIfAbsent(target.getLabel(), Sets.newConcurrentHashSet()) == null) { + totalTests++; + } + } + public synchronized void testSummary(TestSummary summary) { completedTests++; mostRecentTest = summary; diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD index 71a8d66ee4bf59..c8d9e2ccd1aede 100644 --- a/src/test/java/com/google/devtools/build/lib/BUILD +++ b/src/test/java/com/google/devtools/build/lib/BUILD @@ -175,6 +175,7 @@ java_test( "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_progress_receiver", "//src/main/java/com/google/devtools/build/lib/skyframe:loading_phase_started_event", "//src/main/java/com/google/devtools/build/lib/skyframe:package_progress_receiver", + "//src/main/java/com/google/devtools/build/lib/skyframe:top_level_status_events", "//src/main/java/com/google/devtools/build/lib/unix", "//src/main/java/com/google/devtools/build/lib/util", "//src/main/java/com/google/devtools/build/lib/util:abrupt_exit_exception", diff --git a/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java b/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java index 9e7c42bf6744c6..e097757f3ec59a 100644 --- a/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java +++ b/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java @@ -41,6 +41,7 @@ import com.google.devtools.build.lib.actions.SchedulingActionEvent; import com.google.devtools.build.lib.actions.util.ActionsTestUtil; import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; import com.google.devtools.build.lib.bazel.repository.downloader.DownloadProgressEvent; import com.google.devtools.build.lib.buildeventstream.AnnounceBuildEventTransportsEvent; import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; @@ -61,6 +62,7 @@ import com.google.devtools.build.lib.skyframe.ConfiguredTargetProgressReceiver; import com.google.devtools.build.lib.skyframe.LoadingPhaseStartedEvent; import com.google.devtools.build.lib.skyframe.PackageProgressReceiver; +import com.google.devtools.build.lib.skyframe.TopLevelStatusEvents.TestAnalyzedEvent; import com.google.devtools.build.lib.testutil.FoundationTestCase; import com.google.devtools.build.lib.testutil.ManualClock; import com.google.devtools.build.lib.util.DetailedExitCode; @@ -1555,4 +1557,79 @@ public void waitingRemoteCacheMessage_multipleUploadEvents_countCorrectly() thro String output = terminalWriter.getTranscript(); assertThat(output).contains("2 uploads"); } + + @Test + public void testTestAnalyzedEvent() throws Exception { + // The test count should be visible in the status bar, as well as the short status bar + ManualClock clock = new ManualClock(); + UiStateTracker stateTracker = getUiStateTracker(clock); + // Mimic being at the execution phase. + simulateExecutionPhase(stateTracker); + Label labelA = Label.parseCanonical("//foo:A"); + ConfiguredTarget targetA = mock(ConfiguredTarget.class); + when(targetA.getLabel()).thenReturn(labelA); + TestAnalyzedEvent testAnalyzedEventA = + TestAnalyzedEvent.create( + targetA, mock(BuildConfigurationValue.class), /*isSkipped=*/ false); + Label labelB = Label.parseCanonical("//foo:B"); + ConfiguredTarget targetB = mock(ConfiguredTarget.class); + when(targetB.getLabel()).thenReturn(labelB); + TestAnalyzedEvent testAnalyzedEventB = + TestAnalyzedEvent.create( + targetB, mock(BuildConfigurationValue.class), /*isSkipped=*/ false); + // Only targetA has finished running. + TestSummary testSummary = mock(TestSummary.class); + when(testSummary.getTarget()).thenReturn(targetA); + when(testSummary.getLabel()).thenReturn(labelA); + + stateTracker.singleTestAnalyzed(testAnalyzedEventA); + stateTracker.singleTestAnalyzed(testAnalyzedEventB); + stateTracker.testSummary(testSummary); + + LoggingTerminalWriter terminalWriter = new LoggingTerminalWriter(/*discardHighlight=*/ true); + stateTracker.writeProgressBar(terminalWriter); + String output = terminalWriter.getTranscript(); + assertThat(output).contains(" 1 / 2 tests"); + + terminalWriter = new LoggingTerminalWriter(/*discardHighlight=*/ true); + stateTracker.writeProgressBar(terminalWriter, /* shortVersion=*/ true); + output = terminalWriter.getTranscript(); + assertThat(output).contains(" 1 / 2 tests"); + } + + @Test + public void testTestAnalyzedEvent_repeated_noDuplicatedCount() throws Exception { + // The test count should be visible in the status bar, as well as the short status bar + ManualClock clock = new ManualClock(); + UiStateTracker stateTracker = getUiStateTracker(clock); + // Mimic being at the execution phase. + simulateExecutionPhase(stateTracker); + Label labelA = Label.parseCanonical("//foo:A"); + ConfiguredTarget targetA = mock(ConfiguredTarget.class); + when(targetA.getLabel()).thenReturn(labelA); + TestAnalyzedEvent testAnalyzedEventA = + TestAnalyzedEvent.create( + targetA, mock(BuildConfigurationValue.class), /*isSkipped=*/ false); + TestAnalyzedEvent testAnalyzedEventARepeated = + TestAnalyzedEvent.create( + targetA, mock(BuildConfigurationValue.class), /*isSkipped=*/ false); + // Only targetA has finished running. + TestSummary testSummary = mock(TestSummary.class); + when(testSummary.getTarget()).thenReturn(targetA); + when(testSummary.getLabel()).thenReturn(labelA); + + stateTracker.singleTestAnalyzed(testAnalyzedEventA); + stateTracker.singleTestAnalyzed(testAnalyzedEventARepeated); + stateTracker.testSummary(testSummary); + + LoggingTerminalWriter terminalWriter = new LoggingTerminalWriter(/*discardHighlight=*/ true); + stateTracker.writeProgressBar(terminalWriter); + String output = terminalWriter.getTranscript(); + assertThat(output).contains(" 1 / 1 tests"); + + terminalWriter = new LoggingTerminalWriter(/*discardHighlight=*/ true); + stateTracker.writeProgressBar(terminalWriter, /* shortVersion=*/ true); + output = terminalWriter.getTranscript(); + assertThat(output).contains(" 1 / 1 tests"); + } }