diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeCounter.kt b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeCounter.kt index 35e299c27f1..642bbf19dfe 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeCounter.kt +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeCounter.kt @@ -14,6 +14,8 @@ package com.google.firebase.perf.session.gauges +import androidx.annotation.VisibleForTesting +import com.google.firebase.perf.logging.AndroidLogger import java.util.concurrent.atomic.AtomicInteger /** @@ -23,7 +25,11 @@ import java.util.concurrent.atomic.AtomicInteger object GaugeCounter { private const val MAX_METRIC_COUNT = 25 private val counter = AtomicInteger(0) - private val gaugeManager: GaugeManager = GaugeManager.getInstance() + private val logger = AndroidLogger.getInstance() + + @set:VisibleForTesting(otherwise = VisibleForTesting.NONE) + @set:JvmStatic + var gaugeManager: GaugeManager = GaugeManager.getInstance() fun incrementCounter() { val metricsCount = counter.incrementAndGet() @@ -31,9 +37,19 @@ object GaugeCounter { if (metricsCount >= MAX_METRIC_COUNT) { gaugeManager.logGaugeMetrics() } + + logger.debug("Incremented logger to $metricsCount") } fun decrementCounter() { - counter.decrementAndGet() + val curr = counter.decrementAndGet() + logger.debug("Decremented logger to $curr") } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + fun resetCounter() { + counter.set(0) + } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) fun count(): Int = counter.get() } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeManager.java b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeManager.java index fe36654e8f7..ae15f848146 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeManager.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeManager.java @@ -100,13 +100,19 @@ public void initializeGaugeMetadataManager(Context appContext) { @Override public void onUpdateAppState(ApplicationProcessState applicationProcessState) { - this.applicationProcessState = applicationProcessState; - + // If it isn't a verbose session (or unset) update the app state and return. if (session == null || !session.isVerbose()) { + this.applicationProcessState = applicationProcessState; return; } - // If it's a verbose session, start collecting gauges for the new app state. + // Log existing gauges to the current app state. + logGaugeMetrics(); + + // Update App State. + this.applicationProcessState = applicationProcessState; + + // Start collecting gauges for the new app state. startCollectingGauges(this.applicationProcessState, session.getTimer()); } @@ -132,6 +138,7 @@ public void startCollectingGauges(PerfSession session) { stopCollectingGauges(); } + // TODO(b/394127311): Explore always setting the app state as FOREGROUND. ApplicationProcessState gaugeCollectionApplicationProcessState = applicationProcessState; if (gaugeCollectionApplicationProcessState == ApplicationProcessState.APPLICATION_PROCESS_STATE_UNKNOWN) { @@ -419,4 +426,9 @@ private long getMemoryGaugeCollectionFrequencyMs( return memoryGaugeCollectionFrequency; } } + + @VisibleForTesting + void setApplicationProcessState(ApplicationProcessState applicationProcessState) { + this.applicationProcessState = applicationProcessState; + } } diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerformanceTestBase.java b/firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerformanceTestBase.java index b31696d963b..94a455868bc 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerformanceTestBase.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerformanceTestBase.java @@ -25,9 +25,11 @@ import com.google.firebase.perf.config.ConfigResolver; import com.google.firebase.perf.session.PerfSession; import com.google.firebase.perf.session.SessionManager; +import com.google.firebase.perf.session.gauges.GaugeCounter; import com.google.firebase.perf.util.ImmutableBundle; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.robolectric.shadows.ShadowPackageManager; public class FirebasePerformanceTestBase { @@ -54,6 +56,12 @@ public class FirebasePerformanceTestBase { protected Context appContext; + @BeforeClass + public static void setUpBeforeClass() { + // TODO(b/394127311): Explore removing this. + GaugeCounter.INSTANCE.resetCounter(); + } + @Before public void setUpFirebaseApp() { appContext = ApplicationProvider.getApplicationContext(); diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/session/gauges/GaugeManagerTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/session/gauges/GaugeManagerTest.java index 9ec998bd65f..4d87f40363f 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/session/gauges/GaugeManagerTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/session/gauges/GaugeManagerTest.java @@ -27,7 +27,9 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; +import android.os.Looper; import androidx.test.core.app.ApplicationProvider; import com.google.firebase.components.Lazy; import com.google.firebase.perf.FirebasePerformanceTestBase; @@ -41,9 +43,10 @@ import com.google.firebase.perf.v1.GaugeMetadata; import com.google.firebase.perf.v1.GaugeMetric; import com.google.testing.timing.FakeScheduledExecutorService; +import java.util.Random; import java.util.concurrent.TimeUnit; +import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -124,25 +127,16 @@ public void setUp() { new Lazy<>(() -> fakeMemoryGaugeCollector)); } - @Test - @Ignore // b/394127311 - public void testStartCollectingGaugesStartsCollectingMetricsInBackgroundState() { - PerfSession fakeSession = createTestSession(1); - testGaugeManager.startCollectingGauges(fakeSession); - verify(fakeCpuGaugeCollector) - .startCollecting( - eq(DEFAULT_CPU_GAUGE_COLLECTION_FREQUENCY_BG_MS), - ArgumentMatchers.nullable(Timer.class)); - verify(fakeMemoryGaugeCollector) - .startCollecting( - eq(DEFAULT_MEMORY_GAUGE_COLLECTION_FREQUENCY_BG_MS), - ArgumentMatchers.nullable(Timer.class)); + @After + public void tearDown() { + shadowOf(Looper.getMainLooper()).idle(); } @Test - @Ignore // b/394127311 - public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() { + public void testStartCollectingGaugesStartsCollectingMetricsDefault() { PerfSession fakeSession = createTestSession(1); + testGaugeManager.setApplicationProcessState( + ApplicationProcessState.APPLICATION_PROCESS_STATE_UNKNOWN); testGaugeManager.startCollectingGauges(fakeSession); verify(fakeCpuGaugeCollector) .startCollecting( @@ -155,9 +149,9 @@ public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() } @Test - public void - testStartCollectingGaugesStartCollectingMetricsWithUnknownApplicationProcessStateInForegroundState() { + public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() { PerfSession fakeSession = createTestSession(1); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); testGaugeManager.startCollectingGauges(fakeSession); verify(fakeCpuGaugeCollector) .startCollecting( @@ -170,12 +164,12 @@ public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() } @Test - @Ignore // TODO(b/394127311): Fix public void stopCollectingCPUMetric_invalidCPUCaptureFrequency_OtherMetricsWithValidFrequencyInBackground() { // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyBackgroundMs(); PerfSession fakeSession1 = createTestSession(1); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.BACKGROUND); testGaugeManager.startCollectingGauges(fakeSession1); // Verify that Cpu metric collection is not started @@ -201,12 +195,12 @@ public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() } @Test - @Ignore // TODO(b/394127311): Fix public void startCollectingGaugesOnBackground_invalidMemoryCaptureMs_onlyDisableMemoryCollection() { // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyBackgroundMs(); PerfSession fakeSession1 = createTestSession(1); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.BACKGROUND); testGaugeManager.startCollectingGauges(fakeSession1); // Verify that Memory metric collection is not started @@ -232,7 +226,6 @@ public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() } @Test - @Ignore // TODO(b/394127311): Fix public void stopCollectingCPUMetric_invalidCPUCaptureFrequency_OtherMetricsWithValidFrequency() { // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); @@ -262,12 +255,12 @@ public void stopCollectingCPUMetric_invalidCPUCaptureFrequency_OtherMetricsWithV } @Test - @Ignore // TODO(b/394127311): Fix public void startCollectingGaugesOnForeground_invalidMemoryCaptureMs_onlyDisableMemoryCollection() { // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); PerfSession fakeSession1 = createTestSession(1); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); testGaugeManager.startCollectingGauges(fakeSession1); // Verify that Memory metric collection is not started @@ -293,46 +286,29 @@ public void stopCollectingCPUMetric_invalidCPUCaptureFrequency_OtherMetricsWithV } @Test - public void testStartCollectingGaugesDoesNotStartAJobToConsumeMetricsWithUnknownAppState() { + // TODO(b/394127311): Explore parametrized tests. + public void testStartCollectingGaugesDoesNotStartLogging_default() { PerfSession fakeSession = createTestSession(1); + testGaugeManager.setApplicationProcessState( + ApplicationProcessState.APPLICATION_PROCESS_STATE_UNKNOWN); testGaugeManager.startCollectingGauges(fakeSession); assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); } @Test - @Ignore // TODO(b/394127311): Fix - public void stopCollectingCPUMetrics_invalidCPUCaptureFrequency_appInForegrounf() { - // PASS 1: Test with 0 - doReturn(0L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); - - PerfSession fakeSession1 = createTestSession(1); - testGaugeManager.startCollectingGauges(fakeSession1); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); - - // PASS 2: Test with -ve value - doReturn(-25L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); - - PerfSession fakeSession2 = createTestSession(1); - testGaugeManager.startCollectingGauges(fakeSession2); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + public void testStartCollectingGaugesDoesNotStartLogging_appInForeground() { + PerfSession fakeSession = createTestSession(1); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGauges(fakeSession); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); } @Test - @Ignore // TODO(b/394127311): Fix - public void stopCollectingGauges_invalidMemoryCollectionFrequency_appInForeground() { - // PASS 1: Test with 0 - doReturn(0L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - - PerfSession fakeSession1 = createTestSession(1); - testGaugeManager.startCollectingGauges(fakeSession1); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); - - // PASS 2: Test with -ve value - doReturn(-25L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - - PerfSession fakeSession2 = createTestSession(2); - testGaugeManager.startCollectingGauges(fakeSession2); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + public void testStartCollectingGaugesDoesNotStartLogging_appInBackground() { + PerfSession fakeSession = createTestSession(1); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.BACKGROUND); + testGaugeManager.startCollectingGauges(fakeSession); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); } @Test @@ -355,304 +331,196 @@ public void stopCollectingGauges_invalidGaugeCollectionFrequency_appInForeground } @Test - @Ignore // TODO(b/394127311): Fix - public void startCollectingGauges_validGaugeCollectionFrequency_appInForeground() { - doReturn(25L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); - doReturn(15L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - + public void testGaugeCounterStartsAJobToConsumeTheGeneratedMetrics() throws InterruptedException { PerfSession fakeSession = createTestSession(1); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); testGaugeManager.startCollectingGauges(fakeSession); + GaugeCounter.INSTANCE.setGaugeManager(testGaugeManager); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); - assertThat(fakeScheduledExecutorService.getDelayToNextTask(TimeUnit.MILLISECONDS)) - .isEqualTo(15L * APPROX_NUMBER_OF_DATA_POINTS_PER_GAUGE_METRIC); - } + // There's no job to log the gauges. + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); - @Test - @Ignore // TODO(b/394127311): Fix - public void testStartCollectingGaugesStartsAJobToConsumeTheGeneratedMetrics() { - PerfSession fakeSession = createTestSession(1); - testGaugeManager.startCollectingGauges(fakeSession); + // Generate metrics that don't exceed the GaugeCounter.MAX_COUNT. + generateMetricsAndIncrementCounter(20); + + // There's still no job to log the gauges. + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); + + generateMetricsAndIncrementCounter(10); assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); assertThat(fakeScheduledExecutorService.getDelayToNextTask(TimeUnit.MILLISECONDS)) - .isEqualTo( - getMinimumBackgroundCollectionFrequency() - * APPROX_NUMBER_OF_DATA_POINTS_PER_GAUGE_METRIC); - - CpuMetricReading fakeCpuMetricReading1 = createFakeCpuMetricReading(200, 100); - CpuMetricReading fakeCpuMetricReading2 = createFakeCpuMetricReading(300, 200); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading1); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading2); - - AndroidMemoryReading fakeMemoryMetricReading1 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 123456); - AndroidMemoryReading fakeMemoryMetricReading2 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 23454678); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading1); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading2); + .isEqualTo(TIME_TO_WAIT_BEFORE_FLUSHING_GAUGES_QUEUE_MS); fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric, fakeCpuMetricReading1, fakeCpuMetricReading2); - - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric, fakeMemoryMetricReading1, fakeMemoryMetricReading2); - } - - @Test - @Ignore // TODO(b/394127311): Fix - public void testStopCollectingGaugesStopsCollectingAllGaugeMetrics() { - PerfSession fakeSession = createTestSession(1); + // Generate additional metrics, but doesn't start logging them as it hasn't met the threshold. + generateMetricsAndIncrementCounter(5); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); - testGaugeManager.startCollectingGauges(fakeSession); - verify(fakeCpuGaugeCollector) - .startCollecting(eq(DEFAULT_CPU_GAUGE_COLLECTION_FREQUENCY_BG_MS), ArgumentMatchers.any()); + GaugeMetric recordedGaugeMetric = + getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND); - testGaugeManager.stopCollectingGauges(); + // It flushes all the original metrics in the ConcurrentLinkedQueues, but not the new ones + // added after the task completed. + int recordedGaugeMetricsCount = + recordedGaugeMetric.getAndroidMemoryReadingsCount() + + recordedGaugeMetric.getCpuMetricReadingsCount(); + assertThat(recordedGaugeMetricsCount).isEqualTo(30); - verify(fakeCpuGaugeCollector).stopCollecting(); - verify(fakeMemoryGaugeCollector).stopCollecting(); + assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(testSessionId(1)); } @Test - @Ignore // TODO(b/394127311): Fix - public void testStopCollectingGaugesCreatesOneLastJobToConsumeAnyPendingMetrics() { + public void testUpdateAppStateHandlesMultipleAppStates() { PerfSession fakeSession = createTestSession(1); + fakeSession.setGaugeAndEventCollectionEnabled(true); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); testGaugeManager.startCollectingGauges(fakeSession); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + GaugeCounter.INSTANCE.setGaugeManager(testGaugeManager); - testGaugeManager.stopCollectingGauges(); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + // Generate metrics that don't exceed the GaugeCounter.MAX_COUNT. + generateMetricsAndIncrementCounter(10); - CpuMetricReading fakeCpuMetricReading = createFakeCpuMetricReading(200, 100); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading); + // There's no job to log the gauges. + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); - AndroidMemoryReading fakeMemoryMetricReading = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 23454678); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading); + testGaugeManager.onUpdateAppState(ApplicationProcessState.BACKGROUND); + assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); assertThat(fakeScheduledExecutorService.getDelayToNextTask(TimeUnit.MILLISECONDS)) .isEqualTo(TIME_TO_WAIT_BEFORE_FLUSHING_GAUGES_QUEUE_MS); fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric, fakeCpuMetricReading); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric, fakeMemoryMetricReading); - } + shadowOf(Looper.getMainLooper()).idle(); - @Test - @Ignore // TODO(b/394127311): Fix - public void testGaugeManagerClearsTheQueueEachRun() { - PerfSession fakeSession = createTestSession(1); + // Generate additional metrics in the new app state. + generateMetricsAndIncrementCounter(26); - testGaugeManager.startCollectingGauges(fakeSession); + GaugeMetric recordedGaugeMetric = + getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND); - fakeCpuGaugeCollector.cpuMetricReadings.add(createFakeCpuMetricReading(200, 100)); - fakeCpuGaugeCollector.cpuMetricReadings.add(createFakeCpuMetricReading(300, 400)); - fakeMemoryGaugeCollector.memoryMetricReadings.add( - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 1234)); + // It flushes all metrics in the ConcurrentLinkedQueues. + int recordedGaugeMetricsCount = + recordedGaugeMetric.getAndroidMemoryReadingsCount() + + recordedGaugeMetric.getCpuMetricReadingsCount(); + assertThat(recordedGaugeMetricsCount).isEqualTo(10); - assertThat(fakeCpuGaugeCollector.cpuMetricReadings).isNotEmpty(); - assertThat(fakeMemoryGaugeCollector.memoryMetricReadings).isNotEmpty(); + assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(testSessionId(1)); + // Simulate gauges collected in the new app state. fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - assertThat(fakeCpuGaugeCollector.cpuMetricReadings).isEmpty(); - assertThat(fakeMemoryGaugeCollector.memoryMetricReadings).isEmpty(); + shadowOf(Looper.getMainLooper()).idle(); - fakeCpuGaugeCollector.cpuMetricReadings.add(createFakeCpuMetricReading(200, 100)); - fakeMemoryGaugeCollector.memoryMetricReadings.add( - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 1234)); - fakeMemoryGaugeCollector.memoryMetricReadings.add( - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 2345)); + recordedGaugeMetric = getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND); - assertThat(fakeCpuGaugeCollector.cpuMetricReadings).isNotEmpty(); - assertThat(fakeMemoryGaugeCollector.memoryMetricReadings).isNotEmpty(); + // Verify the metrics in the new app state. + recordedGaugeMetricsCount = + recordedGaugeMetric.getAndroidMemoryReadingsCount() + + recordedGaugeMetric.getCpuMetricReadingsCount(); + assertThat(recordedGaugeMetricsCount).isEqualTo(26); - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - assertThat(fakeCpuGaugeCollector.cpuMetricReadings).isEmpty(); - assertThat(fakeMemoryGaugeCollector.memoryMetricReadings).isEmpty(); + assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(testSessionId(1)); } @Test - @Ignore // TODO(b/394127311): Fix - public void testStartingGaugeManagerWithNewSessionIdButSameAppState() { - PerfSession fakeSession1 = createTestSession(1); + public void testGaugeManagerHandlesMultipleSessionIds() { + PerfSession fakeSession = createTestSession(1); + fakeSession.setGaugeAndEventCollectionEnabled(true); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.BACKGROUND); + testGaugeManager.startCollectingGauges(fakeSession); + GaugeCounter.INSTANCE.setGaugeManager(testGaugeManager); - // Start collecting Gauges. - testGaugeManager.startCollectingGauges(fakeSession1); - CpuMetricReading fakeCpuMetricReading1 = createFakeCpuMetricReading(200, 100); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading1); - AndroidMemoryReading fakeMemoryMetricReading1 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 1234); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading1); + // Generate metrics that don't exceed the GaugeCounter.MAX_COUNT. + generateMetricsAndIncrementCounter(10); - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric1 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric1, fakeCpuMetricReading1); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric1, fakeMemoryMetricReading1); - - // One Cpu and Memory metric was added when the gauge was collecting for the previous sessionId. - CpuMetricReading fakeCpuMetricReading2 = createFakeCpuMetricReading(400, 500); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading2); - AndroidMemoryReading fakeMemoryMetricReading2 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 2345); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading2); + PerfSession updatedPerfSession = createTestSession(2); + updatedPerfSession.setGaugeAndEventCollectionEnabled(true); - PerfSession fakeSession2 = createTestSession(2); + // A new session and updated app state. + testGaugeManager.startCollectingGauges(updatedPerfSession); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); - // Start collecting gauges for new session, but same app state. - testGaugeManager.startCollectingGauges(fakeSession2); + assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + assertThat(fakeScheduledExecutorService.getDelayToNextTask(TimeUnit.MILLISECONDS)) + .isEqualTo(TIME_TO_WAIT_BEFORE_FLUSHING_GAUGES_QUEUE_MS); - // The next sweep conducted by GaugeManager still associates metrics to old sessionId and state. fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric2 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric2, fakeCpuMetricReading2); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric2, fakeMemoryMetricReading2); - - // Collect some more Cpu and Memory metrics and verify that they're associated with new - // sessionId and state. - CpuMetricReading fakeCpuMetricReading3 = createFakeCpuMetricReading(500, 600); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading3); - AndroidMemoryReading fakeMemoryMetricReading3 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 3456); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading3); + shadowOf(Looper.getMainLooper()).idle(); + + // Generate metrics for the new session. + generateMetricsAndIncrementCounter(26); + + GaugeMetric recordedGaugeMetric = + getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND); + + // It flushes all metrics in the ConcurrentLinkedQueues. + int recordedGaugeMetricsCount = + recordedGaugeMetric.getAndroidMemoryReadingsCount() + + recordedGaugeMetric.getCpuMetricReadingsCount(); + assertThat(recordedGaugeMetricsCount).isEqualTo(10); + assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(testSessionId(1)); + + // Simulate gauges collected in the new app state. fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric3 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(2), recordedGaugeMetric3, fakeCpuMetricReading3); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(2), recordedGaugeMetric3, fakeMemoryMetricReading3); + shadowOf(Looper.getMainLooper()).idle(); + + recordedGaugeMetric = getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND); + + // Verify the metrics in the new app state. + recordedGaugeMetricsCount = + recordedGaugeMetric.getAndroidMemoryReadingsCount() + + recordedGaugeMetric.getCpuMetricReadingsCount(); + assertThat(recordedGaugeMetricsCount).isEqualTo(26); + + assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(testSessionId(2)); } @Test - @Ignore // TODO(b/394127311): Fix - public void testStartGaugeManagerWithSameSessionIdButDifferentAppState() { + public void testStopCollectingGaugesStopsCollectingAllGaugeMetrics() { PerfSession fakeSession = createTestSession(1); - // Start collecting Gauges. - testGaugeManager.startCollectingGauges(fakeSession); - CpuMetricReading fakeCpuMetricReading1 = createFakeCpuMetricReading(200, 100); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading1); - AndroidMemoryReading fakeMemoryMetricReading1 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 1234); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading1); - - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric1 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric1, fakeCpuMetricReading1); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric1, fakeMemoryMetricReading1); - - // One Cpu and Memory metric was added when the gauge was collecting for the previous sessionId. - CpuMetricReading fakeCpuMetricReading2 = createFakeCpuMetricReading(400, 500); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading2); - AndroidMemoryReading fakeMemoryMetricReading2 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 2345); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading2); - - // Start collecting gauges for same session, but new app state testGaugeManager.startCollectingGauges(fakeSession); + verify(fakeCpuGaugeCollector) + .startCollecting(eq(DEFAULT_CPU_GAUGE_COLLECTION_FREQUENCY_FG_MS), ArgumentMatchers.any()); + verify(fakeMemoryGaugeCollector) + .startCollecting( + eq(DEFAULT_MEMORY_GAUGE_COLLECTION_FREQUENCY_FG_MS), ArgumentMatchers.any()); - // The next sweep conducted by GaugeManager still associates metrics to old sessionId and state. - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric2 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric2, fakeCpuMetricReading2); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric2, fakeMemoryMetricReading2); - - // Collect some more Cpu and Memory metrics and verify that they're associated with new - // sessionId and state. - CpuMetricReading fakeCpuMetricReading3 = createFakeCpuMetricReading(500, 600); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading3); - AndroidMemoryReading fakeMemoryMetricReading3 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 3456); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading3); + testGaugeManager.stopCollectingGauges(); - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric3 = - getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric3, fakeCpuMetricReading3); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric3, fakeMemoryMetricReading3); + verify(fakeCpuGaugeCollector).stopCollecting(); + verify(fakeMemoryGaugeCollector).stopCollecting(); } @Test - @Ignore // TODO(b/394127311): Fix - public void testStartGaugeManagerWithNewSessionIdAndNewAppState() { - PerfSession fakeSession1 = createTestSession(1); - - // Start collecting Gauges. - testGaugeManager.startCollectingGauges(fakeSession1); - CpuMetricReading fakeCpuMetricReading1 = createFakeCpuMetricReading(200, 100); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading1); - AndroidMemoryReading fakeMemoryMetricReading1 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 1234); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading1); + public void testStopCollectingGaugesCreatesOneLastJobToConsumeAnyPendingMetrics() { + PerfSession fakeSession = createTestSession(1); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGauges(fakeSession); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric1 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric1, fakeCpuMetricReading1); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric1, fakeMemoryMetricReading1); - - // One Cpu and Memory metric was added when the gauge was collecting for the previous sessionId. - CpuMetricReading fakeCpuMetricReading2 = createFakeCpuMetricReading(400, 500); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading2); - AndroidMemoryReading fakeMemoryMetricReading2 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 2345); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading2); + generateMetricsAndIncrementCounter(2); - PerfSession fakeSession2 = createTestSession(2); + testGaugeManager.stopCollectingGauges(); + assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); - // Start collecting gauges for new session and new app state - testGaugeManager.startCollectingGauges(fakeSession2); + assertThat(fakeScheduledExecutorService.getDelayToNextTask(TimeUnit.MILLISECONDS)) + .isEqualTo(TIME_TO_WAIT_BEFORE_FLUSHING_GAUGES_QUEUE_MS); - // The next sweep conducted by GaugeManager still associates metrics to old sessionId and state. fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric2 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric2, fakeCpuMetricReading2); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(1), recordedGaugeMetric2, fakeMemoryMetricReading2); - - // Collect some more Cpu and Memory metrics and verify that they're associated with new - // sessionId and state. - CpuMetricReading fakeCpuMetricReading3 = createFakeCpuMetricReading(500, 600); - fakeCpuGaugeCollector.cpuMetricReadings.add(fakeCpuMetricReading3); - AndroidMemoryReading fakeMemoryMetricReading3 = - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 3456); - fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading3); - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric3 = - getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - testSessionId(2), recordedGaugeMetric3, fakeCpuMetricReading3); - assertThatMemoryGaugeMetricWasSentToTransport( - testSessionId(2), recordedGaugeMetric3, fakeMemoryMetricReading3); + GaugeMetric recordedGaugeMetric = + getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND); + assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(testSessionId(1)); + int recordedGaugeMetricsCount = + recordedGaugeMetric.getAndroidMemoryReadingsCount() + + recordedGaugeMetric.getCpuMetricReadingsCount(); + assertThat(recordedGaugeMetricsCount).isEqualTo(2); + + // TODO(b/394127311): Investigate why this isn't 0 on local runs. + // assertThat(GaugeCounter.INSTANCE.count()).isEqualTo(0); } @Test @@ -664,7 +532,7 @@ public void testLogGaugeMetadataSendDataToTransport() { testGaugeManager.logGaugeMetadata(testSessionId(1), ApplicationProcessState.FOREGROUND); GaugeMetric recordedGaugeMetric = - getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND, 1); + getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND); GaugeMetadata recordedGaugeMetadata = recordedGaugeMetric.getGaugeMetadata(); assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(testSessionId(1)); @@ -679,7 +547,6 @@ public void testLogGaugeMetadataSendDataToTransport() { @Test public void testLogGaugeMetadataDoesNotLogWhenGaugeMetadataManagerNotAvailable() { - testGaugeManager = new GaugeManager( new Lazy<>(() -> fakeScheduledExecutorService), @@ -716,10 +583,11 @@ public void testLogGaugeMetadataLogsAfterApplicationContextIsSet() { .isTrue(); GaugeMetric recordedGaugeMetric = - getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND, 1); + getLastRecordedGaugeMetric(ApplicationProcessState.FOREGROUND); GaugeMetadata recordedGaugeMetadata = recordedGaugeMetric.getGaugeMetadata(); assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(testSessionId(1)); + assertThat(recordedGaugeMetadata).isNotEqualTo(GaugeMetadata.getDefaultInstance()); } @Test @@ -739,6 +607,22 @@ private long getMinimumBackgroundCollectionFrequency() { return DEFAULT_CPU_GAUGE_COLLECTION_FREQUENCY_BG_MS; } + // Simulates the behavior of Cpu and Memory Gauge collector. + private void generateMetricsAndIncrementCounter(int count) { + // TODO(b/394127311): Explore actually collecting metrics using the fake Cpu and Memory + // metric collectors. + Random random = new Random(); + for (int i = 0; i < count; ++i) { + if (random.nextInt(2) == 0) { + fakeCpuGaugeCollector.cpuMetricReadings.add(createFakeCpuMetricReading(100, 200)); + GaugeCounter.INSTANCE.incrementCounter(); + } else { + fakeMemoryGaugeCollector.memoryMetricReadings.add(createFakeAndroidMetricReading(100)); + GaugeCounter.INSTANCE.incrementCounter(); + } + } + } + private CpuMetricReading createFakeCpuMetricReading(long userTimeUs, long systemTimeUs) { CpuMetricReading.Builder fakeMetricReadingBuilder = CpuMetricReading.newBuilder(); fakeMetricReadingBuilder.setClientTimeUs(System.currentTimeMillis()); @@ -758,35 +642,18 @@ private AndroidMemoryReading createFakeAndroidMetricReading(int currentUsedAppJa * Gets the last recorded GaugeMetric, and verifies that they were logged for the right {@link * ApplicationProcessState}. * - * @param applicationProcessState The expected {@link ApplicationProcessState} that it was logged - * to. - * @param timesLogged Number of {@link GaugeMetric} that were expected to be logged to Transport. + * @param expectedApplicationProcessState The expected {@link ApplicationProcessState} that it was logged + * to. * @return The last logged {@link GaugeMetric}. */ private GaugeMetric getLastRecordedGaugeMetric( - ApplicationProcessState applicationProcessState, int timesLogged) { + ApplicationProcessState expectedApplicationProcessState) { ArgumentCaptor argMetric = ArgumentCaptor.forClass(GaugeMetric.class); - verify(mockTransportManager, times(timesLogged)) - .log(argMetric.capture(), eq(applicationProcessState)); + verify(mockTransportManager, times(1)) + .log(argMetric.capture(), eq(expectedApplicationProcessState)); reset(mockTransportManager); // Required after resetting the mock. By default we assume that Transport is initialized. when(mockTransportManager.isInitialized()).thenReturn(true); return argMetric.getValue(); } - - private void assertThatCpuGaugeMetricWasSentToTransport( - String sessionId, GaugeMetric recordedGaugeMetric, CpuMetricReading... cpuMetricReadings) { - assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(sessionId); - assertThat(recordedGaugeMetric.getCpuMetricReadingsList()) - .containsAtLeastElementsIn(cpuMetricReadings); - } - - private void assertThatMemoryGaugeMetricWasSentToTransport( - String sessionId, - GaugeMetric recordedGaugeMetric, - AndroidMemoryReading... androidMetricReadings) { - assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(sessionId); - assertThat(recordedGaugeMetric.getAndroidMemoryReadingsList()) - .containsAtLeastElementsIn(androidMetricReadings); - } }