diff --git a/firebase-perf/firebase-perf.gradle b/firebase-perf/firebase-perf.gradle index c0fd6df6056..0e6c0f47029 100644 --- a/firebase-perf/firebase-perf.gradle +++ b/firebase-perf/firebase-perf.gradle @@ -70,6 +70,7 @@ android { buildConfigField("String", "TRANSPORT_LOG_SRC", "String.valueOf(\"FIREPERF\")") buildConfigField("Boolean", "ENFORCE_DEFAULT_LOG_SRC", "Boolean.valueOf(false)") buildConfigField("String", "FIREPERF_VERSION_NAME", "String.valueOf(\"" + property("version") + "\")") + buildConfigField("Boolean", "ENFORCE_LEGACY_SESSIONS", "Boolean.valueOf(false)") if (project.hasProperty("fireperfBuildForAutopush")) { // This allows the SDK to be built for "Autopush" env when the mentioned flag @@ -77,6 +78,7 @@ android { // SDK or the Test App). buildConfigField("String", "TRANSPORT_LOG_SRC", "String.valueOf(\"FIREPERF_AUTOPUSH\")") buildConfigField("Boolean", "ENFORCE_DEFAULT_LOG_SRC", "Boolean.valueOf(true)") + buildConfigField("Boolean", "ENFORCE_LEGACY_SESSIONS", "Boolean.valueOf(true)") } minSdkVersion project.minSdkVersion @@ -118,7 +120,7 @@ dependencies { api("com.google.firebase:firebase-components:18.0.0") api("com.google.firebase:firebase-config:21.5.0") api("com.google.firebase:firebase-installations:17.2.0") - api("com.google.firebase:firebase-sessions:2.0.7") { + api(project(":firebase-sessions")) { exclude group: 'com.google.firebase', module: 'firebase-common' exclude group: 'com.google.firebase', module: 'firebase-common-ktx' exclude group: 'com.google.firebase', module: 'firebase-components' @@ -138,4 +140,5 @@ dependencies { testImplementation libs.mockito.core testImplementation libs.mockito.mockito.inline testImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.6' + testImplementation "androidx.arch.core:core-testing:2.1.0" } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java index 5b89deaad82..766fd7daa95 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java @@ -51,12 +51,10 @@ public FirebasePerfEarly( uiExecutor.execute(new AppStartTrace.StartFromBackgroundRunnable(appStartTrace)); } - // TODO: Bring back Firebase Sessions dependency to watch for updates to sessions. - // In the case of cold start, we create a session and start collecting gauges as early as // possible. - // There is code in SessionManager that prevents us from resetting the session twice in case - // of app cold start. + // Uploading the gauges however only starts once AQS is initialized. + // TODO(b/394127311): Update this to use changes in AQS initialization, SessionManager.getInstance().initializeGaugeCollection(); } } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java index c01f035af1f..daffc2de81a 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java @@ -30,6 +30,8 @@ import com.google.firebase.perf.injection.modules.FirebasePerformanceModule; import com.google.firebase.platforminfo.LibraryVersionComponent; import com.google.firebase.remoteconfig.RemoteConfigComponent; +import com.google.firebase.sessions.api.FirebaseSessionsDependencies; +import com.google.firebase.sessions.api.SessionSubscriber; import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; @@ -47,6 +49,11 @@ public class FirebasePerfRegistrar implements ComponentRegistrar { private static final String LIBRARY_NAME = "fire-perf"; private static final String EARLY_LIBRARY_NAME = "fire-perf-early"; + static { + // Add Firebase Performance as a dependency of Sessions when this class is loaded into memory. + FirebaseSessionsDependencies.addDependency(SessionSubscriber.Name.PERFORMANCE); + } + @Override @Keep public List> getComponents() { diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerformance.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerformance.java index 40468566225..5ff288ebb40 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerformance.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerformance.java @@ -34,14 +34,18 @@ import com.google.firebase.perf.config.RemoteConfigManager; import com.google.firebase.perf.logging.AndroidLogger; import com.google.firebase.perf.logging.ConsoleUrlGenerator; +import com.google.firebase.perf.logging.DebugEnforcementCheck; import com.google.firebase.perf.metrics.HttpMetric; import com.google.firebase.perf.metrics.Trace; +import com.google.firebase.perf.session.FirebasePerformanceSessionSubscriber; import com.google.firebase.perf.session.SessionManager; import com.google.firebase.perf.transport.TransportManager; import com.google.firebase.perf.util.Constants; import com.google.firebase.perf.util.ImmutableBundle; import com.google.firebase.perf.util.Timer; import com.google.firebase.remoteconfig.RemoteConfigComponent; +import com.google.firebase.sessions.api.FirebaseSessionsDependencies; +import com.google.firebase.sessions.api.SessionSubscriber; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.URL; @@ -92,6 +96,8 @@ public class FirebasePerformance implements FirebasePerformanceAttributable { // once during initialization and cache it. private final ImmutableBundle mMetadataBundle; + private final SessionSubscriber sessionSubscriber; + /** Valid HttpMethods for manual network APIs */ @StringDef({ HttpMethod.GET, @@ -136,11 +142,6 @@ public static FirebasePerformance getInstance() { // to false if it's been force disabled or it is set to null if neither. @Nullable private Boolean mPerformanceCollectionForceEnabledState = null; - private final FirebaseApp firebaseApp; - private final Provider firebaseRemoteConfigProvider; - private final FirebaseInstallationsApi firebaseInstallationsApi; - private final Provider transportFactoryProvider; - /** * Constructs the FirebasePerformance class and allows injecting dependencies. * @@ -166,23 +167,19 @@ public static FirebasePerformance getInstance() { ConfigResolver configResolver, SessionManager sessionManager) { - this.firebaseApp = firebaseApp; - this.firebaseRemoteConfigProvider = firebaseRemoteConfigProvider; - this.firebaseInstallationsApi = firebaseInstallationsApi; - this.transportFactoryProvider = transportFactoryProvider; - if (firebaseApp == null) { this.mPerformanceCollectionForceEnabledState = false; this.configResolver = configResolver; this.mMetadataBundle = new ImmutableBundle(new Bundle()); + this.sessionSubscriber = new FirebasePerformanceSessionSubscriber(false); return; } + DebugEnforcementCheck.setEnforcement(BuildConfig.ENFORCE_LEGACY_SESSIONS); TransportManager.getInstance() .initialize(firebaseApp, firebaseInstallationsApi, transportFactoryProvider); Context appContext = firebaseApp.getApplicationContext(); - // TODO(b/110178816): Explore moving off of main thread. mMetadataBundle = extractMetadata(appContext); remoteConfigManager.setFirebaseRemoteConfigProvider(firebaseRemoteConfigProvider); @@ -192,6 +189,9 @@ public static FirebasePerformance getInstance() { sessionManager.setApplicationContext(appContext); mPerformanceCollectionForceEnabledState = configResolver.getIsPerformanceCollectionEnabled(); + sessionSubscriber = new FirebasePerformanceSessionSubscriber(isPerformanceCollectionEnabled()); + FirebaseSessionsDependencies.register(sessionSubscriber); + if (logger.isLogcatEnabled() && isPerformanceCollectionEnabled()) { logger.info( String.format( @@ -282,7 +282,7 @@ public synchronized void setPerformanceCollectionEnabled(@Nullable Boolean enabl return; } - if (configResolver.getIsPerformanceCollectionDeactivated()) { + if (Boolean.TRUE.equals(configResolver.getIsPerformanceCollectionDeactivated())) { logger.info("Firebase Performance is permanently disabled"); return; } @@ -466,4 +466,9 @@ private static ImmutableBundle extractMetadata(Context appContext) { Boolean getPerformanceCollectionForceEnabledState() { return mPerformanceCollectionForceEnabledState; } + + @VisibleForTesting + SessionSubscriber getSessionSubscriber() { + return sessionSubscriber; + } } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/logging/DebugEnforcementCheck.kt b/firebase-perf/src/main/java/com/google/firebase/perf/logging/DebugEnforcementCheck.kt new file mode 100644 index 00000000000..ad22eadd59a --- /dev/null +++ b/firebase-perf/src/main/java/com/google/firebase/perf/logging/DebugEnforcementCheck.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.perf.logging + +import com.google.firebase.perf.session.PerfSession +import com.google.firebase.perf.session.isLegacy + +class DebugEnforcementCheck { + companion object { + /** When enabled, failed preconditions will cause assertion errors for debugging. */ + @JvmStatic var enforcement: Boolean = false + private var logger: AndroidLogger = AndroidLogger.getInstance() + + fun checkSession(session: PerfSession, failureMessage: String) { + if (session.isLegacy()) { + logger.debug("legacy session ${session.sessionId()}: $failureMessage") + assert(!enforcement) { failureMessage } + } + } + + fun checkSession(sessionId: String, failureMessage: String) { + if (sessionId.isLegacy()) { + logger.debug("legacy session ${sessionId}: $failureMessage") + assert(!enforcement) { failureMessage } + } + } + } +} diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/FirebasePerformanceSessionSubscriber.kt b/firebase-perf/src/main/java/com/google/firebase/perf/session/FirebasePerformanceSessionSubscriber.kt new file mode 100644 index 00000000000..750a82a2008 --- /dev/null +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/FirebasePerformanceSessionSubscriber.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.perf.session + +import com.google.firebase.perf.logging.DebugEnforcementCheck +import com.google.firebase.perf.session.gauges.GaugeManager +import com.google.firebase.perf.v1.ApplicationProcessState +import com.google.firebase.sessions.api.SessionSubscriber + +class FirebasePerformanceSessionSubscriber(override val isDataCollectionEnabled: Boolean) : + SessionSubscriber { + + override val sessionSubscriberName: SessionSubscriber.Name = SessionSubscriber.Name.PERFORMANCE + + override fun onSessionChanged(sessionDetails: SessionSubscriber.SessionDetails) { + val currentPerfSession = SessionManager.getInstance().perfSession() + DebugEnforcementCheck.checkSession(currentPerfSession, "onSessionChanged") + + // A [PerfSession] was created before a session was started. + // Since these were verbose gauge collection at app startup, it logs them to the updated session + // ID. + if (currentPerfSession.isLegacy() && currentPerfSession.isVerbose) { + GaugeManager.getInstance() + .logGaugeMetadata(sessionDetails.sessionId, ApplicationProcessState.FOREGROUND) + GaugeManager.getInstance() + .stopCollectingGaugesForLegacySession( + sessionDetails.sessionId, + ApplicationProcessState.FOREGROUND + ) + } + + val updatedSession = PerfSession.createWithId(sessionDetails.sessionId) + SessionManager.getInstance().updatePerfSession(updatedSession) + GaugeManager.getInstance() + .logGaugeMetadata(updatedSession.sessionId(), ApplicationProcessState.FOREGROUND) + } +} diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/FirebaseSessionsHelper.kt b/firebase-perf/src/main/java/com/google/firebase/perf/session/FirebaseSessionsHelper.kt new file mode 100644 index 00000000000..71e251119f4 --- /dev/null +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/FirebaseSessionsHelper.kt @@ -0,0 +1,24 @@ +package com.google.firebase.perf.session + +import com.google.firebase.perf.util.Constants +import java.util.UUID + +/** Identifies whether the [PerfSession] is legacy or not. */ +fun PerfSession.isLegacy(): Boolean { + return this.sessionId().isLegacy() +} + +/** Identifies whether the string is from a legacy [PerfSession]. */ +fun String.isLegacy(): Boolean { + return this.startsWith(Constants.UNDEFINED_AQS_ID_PREFIX) +} + +/** Creates a valid session ID for [PerfSession] that can be predictably identified as legacy. */ +fun createLegacySessionId(): String { + val uuid = UUID.randomUUID().toString().replace("-", "") + return uuid.replaceRange( + 0, + Constants.UNDEFINED_AQS_ID_PREFIX.length, + Constants.UNDEFINED_AQS_ID_PREFIX + ) +} diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/PerfSession.java b/firebase-perf/src/main/java/com/google/firebase/perf/session/PerfSession.java index 160a4507560..a89c8987896 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/session/PerfSession.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/PerfSession.java @@ -28,20 +28,20 @@ /** Details of a session including a unique Id and related information. */ public class PerfSession implements Parcelable { - - private final String sessionId; private final Timer creationTime; - + private final String sessionId; private boolean isGaugeAndEventCollectionEnabled = false; /* * Creates a PerfSession object and decides what metrics to collect. */ - public static PerfSession createWithId(@NonNull String sessionId) { - String prunedSessionId = sessionId.replace("-", ""); - PerfSession session = new PerfSession(prunedSessionId, new Clock()); - session.setGaugeAndEventCollectionEnabled(shouldCollectGaugesAndEvents()); - + public static PerfSession createWithId(@Nullable String aqsSessionId) { + String sessionId = aqsSessionId; + if (sessionId == null) { + sessionId = FirebaseSessionsHelperKt.createLegacySessionId(); + } + PerfSession session = new PerfSession(sessionId, new Clock()); + session.setGaugeAndEventCollectionEnabled(session.shouldCollectGaugesAndEvents()); return session; } @@ -59,7 +59,8 @@ private PerfSession(@NonNull Parcel in) { creationTime = in.readParcelable(Timer.class.getClassLoader()); } - /** Returns the sessionId of the object. */ + /** Returns the sessionId for the given session. */ + @NonNull public String sessionId() { return sessionId; } @@ -90,18 +91,6 @@ public boolean isVerbose() { return isGaugeAndEventCollectionEnabled; } - /** Checks if the current {@link com.google.firebase.perf.v1.PerfSession} is verbose or not. */ - @VisibleForTesting - static boolean isVerbose(@NonNull com.google.firebase.perf.v1.PerfSession perfSession) { - for (SessionVerbosity sessionVerbosity : perfSession.getSessionVerbosityList()) { - if (sessionVerbosity == SessionVerbosity.GAUGES_AND_SYSTEM_EVENTS) { - return true; - } - } - - return false; - } - /** * Checks if it has been more than {@link ConfigResolver#getSessionsMaxDurationMinutes()} time * since the creation time of the current session. @@ -163,11 +152,11 @@ public static com.google.firebase.perf.v1.PerfSession[] buildAndSort( } /** If true, Session Gauge collection is enabled. */ - public static boolean shouldCollectGaugesAndEvents() { + public boolean shouldCollectGaugesAndEvents() { ConfigResolver configResolver = ConfigResolver.getInstance(); - return configResolver.isPerformanceMonitoringEnabled() - && Math.random() < configResolver.getSessionsSamplingRate(); + && (Math.abs(this.sessionId.hashCode() % 100) + < configResolver.getSessionsSamplingRate() * 100); } /** @@ -208,4 +197,16 @@ public PerfSession[] newArray(int size) { return new PerfSession[size]; } }; + + /** Checks if the current {@link com.google.firebase.perf.v1.PerfSession} is verbose or not. */ + @VisibleForTesting + static boolean isVerbose(@NonNull com.google.firebase.perf.v1.PerfSession perfSession) { + for (SessionVerbosity sessionVerbosity : perfSession.getSessionVerbosityList()) { + if (sessionVerbosity == SessionVerbosity.GAUGES_AND_SYSTEM_EVENTS) { + return true; + } + } + + return false; + } } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/SessionManager.java b/firebase-perf/src/main/java/com/google/firebase/perf/session/SessionManager.java index 79d034b9b0b..9ce86249795 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/session/SessionManager.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/SessionManager.java @@ -20,6 +20,7 @@ import androidx.annotation.VisibleForTesting; import com.google.firebase.perf.application.AppStateMonitor; import com.google.firebase.perf.application.AppStateUpdateHandler; +import com.google.firebase.perf.logging.DebugEnforcementCheck; import com.google.firebase.perf.session.gauges.GaugeManager; import com.google.firebase.perf.v1.ApplicationProcessState; import com.google.firebase.perf.v1.GaugeMetadata; @@ -27,11 +28,8 @@ import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.Iterator; +import java.util.Objects; import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; /** Session manager to generate sessionIDs and broadcast to the application. */ @Keep // Needed because of b/117526359. @@ -45,7 +43,6 @@ public class SessionManager extends AppStateUpdateHandler { private final Set> clients = new HashSet<>(); private PerfSession perfSession; - private Future syncInitFuture; /** Returns the singleton instance of SessionManager. */ public static SessionManager getInstance() { @@ -54,15 +51,13 @@ public static SessionManager getInstance() { /** Returns the currently active PerfSession. */ public final PerfSession perfSession() { + DebugEnforcementCheck.Companion.checkSession(perfSession, "SessionManager.perfSession()"); return perfSession; } private SessionManager() { // Generate a new sessionID for every cold start. - this( - GaugeManager.getInstance(), - PerfSession.createWithId(UUID.randomUUID().toString()), - AppStateMonitor.getInstance()); + this(GaugeManager.getInstance(), PerfSession.createWithId(null), AppStateMonitor.getInstance()); } @VisibleForTesting @@ -71,57 +66,28 @@ public SessionManager( this.gaugeManager = gaugeManager; this.perfSession = perfSession; this.appStateMonitor = appStateMonitor; - registerForAppState(); - } - - /** - * Finalizes gauge initialization during cold start. This must be called before app start finishes - * (currently that is before onResume finishes) to ensure gauge collection starts on time. - */ - public void setApplicationContext(final Context appContext) { - // Get PerfSession in main thread first, because it is possible that app changes fg/bg state - // which creates a new perfSession, before the following is executed in background thread - final PerfSession appStartSession = perfSession; - // TODO(b/258263016): Migrate to go/firebase-android-executors - @SuppressLint("ThreadPoolCreation") - ExecutorService executorService = Executors.newSingleThreadExecutor(); - syncInitFuture = - executorService.submit( - () -> { - gaugeManager.initializeGaugeMetadataManager(appContext); - if (appStartSession.isGaugeAndEventCollectionEnabled()) { - gaugeManager.logGaugeMetadata( - appStartSession.sessionId(), ApplicationProcessState.FOREGROUND); - } - }); } @Override public void onUpdateAppState(ApplicationProcessState newAppState) { super.onUpdateAppState(newAppState); - if (appStateMonitor.isColdStart()) { - // We want the Session to remain unchanged if this is a cold start of the app since we already - // update the PerfSession in FirebasePerfProvider#onAttachInfo(). + // Ignore the app state change if it's a cold start. [FirebasePerformanceSessionSubscriber] + // handles any change that's needed. return; } - if (newAppState == ApplicationProcessState.FOREGROUND) { - // A new foregrounding of app will force a new sessionID generation. - PerfSession session = PerfSession.createWithId(UUID.randomUUID().toString()); - updatePerfSession(session); - } else { - // If the session is running for too long, generate a new session and collect gauges as - // necessary. - if (perfSession.isSessionRunningTooLong()) { - PerfSession session = PerfSession.createWithId(UUID.randomUUID().toString()); - updatePerfSession(session); - } else { - // For any other state change of the application, modify gauge collection state as - // necessary. - startOrStopCollectingGauges(newAppState); - } - } + // While the change in app state doesn't start or stop gauge collection, it updates the upload + // and gauge collection frequency. + updateGaugeCollection(newAppState); + } + + /** + * Finalizes gauge initialization during cold start. This must be called before app start finishes + * (currently that is before onResume finishes) to ensure gauge collection starts on time. + */ + public void setApplicationContext(final Context appContext) { + gaugeManager.initializeGaugeMetadataManager(appContext); } /** @@ -145,7 +111,7 @@ public void stopGaugeCollectionIfSessionRunningTooLong() { */ public void updatePerfSession(PerfSession perfSession) { // Do not update the perf session if it is the exact same sessionId. - if (perfSession.sessionId() == this.perfSession.sessionId()) { + if (Objects.equals(perfSession.sessionId(), this.perfSession.sessionId())) { return; } @@ -164,11 +130,9 @@ public void updatePerfSession(PerfSession perfSession) { } } - // Log the gauge metadata event if data collection is enabled. - logGaugeMetadataIfCollectionEnabled(appStateMonitor.getAppState()); - - // Start of stop the gauge data collection. - startOrStopCollectingGauges(appStateMonitor.getAppState()); + startOrStopCollectingGauges(); + // A session is *only* updated in Foreground. + updateGaugeCollection(ApplicationProcessState.FOREGROUND); } /** @@ -178,8 +142,7 @@ public void updatePerfSession(PerfSession perfSession) { * this does not reset the perfSession. */ public void initializeGaugeCollection() { - logGaugeMetadataIfCollectionEnabled(ApplicationProcessState.FOREGROUND); - startOrStopCollectingGauges(ApplicationProcessState.FOREGROUND); + startOrStopCollectingGauges(); } /** @@ -206,27 +169,20 @@ public void unregisterForSessionUpdates(WeakReference client } } - private void logGaugeMetadataIfCollectionEnabled(ApplicationProcessState appState) { - if (perfSession.isGaugeAndEventCollectionEnabled()) { - gaugeManager.logGaugeMetadata(perfSession.sessionId(), appState); - } - } - - private void startOrStopCollectingGauges(ApplicationProcessState appState) { + private void startOrStopCollectingGauges() { if (perfSession.isGaugeAndEventCollectionEnabled()) { - gaugeManager.startCollectingGauges(perfSession, appState); + gaugeManager.startCollectingGaugeMetrics(perfSession); } else { gaugeManager.stopCollectingGauges(); } } - @VisibleForTesting - public void setPerfSession(PerfSession perfSession) { - this.perfSession = perfSession; + private void updateGaugeCollection(ApplicationProcessState applicationProcessState) { + gaugeManager.updateGaugeCollection(applicationProcessState); } @VisibleForTesting - public Future getSyncInitFuture() { - return this.syncInitFuture; + public void setPerfSession(PerfSession perfSession) { + this.perfSession = perfSession; } } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/CpuGaugeCollector.java b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/CpuGaugeCollector.java index e33d363c0aa..ceb636d56b3 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/CpuGaugeCollector.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/CpuGaugeCollector.java @@ -17,8 +17,6 @@ import static android.system.Os.sysconf; import android.annotation.SuppressLint; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; import android.system.OsConstants; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -163,7 +161,7 @@ private synchronized void scheduleCpuMetricCollectionWithRate( this.cpuMetricCollectionRateMs = cpuMetricCollectionRate; try { cpuMetricCollectorJob = - cpuMetricCollectorExecutor.scheduleAtFixedRate( + cpuMetricCollectorExecutor.scheduleWithFixedDelay( () -> { CpuMetricReading currCpuReading = syncCollectCpuMetric(referenceTime); if (currCpuReading != null) { @@ -181,7 +179,7 @@ private synchronized void scheduleCpuMetricCollectionWithRate( private synchronized void scheduleCpuMetricCollectionOnce(Timer referenceTime) { try { @SuppressWarnings("FutureReturnValueIgnored") - ScheduledFuture unusedFuture = + ScheduledFuture unusedFuture = cpuMetricCollectorExecutor.schedule( () -> { CpuMetricReading currCpuReading = syncCollectCpuMetric(referenceTime); @@ -227,12 +225,7 @@ private CpuMetricReading syncCollectCpuMetric(Timer referenceTime) { } private long getClockTicksPerSecond() { - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - return sysconf(OsConstants._SC_CLK_TCK); - } else { - // TODO(b/110779408): Figure out how to collect this info for Android API 20 and below. - return INVALID_SC_PER_CPU_CLOCK_TICK; - } + return sysconf(OsConstants._SC_CLK_TCK); } private long convertClockTicksToMicroseconds(long clockTicks) { 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 7f6182a9c15..71b04524ddd 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 @@ -22,6 +22,8 @@ import com.google.firebase.components.Lazy; import com.google.firebase.perf.config.ConfigResolver; import com.google.firebase.perf.logging.AndroidLogger; +import com.google.firebase.perf.logging.DebugEnforcementCheck; +import com.google.firebase.perf.session.FirebaseSessionsHelperKt; import com.google.firebase.perf.session.PerfSession; import com.google.firebase.perf.transport.TransportManager; import com.google.firebase.perf.util.Timer; @@ -59,10 +61,12 @@ public class GaugeManager { private final TransportManager transportManager; @Nullable private GaugeMetadataManager gaugeMetadataManager; - @Nullable private ScheduledFuture gaugeManagerDataCollectionJob = null; - @Nullable private String sessionId = null; - private ApplicationProcessState applicationProcessState = - ApplicationProcessState.APPLICATION_PROCESS_STATE_UNKNOWN; + @Nullable private ScheduledFuture gaugeManagerDataCollectionJob = null; + @Nullable private PerfSession session = null; + + // The default value for application process state is Foreground. This is used to start collecting + // gauge metrics by default. + private ApplicationProcessState applicationProcessState = ApplicationProcessState.FOREGROUND; // TODO(b/258263016): Migrate to go/firebase-android-executors @SuppressLint("ThreadPoolCreation") @@ -72,8 +76,8 @@ private GaugeManager() { TransportManager.getInstance(), ConfigResolver.getInstance(), null, - new Lazy<>(() -> new CpuGaugeCollector()), - new Lazy<>(() -> new MemoryGaugeCollector())); + new Lazy<>(CpuGaugeCollector::new), + new Lazy<>(MemoryGaugeCollector::new)); } @VisibleForTesting @@ -81,7 +85,7 @@ private GaugeManager() { Lazy gaugeManagerExecutor, TransportManager transportManager, ConfigResolver configResolver, - GaugeMetadataManager gaugeMetadataManager, + @Nullable GaugeMetadataManager gaugeMetadataManager, Lazy cpuGaugeCollector, Lazy memoryGaugeCollector) { @@ -103,46 +107,30 @@ public static synchronized GaugeManager getInstance() { return instance; } - /** - * Starts the collection of available gauges for the given {@code sessionId} and {@code - * applicationProcessState}. The collected Gauge Metrics will be flushed at regular intervals. - * - *

GaugeManager can only collect gauges for one session at a time, and if this method is called - * again with the same or new sessionId while it's already collecting gauges, all future gauges - * will then be associated with the same or new sessionId and applicationProcessState. - * - * @param session The {@link PerfSession} to which the collected gauges will be associated with. - * @param applicationProcessState The {@link ApplicationProcessState} the collected GaugeMetrics - * will be associated with. - * @note: This method is NOT thread safe - {@link this.startCollectingGauges()} and {@link - * this.stopCollectingGauges()} should always be called from the same thread. - */ - public void startCollectingGauges( - PerfSession session, ApplicationProcessState applicationProcessState) { - if (this.sessionId != null) { - stopCollectingGauges(); + public void updateGaugeCollection(ApplicationProcessState applicationProcessState) { + if (gaugeManagerDataCollectionJob != null) { + gaugeManagerDataCollectionJob.cancel(false); } - long collectionFrequency = startCollectingGauges(applicationProcessState, session.getTimer()); - if (collectionFrequency == INVALID_GAUGE_COLLECTION_FREQUENCY) { - logger.warn("Invalid gauge collection frequency. Unable to start collecting Gauges."); + if (!isValidSessionForLogging()) { + logger.warn("Not starting gauge collection."); + stopCollectingGauges(); return; } - this.sessionId = session.sessionId(); - this.applicationProcessState = applicationProcessState; - - // This is needed, otherwise the Runnable might use a stale value. - final String sessionIdForScheduledTask = sessionId; - final ApplicationProcessState applicationProcessStateForScheduledTask = applicationProcessState; + long collectionFrequency = + Math.min( + getCpuGaugeCollectionFrequencyMs(applicationProcessState), + getMemoryGaugeCollectionFrequencyMs(applicationProcessState)); + String sessionIdForScheduledTask = session.sessionId(); try { gaugeManagerDataCollectionJob = gaugeManagerExecutor .get() - .scheduleAtFixedRate( + .scheduleWithFixedDelay( () -> { - syncFlush(sessionIdForScheduledTask, applicationProcessStateForScheduledTask); + syncFlush(sessionIdForScheduledTask, applicationProcessState); }, /* initialDelay= */ collectionFrequency * APPROX_NUMBER_OF_DATA_POINTS_PER_GAUGE_METRIC, @@ -152,6 +140,43 @@ public void startCollectingGauges( } catch (RejectedExecutionException e) { logger.warn("Unable to start collecting Gauges: " + e.getMessage()); } + + if (this.applicationProcessState != applicationProcessState) { + this.applicationProcessState = applicationProcessState; + + // If there's a change in App State - update the gauge collection frequency as well. + stopCollectingGauges(); + startCollectingGauges(applicationProcessState, this.session.getTimer()); + } + } + + /** + * Starts the collection of available gauges for the given {@code session}. + * + *

GaugeManager can only collect gauges for one session at a time, and if this method is called + * again with the same or new sessionId while it's already collecting gauges, all future gauges + * will then be associated with the same or new sessionId and applicationProcessState. + * + * @param session The {@link PerfSession} to which the collected gauges will be associated with. + * @note: This method is NOT thread safe - {@link this.startCollectingGauges()} and {@link + * this.stopCollectingGauges()} should always be called from the same thread. + */ + public void startCollectingGaugeMetrics(PerfSession session) { + if (this.session != null) { + stopCollectingGauges(); + } + + // Updates the session associated w/ Gauge Manager when starting collecting gauges. + this.session = session; + + if (!session.isGaugeAndEventCollectionEnabled()) { + return; + } + + long collectionFrequency = startCollectingGauges(applicationProcessState, session.getTimer()); + if (collectionFrequency == INVALID_GAUGE_COLLECTION_FREQUENCY) { + logger.warn("Invalid gauge collection frequency. Unable to start collecting Gauges."); + } } /** @@ -189,12 +214,12 @@ private long startCollectingGauges(ApplicationProcessState appState, Timer refer * this.stopCollectingGauges()} should always be called from the same thread. */ public void stopCollectingGauges() { - if (this.sessionId == null) { + if (this.session == null) { return; } // This is needed, otherwise the Runnable might use a stale value. - final String sessionIdForScheduledTask = sessionId; + final ApplicationProcessState applicationProcessStateForScheduledTask = applicationProcessState; cpuGaugeCollector.get().stopCollecting(); @@ -204,9 +229,12 @@ public void stopCollectingGauges() { gaugeManagerDataCollectionJob.cancel(false); } + final String sessionIdForScheduledTask = session.sessionId(); + this.session = null; + // Flush any data that was collected for this session one last time. @SuppressWarnings("FutureReturnValueIgnored") - ScheduledFuture unusedFuture = + ScheduledFuture unusedFuture = gaugeManagerExecutor .get() .schedule( @@ -215,9 +243,40 @@ public void stopCollectingGauges() { }, TIME_TO_WAIT_BEFORE_FLUSHING_GAUGES_QUEUE_MS, TimeUnit.MILLISECONDS); + } - this.sessionId = null; - this.applicationProcessState = ApplicationProcessState.APPLICATION_PROCESS_STATE_UNKNOWN; + /** + * Stops the collection of gauges if it was currently collecting them for a legacy session. + * + * @note: This method is NOT thread safe - {@link this.startCollectingGauges()} and {@link + * this.stopCollectingGauges()} should always be called from the same thread. + */ + public void stopCollectingGaugesForLegacySession( + String updatedSessionId, ApplicationProcessState flushToApplicationProcessState) { + final ApplicationProcessState applicationProcessStateForScheduledTask = + flushToApplicationProcessState; + + cpuGaugeCollector.get().stopCollecting(); + memoryGaugeCollector.get().stopCollecting(); + + if (gaugeManagerDataCollectionJob != null) { + gaugeManagerDataCollectionJob.cancel(false); + } + + final String sessionIdForScheduledTask = updatedSessionId; + this.session = null; + + // Flush any data that was collected for this session one last time. + @SuppressWarnings("FutureReturnValueIgnored") + ScheduledFuture unusedFuture = + gaugeManagerExecutor + .get() + .schedule( + () -> { + syncFlush(sessionIdForScheduledTask, applicationProcessStateForScheduledTask); + }, + TIME_TO_WAIT_BEFORE_FLUSHING_GAUGES_QUEUE_MS, + TimeUnit.MILLISECONDS); } /** @@ -228,6 +287,7 @@ public void stopCollectingGauges() { * @param appState The app state for which these gauges are collected. */ private void syncFlush(String sessionId, ApplicationProcessState appState) { + DebugEnforcementCheck.Companion.checkSession(sessionId, "syncFlush"); GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder(); // Adding CPU metric readings. @@ -243,14 +303,13 @@ private void syncFlush(String sessionId, ApplicationProcessState appState) { // Adding Session ID info. gaugeMetricBuilder.setSessionId(sessionId); - transportManager.log(gaugeMetricBuilder.build(), appState); } /** * Log the Gauge Metadata information to the transport. * - * @param sessionId The {@link PerfSession#sessionId()} to which the collected Gauge Metrics + * @param sessionId The {@link PerfSession#sessionId()} ()} to which the collected Gauge Metrics * should be associated with. * @param appState The {@link ApplicationProcessState} for which these gauges are collected. * @return true if GaugeMetadata was logged, false otherwise. @@ -402,4 +461,15 @@ private long getMemoryGaugeCollectionFrequencyMs( return memoryGaugeCollectionFrequency; } } + + private boolean isValidSessionForLogging() { + if (session == null) return false; + return session.isGaugeAndEventCollectionEnabled() + && !FirebaseSessionsHelperKt.isLegacy(session); + } + + @VisibleForTesting + void setApplicationProcessState(ApplicationProcessState applicationProcessState) { + this.applicationProcessState = applicationProcessState; + } } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeMetadataManager.java b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeMetadataManager.java index 6b4466dfc35..ed38dd8f38d 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeMetadataManager.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeMetadataManager.java @@ -17,18 +17,11 @@ import android.app.ActivityManager; import android.app.ActivityManager.MemoryInfo; import android.content.Context; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; import androidx.annotation.VisibleForTesting; import com.google.firebase.perf.logging.AndroidLogger; import com.google.firebase.perf.util.StorageUnit; import com.google.firebase.perf.util.Utils; import com.google.firebase.perf.v1.GaugeMetadata; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * The {@code GaugeMetadataManager} class is responsible for collecting {@link GaugeMetadata} @@ -41,7 +34,6 @@ class GaugeMetadataManager { private final Runtime runtime; private final ActivityManager activityManager; private final MemoryInfo memoryInfo; - private final Context appContext; GaugeMetadataManager(Context appContext) { this(Runtime.getRuntime(), appContext); @@ -50,7 +42,6 @@ class GaugeMetadataManager { @VisibleForTesting GaugeMetadataManager(Runtime runtime, Context appContext) { this.runtime = runtime; - this.appContext = appContext; this.activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE); memoryInfo = new ActivityManager.MemoryInfo(); activityManager.getMemoryInfo(memoryInfo); @@ -75,29 +66,6 @@ public int getMaxEncouragedAppJavaHeapMemoryKb() { /** Returns the total memory (in kilobytes) accessible by the kernel (called the RAM size). */ public int getDeviceRamSizeKb() { - if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { - return Utils.saturatedIntCast(StorageUnit.BYTES.toKilobytes(memoryInfo.totalMem)); - } - - return readTotalRAM(/* procFileName= */ "/proc/meminfo"); - } - - /** Returns the total ram size of the device (in kilobytes) by reading the "proc/meminfo" file. */ - @VisibleForTesting - int readTotalRAM(String procFileName) { - try (BufferedReader br = new BufferedReader(new FileReader(procFileName))) { - for (String s = br.readLine(); s != null; s = br.readLine()) { - if (s.startsWith("MemTotal")) { - Matcher m = Pattern.compile("\\d+").matcher(s); - return m.find() ? Integer.parseInt(m.group()) : 0; - } - } - } catch (IOException ioe) { - logger.warn("Unable to read '" + procFileName + "' file: " + ioe.getMessage()); - } catch (NumberFormatException nfe) { - logger.warn("Unable to parse '" + procFileName + "' file: " + nfe.getMessage()); - } - - return 0; + return Utils.saturatedIntCast(StorageUnit.BYTES.toKilobytes(memoryInfo.totalMem)); } } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/MemoryGaugeCollector.java b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/MemoryGaugeCollector.java index eeaf4eb7c80..a7b4b40002a 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/MemoryGaugeCollector.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/MemoryGaugeCollector.java @@ -50,7 +50,7 @@ public class MemoryGaugeCollector { public final ConcurrentLinkedQueue memoryMetricReadings; private final Runtime runtime; - @Nullable private ScheduledFuture memoryMetricCollectorJob = null; + @Nullable private ScheduledFuture memoryMetricCollectorJob = null; private long memoryMetricCollectionRateMs = UNSET_MEMORY_METRIC_COLLECTION_RATE; // TODO(b/258263016): Migrate to go/firebase-android-executors @@ -124,7 +124,7 @@ private synchronized void scheduleMemoryMetricCollectionWithRate( try { memoryMetricCollectorJob = - memoryMetricCollectorExecutor.scheduleAtFixedRate( + memoryMetricCollectorExecutor.scheduleWithFixedDelay( () -> { AndroidMemoryReading memoryReading = syncCollectMemoryMetric(referenceTime); if (memoryReading != null) { @@ -142,7 +142,7 @@ private synchronized void scheduleMemoryMetricCollectionWithRate( private synchronized void scheduleMemoryMetricCollectionOnce(Timer referenceTime) { try { @SuppressWarnings("FutureReturnValueIgnored") - ScheduledFuture unusedFuture = + ScheduledFuture unusedFuture = memoryMetricCollectorExecutor.schedule( () -> { AndroidMemoryReading memoryReading = syncCollectMemoryMetric(referenceTime); diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/transport/TransportManager.java b/firebase-perf/src/main/java/com/google/firebase/perf/transport/TransportManager.java index 9600b099a6d..159af53d3d3 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/transport/TransportManager.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/transport/TransportManager.java @@ -354,6 +354,7 @@ public void log(final GaugeMetric gaugeMetric) { * {@link #isAllowedToDispatch(PerfMetric)}). */ public void log(final GaugeMetric gaugeMetric, final ApplicationProcessState appState) { + // TODO(b/394127311): This *might* potentially be the right place to get AQS. executorService.execute( () -> syncLog(PerfMetric.newBuilder().setGaugeMetric(gaugeMetric), appState)); } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/util/Constants.java b/firebase-perf/src/main/java/com/google/firebase/perf/util/Constants.java index f2704b903ce..a8ef753f0bf 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/util/Constants.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/util/Constants.java @@ -22,6 +22,10 @@ public class Constants { public static final String PREFS_NAME = "FirebasePerfSharedPrefs"; public static final String ENABLE_DISABLE = "isEnabled"; + // Contains non-hex characters and so guarantees it isn't an AQS. + // https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.uuid/-uuid/ + public static final String UNDEFINED_AQS_ID_PREFIX = "noaqsid"; + public static final double MIN_SAMPLING_RATE = 0.0; public static final double MAX_SAMPLING_RATE = 1.0; 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..46813471478 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 @@ -26,12 +26,13 @@ import com.google.firebase.perf.session.PerfSession; import com.google.firebase.perf.session.SessionManager; import com.google.firebase.perf.util.ImmutableBundle; +import com.google.firebase.sessions.api.SessionSubscriber; +import java.util.UUID; import org.junit.After; import org.junit.Before; import org.robolectric.shadows.ShadowPackageManager; public class FirebasePerformanceTestBase { - /** * The following values are needed by Firebase to identify the project and application that all * data stored in Firebase databases gets associated with. This is important to determine data @@ -72,6 +73,7 @@ public void setUpFirebaseApp() { .setProjectId(FAKE_FIREBASE_PROJECT_ID) .build(); FirebaseApp.initializeApp(appContext, options); + forceAppQualitySession(); } @After @@ -93,11 +95,26 @@ protected static void forceNonVerboseSession() { forceVerboseSessionWithSamplingPercentage(0); } + protected static void forceAppQualitySession() { + PerfSession existingPerfSession = createPerfSession(false); + SessionManager.getInstance().setPerfSession(existingPerfSession); + SessionSubscriber sessionSubscriber = FirebasePerformance.getInstance().getSessionSubscriber(); + sessionSubscriber.onSessionChanged(new SessionSubscriber.SessionDetails("fakeAQS")); + } + private static void forceVerboseSessionWithSamplingPercentage(long samplingPercentage) { Bundle bundle = new Bundle(); bundle.putFloat("sessions_sampling_percentage", samplingPercentage); ConfigResolver.getInstance().setMetadataBundle(new ImmutableBundle(bundle)); + forceAppQualitySession(); + } + + private static PerfSession createPerfSession(boolean isLegacy) { + if (isLegacy) { + return PerfSession.createWithId(null); + } - SessionManager.getInstance().setPerfSession(PerfSession.createWithId("sessionId")); + String aqsId = UUID.randomUUID().toString().replace("-", ""); + return PerfSession.createWithId(aqsId); } } diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/PerformanceTests.kt b/firebase-perf/src/test/java/com/google/firebase/perf/PerformanceTests.kt index e8844e0cc43..a90ddbc108c 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/PerformanceTests.kt +++ b/firebase-perf/src/test/java/com/google/firebase/perf/PerformanceTests.kt @@ -37,6 +37,7 @@ import com.google.firebase.perf.v1.ApplicationProcessState import com.google.firebase.perf.v1.NetworkRequestMetric import com.google.firebase.perf.v1.TraceMetric import com.google.firebase.platforminfo.UserAgentPublisher +import com.google.firebase.sessions.api.SessionSubscriber import org.junit.After import org.junit.Before import org.junit.Test @@ -84,6 +85,9 @@ abstract class BaseTestCase { .build(), EXISTING_APP ) + Firebase.performance.sessionSubscriber.onSessionChanged( + SessionSubscriber.SessionDetails("fakeAQSSession") + ) } @After diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/application/AppStateMonitorTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/application/AppStateMonitorTest.java index 0b7d4bbfc17..f30ee5d73a0 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/application/AppStateMonitorTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/application/AppStateMonitorTest.java @@ -39,6 +39,8 @@ import com.google.firebase.perf.config.DeviceCacheManager; import com.google.firebase.perf.metrics.NetworkRequestMetricBuilder; import com.google.firebase.perf.metrics.Trace; +import com.google.firebase.perf.session.PerfSession; +import com.google.firebase.perf.session.SessionManager; import com.google.firebase.perf.session.gauges.GaugeManager; import com.google.firebase.perf.transport.TransportManager; import com.google.firebase.perf.util.Clock; @@ -80,6 +82,7 @@ public class AppStateMonitorTest extends FirebasePerformanceTestBase { @Before public void setUp() { currentTime = 0; + SessionManager.getInstance().updatePerfSession(PerfSession.createWithId("sessionId")); initMocks(this); doAnswer((Answer) invocationOnMock -> new Timer(currentTime)).when(clock).getTime(); diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/ktx/PerformanceTests.kt b/firebase-perf/src/test/java/com/google/firebase/perf/ktx/PerformanceTests.kt index f5d02ea6b35..8eb63acc2e4 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/ktx/PerformanceTests.kt +++ b/firebase-perf/src/test/java/com/google/firebase/perf/ktx/PerformanceTests.kt @@ -31,6 +31,7 @@ import com.google.firebase.perf.metrics.HttpMetric import com.google.firebase.perf.metrics.Trace import com.google.firebase.perf.metrics.getTraceCounter import com.google.firebase.perf.metrics.getTraceCounterCount +import com.google.firebase.perf.performance import com.google.firebase.perf.transport.TransportManager import com.google.firebase.perf.util.Clock import com.google.firebase.perf.util.Timer diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/metrics/TraceTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/metrics/TraceTest.java index 0be443031f2..27fb9fde775 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/metrics/TraceTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/metrics/TraceTest.java @@ -32,7 +32,6 @@ import com.google.firebase.perf.application.AppStateMonitor; import com.google.firebase.perf.config.ConfigResolver; import com.google.firebase.perf.config.DeviceCacheManager; -import com.google.firebase.perf.session.PerfSession; import com.google.firebase.perf.session.SessionManager; import com.google.firebase.perf.session.gauges.GaugeManager; import com.google.firebase.perf.transport.TransportManager; @@ -1016,8 +1015,7 @@ public void testSessionIdAdditionInTrace() { int numberOfSessionIds = trace.getSessions().size(); - PerfSession perfSession = PerfSession.createWithId("test_session_id"); - SessionManager.getInstance().updatePerfSession(perfSession); + forceAppQualitySession(); assertThat(trace.getSessions()).hasSize(numberOfSessionIds + 1); trace.stop(); @@ -1071,7 +1069,7 @@ public void testUpdateSessionWithValidSessionIsAdded() { trace.start(); assertThat(trace.getSessions()).hasSize(1); - trace.updateSession(PerfSession.createWithId("test_session_id")); + forceAppQualitySession(); assertThat(trace.getSessions()).hasSize(2); trace.stop(); diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/session/PerfSessionTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/session/PerfSessionTest.java index 43257987b0f..9e4e3149e62 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/session/PerfSessionTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/session/PerfSessionTest.java @@ -62,12 +62,24 @@ public void setUp() { @Test public void instanceCreation() { - PerfSession session = new PerfSession("sessionId", mockClock); + PerfSession session = PerfSession.createWithId("sessionId"); assertThat(session).isNotNull(); session.setGaugeAndEventCollectionEnabled(true); - Assert.assertTrue(session.isGaugeAndEventCollectionEnabled()); + assertThat(session.isVerbose()).isTrue(); session.setGaugeAndEventCollectionEnabled(false); - Assert.assertFalse(session.isGaugeAndEventCollectionEnabled()); + assertThat(session.isVerbose()).isFalse(); + assertThat(FirebaseSessionsHelperKt.isLegacy(session)).isFalse(); + } + + @Test + public void legacyInstanceCreation() { + PerfSession perfSession = PerfSession.createWithId(null); + assertThat(perfSession).isNotNull(); + perfSession.setGaugeAndEventCollectionEnabled(true); + assertThat(perfSession.isVerbose()).isTrue(); + perfSession.setGaugeAndEventCollectionEnabled(false); + assertThat(perfSession.isVerbose()).isFalse(); + assertThat(FirebaseSessionsHelperKt.isLegacy(perfSession)).isTrue(); } @Test @@ -76,19 +88,20 @@ public void shouldCollectGaugesAndEvents_perfMonDisabledAtRuntime_sessionNotVerb Bundle bundle = new Bundle(); bundle.putFloat("sessions_sampling_percentage", 100); configResolver.setMetadataBundle(new ImmutableBundle(bundle)); + PerfSession testSession = PerfSession.createWithId("aqsSessionId"); // By default, session is verbose if developer has set 100% of session verbosity. - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isTrue(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isTrue(); // Case #1: developer has disabled Performance Monitoring during runtime. configResolver.setIsPerformanceCollectionEnabled(false); - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isFalse(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isFalse(); // Case #2: developer has enabled Performance Monitoring during runtime. configResolver.setIsPerformanceCollectionEnabled(true); - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isTrue(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isTrue(); } @Test @@ -99,20 +112,21 @@ public void shouldCollectGaugesAndEvents_perfMonDisabledAtBuildtime_verbosityDep bundle.putFloat("sessions_sampling_percentage", 100); bundle.putBoolean("firebase_performance_collection_enabled", false); configResolver.setMetadataBundle(new ImmutableBundle(bundle)); + PerfSession testSession = PerfSession.createWithId("aqsSessionId"); // By default, session is not verbose if developer disabled performance monitoring at build // time. - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isFalse(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isFalse(); // Case #1: developer has enabled Performance Monitoring during runtime. configResolver.setIsPerformanceCollectionEnabled(true); - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isTrue(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isTrue(); // Case #2: developer has disabled Performance Monitoring during runtime. configResolver.setIsPerformanceCollectionEnabled(false); - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isFalse(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isFalse(); } @Test @@ -122,24 +136,25 @@ public void shouldCollectGaugesAndEvents_perfMonDeactivated_sessionNotVerbose() bundle.putFloat("sessions_sampling_percentage", 100); bundle.putBoolean("firebase_performance_collection_deactivated", true); configResolver.setMetadataBundle(new ImmutableBundle(bundle)); + PerfSession testSession = PerfSession.createWithId("aqsSessionId"); // Session will never be verbose if developer deactivated performance monitoring at build time. - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isFalse(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isFalse(); // Case #1: developer has enabled Performance Monitoring during runtime. configResolver.setIsPerformanceCollectionEnabled(true); - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isFalse(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isFalse(); // Case #2: developer has disabled Performance Monitoring during runtime. configResolver.setIsPerformanceCollectionEnabled(false); - assertThat(PerfSession.shouldCollectGaugesAndEvents()).isFalse(); + assertThat(testSession.shouldCollectGaugesAndEvents()).isFalse(); } @Test public void testPerfSessionConversion() { - PerfSession session1 = new PerfSession("sessionId", mockClock); + PerfSession session1 = PerfSession.createWithId("aqsSessionId"); session1.setGaugeAndEventCollectionEnabled(true); com.google.firebase.perf.v1.PerfSession perfSession = session1.build(); @@ -150,7 +165,7 @@ public void testPerfSessionConversion() { @Test public void testPerfSessionConversionWithoutVerbosity() { - PerfSession session1 = new PerfSession("sessionId", mockClock); + PerfSession session1 = PerfSession.createWithId("sessionId"); com.google.firebase.perf.v1.PerfSession perfSession = session1.build(); Assert.assertEquals(session1.sessionId(), perfSession.getSessionId()); diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/session/SessionManagerTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/session/SessionManagerTest.java index f3e3795f3f8..be819a32f26 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/session/SessionManagerTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/session/SessionManagerTest.java @@ -16,9 +16,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -40,7 +37,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.AdditionalMatchers; import org.mockito.ArgumentMatchers; import org.mockito.InOrder; import org.mockito.Mock; @@ -70,11 +66,10 @@ public void setUp() { public void testInstanceCreation() { assertThat(SessionManager.getInstance()).isNotNull(); assertThat(SessionManager.getInstance()).isEqualTo(SessionManager.getInstance()); - assertThat(SessionManager.getInstance().perfSession().sessionId()).isNotNull(); } @Test - public void setApplicationContext_logGaugeMetadata_afterGaugeMetadataManagerIsInitialized() + public void setApplicationContext_initializeGaugeMetadataManager() throws ExecutionException, InterruptedException { when(mockPerfSession.isGaugeAndEventCollectionEnabled()).thenReturn(true); InOrder inOrder = Mockito.inOrder(mockGaugeManager); @@ -82,127 +77,7 @@ public void setApplicationContext_logGaugeMetadata_afterGaugeMetadataManagerIsIn new SessionManager(mockGaugeManager, mockPerfSession, mockAppStateMonitor); testSessionManager.setApplicationContext(mockApplicationContext); - testSessionManager.getSyncInitFuture().get(); inOrder.verify(mockGaugeManager).initializeGaugeMetadataManager(any()); - inOrder.verify(mockGaugeManager).logGaugeMetadata(any(), any()); - } - - @Test - public void testOnUpdateAppStateDoesNothingDuringAppStart() { - String oldSessionId = SessionManager.getInstance().perfSession().sessionId(); - - assertThat(oldSessionId).isNotNull(); - assertThat(oldSessionId).isEqualTo(SessionManager.getInstance().perfSession().sessionId()); - - AppStateMonitor.getInstance().setIsColdStart(true); - - SessionManager.getInstance().onUpdateAppState(ApplicationProcessState.FOREGROUND); - assertThat(oldSessionId).isEqualTo(SessionManager.getInstance().perfSession().sessionId()); - } - - @Test - public void testOnUpdateAppStateGeneratesNewSessionIdOnForegroundState() { - String oldSessionId = SessionManager.getInstance().perfSession().sessionId(); - - assertThat(oldSessionId).isNotNull(); - assertThat(oldSessionId).isEqualTo(SessionManager.getInstance().perfSession().sessionId()); - - SessionManager.getInstance().onUpdateAppState(ApplicationProcessState.FOREGROUND); - assertThat(oldSessionId).isNotEqualTo(SessionManager.getInstance().perfSession().sessionId()); - } - - @Test - public void testOnUpdateAppStateDoesntGenerateNewSessionIdOnBackgroundState() { - String oldSessionId = SessionManager.getInstance().perfSession().sessionId(); - - assertThat(oldSessionId).isNotNull(); - assertThat(oldSessionId).isEqualTo(SessionManager.getInstance().perfSession().sessionId()); - - SessionManager.getInstance().onUpdateAppState(ApplicationProcessState.BACKGROUND); - assertThat(oldSessionId).isEqualTo(SessionManager.getInstance().perfSession().sessionId()); - } - - @Test - public void testOnUpdateAppStateGeneratesNewSessionIdOnBackgroundStateIfPerfSessionExpires() { - when(mockPerfSession.isSessionRunningTooLong()).thenReturn(true); - SessionManager testSessionManager = - new SessionManager(mockGaugeManager, mockPerfSession, mockAppStateMonitor); - String oldSessionId = testSessionManager.perfSession().sessionId(); - - assertThat(oldSessionId).isNotNull(); - assertThat(oldSessionId).isEqualTo(testSessionManager.perfSession().sessionId()); - - testSessionManager.onUpdateAppState(ApplicationProcessState.BACKGROUND); - assertThat(oldSessionId).isNotEqualTo(testSessionManager.perfSession().sessionId()); - } - - @Test - public void - testOnUpdateAppStateMakesGaugeManagerLogGaugeMetadataOnForegroundStateIfSessionIsVerbose() { - forceVerboseSession(); - - SessionManager testSessionManager = - new SessionManager(mockGaugeManager, mockPerfSession, mockAppStateMonitor); - testSessionManager.onUpdateAppState(ApplicationProcessState.FOREGROUND); - - verify(mockGaugeManager) - .logGaugeMetadata( - anyString(), nullable(com.google.firebase.perf.v1.ApplicationProcessState.class)); - } - - @Test - public void - testOnUpdateAppStateDoesntMakeGaugeManagerLogGaugeMetadataOnForegroundStateIfSessionIsNonVerbose() { - forceNonVerboseSession(); - - SessionManager testSessionManager = - new SessionManager(mockGaugeManager, mockPerfSession, mockAppStateMonitor); - testSessionManager.onUpdateAppState(ApplicationProcessState.FOREGROUND); - - verify(mockGaugeManager, never()) - .logGaugeMetadata( - anyString(), nullable(com.google.firebase.perf.v1.ApplicationProcessState.class)); - } - - @Test - public void - testOnUpdateAppStateDoesntMakeGaugeManagerLogGaugeMetadataOnBackgroundStateEvenIfSessionIsVerbose() { - forceVerboseSession(); - - SessionManager testSessionManager = - new SessionManager(mockGaugeManager, mockPerfSession, mockAppStateMonitor); - testSessionManager.onUpdateAppState(ApplicationProcessState.BACKGROUND); - - verify(mockGaugeManager, never()) - .logGaugeMetadata( - anyString(), nullable(com.google.firebase.perf.v1.ApplicationProcessState.class)); - } - - @Test - public void - testOnUpdateAppStateMakesGaugeManagerLogGaugeMetadataOnBackgroundAppStateIfSessionIsVerboseAndTimedOut() { - when(mockPerfSession.isSessionRunningTooLong()).thenReturn(true); - forceVerboseSession(); - - SessionManager testSessionManager = - new SessionManager(mockGaugeManager, mockPerfSession, mockAppStateMonitor); - testSessionManager.onUpdateAppState(ApplicationProcessState.BACKGROUND); - - verify(mockGaugeManager) - .logGaugeMetadata( - anyString(), nullable(com.google.firebase.perf.v1.ApplicationProcessState.class)); - } - - @Test - public void testOnUpdateAppStateMakesGaugeManagerStartCollectingGaugesIfSessionIsVerbose() { - forceVerboseSession(); - - SessionManager testSessionManager = - new SessionManager(mockGaugeManager, mockPerfSession, mockAppStateMonitor); - testSessionManager.onUpdateAppState(ApplicationProcessState.FOREGROUND); - - verify(mockGaugeManager) - .startCollectingGauges(AdditionalMatchers.not(eq(mockPerfSession)), any()); } // LogGaugeData on new perf session when Verbose @@ -210,7 +85,7 @@ public void testOnUpdateAppStateMakesGaugeManagerStartCollectingGaugesIfSessionI // Mark Session as expired after time limit. @Test - public void testOnUpdateAppStateMakesGaugeManagerStopCollectingGaugesIfSessionIsNonVerbose() { + public void testUpdatePerfSessionMakesGaugeManagerStopCollectingGaugesIfSessionIsNonVerbose() { forceNonVerboseSession(); SessionManager testSessionManager = @@ -221,7 +96,7 @@ public void testOnUpdateAppStateMakesGaugeManagerStopCollectingGaugesIfSessionIs } @Test - public void testOnUpdateAppStateMakesGaugeManagerStopCollectingGaugesWhenSessionsDisabled() { + public void testUpdatePerfSessionMakesGaugeManagerStopCollectingGaugesWhenSessionsDisabled() { forceSessionsFeatureDisabled(); SessionManager testSessionManager = @@ -232,32 +107,6 @@ public void testOnUpdateAppStateMakesGaugeManagerStopCollectingGaugesWhenSession verify(mockGaugeManager).stopCollectingGauges(); } - @Test - public void testGaugeMetadataIsFlushedOnlyWhenNewVerboseSessionIsCreated() { - when(mockPerfSession.isSessionRunningTooLong()).thenReturn(false); - - // Start with a non verbose session - forceNonVerboseSession(); - SessionManager testSessionManager = - new SessionManager( - mockGaugeManager, PerfSession.createWithId("testSessionId1"), mockAppStateMonitor); - - verify(mockGaugeManager, times(0)) - .logGaugeMetadata( - eq("testSessionId1"), - eq(com.google.firebase.perf.v1.ApplicationProcessState.FOREGROUND)); - - // Forcing a verbose session will enable Gauge collection - forceVerboseSession(); - testSessionManager.updatePerfSession(PerfSession.createWithId("testSessionId2")); - verify(mockGaugeManager, times(1)).logGaugeMetadata(eq("testSessionId2"), any()); - - // Force a non-verbose session and verify if we are not logging metadata - forceVerboseSession(); - testSessionManager.updatePerfSession(PerfSession.createWithId("testSessionId3")); - verify(mockGaugeManager, times(1)).logGaugeMetadata(eq("testSessionId3"), any()); - } - @Test public void testSessionIdDoesNotUpdateIfPerfSessionRunsTooLong() { Timer mockTimer = mock(Timer.class); @@ -277,22 +126,24 @@ public void testSessionIdDoesNotUpdateIfPerfSessionRunsTooLong() { } @Test - public void testPerfSessionExpiredMakesGaugeManagerStopsCollectingGaugesIfSessionIsVerbose() { - forceVerboseSession(); + public void testUpdatePerfSessionStartsCollectingGaugesIfSessionIsVerbose() { Timer mockTimer = mock(Timer.class); when(mockClock.getTime()).thenReturn(mockTimer); + when(mockAppStateMonitor.getAppState()).thenReturn(ApplicationProcessState.FOREGROUND); - PerfSession session = new PerfSession("sessionId", mockClock); - SessionManager testSessionManager = - new SessionManager(mockGaugeManager, session, mockAppStateMonitor); + PerfSession previousSession = new PerfSession("previousSession", mockClock); + previousSession.setGaugeAndEventCollectionEnabled(false); - assertThat(session.isSessionRunningTooLong()).isFalse(); + PerfSession newSession = new PerfSession("newSession", mockClock); + newSession.setGaugeAndEventCollectionEnabled(true); - when(mockTimer.getDurationMicros()) - .thenReturn(TimeUnit.HOURS.toMicros(5)); // Default Max Session Length is 4 hours + SessionManager testSessionManager = + new SessionManager(mockGaugeManager, previousSession, mockAppStateMonitor); + testSessionManager.updatePerfSession(newSession); + testSessionManager.setApplicationContext(mockApplicationContext); - assertThat(session.isSessionRunningTooLong()).isTrue(); - verify(mockGaugeManager, times(0)).logGaugeMetadata(any(), any()); + verify(mockGaugeManager, times(1)).initializeGaugeMetadataManager(mockApplicationContext); + verify(mockGaugeManager, times(1)).startCollectingGaugeMetrics(newSession); } @Test 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 5090d66c8b9..f5731d9182c 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 @@ -15,6 +15,8 @@ package com.google.firebase.perf.session.gauges; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -32,7 +34,6 @@ import com.google.firebase.perf.config.ConfigResolver; import com.google.firebase.perf.session.PerfSession; import com.google.firebase.perf.transport.TransportManager; -import com.google.firebase.perf.util.Clock; import com.google.firebase.perf.util.Timer; import com.google.firebase.perf.v1.AndroidMemoryReading; import com.google.firebase.perf.v1.ApplicationProcessState; @@ -40,6 +41,7 @@ import com.google.firebase.perf.v1.GaugeMetadata; import com.google.firebase.perf.v1.GaugeMetric; import com.google.testing.timing.FakeScheduledExecutorService; +import java.util.UUID; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; @@ -83,12 +85,12 @@ public void setUp() { doNothing() .when(fakeCpuGaugeCollector) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); doNothing().when(fakeCpuGaugeCollector).stopCollecting(); doNothing() .when(fakeMemoryGaugeCollector) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); doNothing().when(fakeMemoryGaugeCollector).stopCollecting(); doNothing().when(fakeCpuGaugeCollector).collectOnce(ArgumentMatchers.nullable(Timer.class)); @@ -123,23 +125,9 @@ public void setUp() { } @Test - public void testStartCollectingGaugesStartsCollectingMetricsInBackgroundState() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession, ApplicationProcessState.BACKGROUND); - 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)); - } - - @Test - public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession, ApplicationProcessState.FOREGROUND); + public void testStartCollectingGaugesStartsCollectingMetrics() { + PerfSession fakeSession = createTestPerfSession(); + testGaugeManager.startCollectingGaugeMetrics(fakeSession); verify(fakeCpuGaugeCollector) .startCollecting( eq(DEFAULT_CPU_GAUGE_COLLECTION_FREQUENCY_FG_MS), @@ -151,45 +139,42 @@ public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() } @Test - public void - testStartCollectingGaugesDoesNotStartCollectingMetricsWithUnknownApplicationProcessState() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges( - fakeSession, ApplicationProcessState.APPLICATION_PROCESS_STATE_UNKNOWN); - verify(fakeCpuGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); - verify(fakeMemoryGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + public void testStartCollectingGaugesDoesNotLogMetrics() { + PerfSession fakeSession = createTestPerfSession(); + testGaugeManager.startCollectingGaugeMetrics(fakeSession); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); + assertThatNothingWasLoggedToTransport(); } @Test public void - stopCollectingCPUMetric_invalidCPUCaptureFrequency_OtherMetricsWithValidFrequencyInBackground() { + stopCollectingCPUMetric_invalidCPUCaptureFrequency_OtherMetricsWithValidFrequencyInForeground() { // PASS 1: Test with 0 - doReturn(0L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyBackgroundMs(); - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.BACKGROUND); + doReturn(0L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); + PerfSession fakeSession1 = createTestPerfSession(); + + testGaugeManager.startCollectingGaugeMetrics(fakeSession1); - // Verify that Cpu metric collection is not started verify(fakeCpuGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); // Verify that Memory metric collection is started verify(fakeMemoryGaugeCollector) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); // PASS 2: Test with -ve value - doReturn(-25L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyBackgroundMs(); - PerfSession fakeSession2 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.BACKGROUND); + doReturn(-25L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); + PerfSession fakeSession2 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession2); // Verify that Cpu metric collection is not started verify(fakeCpuGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); // Verify that Memory metric collection is started verify(fakeMemoryGaugeCollector, times(2)) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.any(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.any(Timer.class)); } @Test @@ -197,58 +182,61 @@ public void testStartCollectingGaugesStartsCollectingMetricsInForegroundState() startCollectingGaugesOnBackground_invalidMemoryCaptureMs_onlyDisableMemoryCollection() { // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyBackgroundMs(); - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.BACKGROUND); + PerfSession fakeSession1 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.BACKGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession1); // Verify that Memory metric collection is not started verify(fakeMemoryGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); // Verify that Cpu metric collection is started verify(fakeCpuGaugeCollector) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); // PASS 2: Test with -ve value doReturn(-25L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyBackgroundMs(); - PerfSession fakeSession2 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.BACKGROUND); + PerfSession fakeSession2 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.BACKGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession2); // Verify that Memory metric collection is not started verify(fakeMemoryGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); // Verify that Cpu metric collection is started verify(fakeCpuGaugeCollector, times(2)) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.any(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.any(Timer.class)); } @Test public void stopCollectingCPUMetric_invalidCPUCaptureFrequency_OtherMetricsWithValidFrequency() { // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.FOREGROUND); + PerfSession fakeSession1 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession1); // Verify that Cpu metric collection is not started verify(fakeCpuGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.any(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.any(Timer.class)); // Verify that Memory metric collection is started - verify(fakeMemoryGaugeCollector) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.any(Timer.class)); + verify(fakeMemoryGaugeCollector).startCollecting(anyLong(), ArgumentMatchers.any(Timer.class)); // PASS 2: Test with -ve value doReturn(-25L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); - PerfSession fakeSession2 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.FOREGROUND); + PerfSession fakeSession2 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession2); // Verify that Cpu metric collection is not started verify(fakeCpuGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); // Verify that Memory metric collection is started verify(fakeMemoryGaugeCollector, times(2)) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.any(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.any(Timer.class)); } @Test @@ -256,54 +244,60 @@ public void stopCollectingCPUMetric_invalidCPUCaptureFrequency_OtherMetricsWithV startCollectingGaugesOnForeground_invalidMemoryCaptureMs_onlyDisableMemoryCollection() { // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.FOREGROUND); + PerfSession fakeSession1 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession1); // Verify that Memory metric collection is not started verify(fakeMemoryGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.any(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.any(Timer.class)); // Verify that Cpu metric collection is started - verify(fakeCpuGaugeCollector) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.any(Timer.class)); + verify(fakeCpuGaugeCollector).startCollecting(anyLong(), ArgumentMatchers.any(Timer.class)); // PASS 2: Test with -ve value doReturn(-25L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - PerfSession fakeSession2 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.FOREGROUND); + PerfSession fakeSession2 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession2); // Verify that Memory metric collection is not started verify(fakeMemoryGaugeCollector, never()) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.nullable(Timer.class)); // Verify that Cpu metric collection is started verify(fakeCpuGaugeCollector, times(2)) - .startCollecting(ArgumentMatchers.anyLong(), ArgumentMatchers.any(Timer.class)); + .startCollecting(anyLong(), ArgumentMatchers.any(Timer.class)); } @Test public void testStartCollectingGaugesDoesNotStartAJobToConsumeMetricsWithUnknownAppState() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges( - fakeSession, ApplicationProcessState.APPLICATION_PROCESS_STATE_UNKNOWN); + PerfSession fakeSession = createTestPerfSession(); + testGaugeManager.setApplicationProcessState( + ApplicationProcessState.APPLICATION_PROCESS_STATE_UNKNOWN); + testGaugeManager.startCollectingGaugeMetrics(fakeSession); assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); } @Test - public void stopCollectingCPUMetrics_invalidCPUCaptureFrequency_appInForegrounf() { + public void stopCollectingCPUMetrics_invalidCPUCaptureFrequency_appInForeground() { // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.FOREGROUND); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + PerfSession fakeSession1 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession1); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); // PASS 2: Test with -ve value doReturn(-25L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); - PerfSession fakeSession2 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.FOREGROUND); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + PerfSession fakeSession2 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession2); + + fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); } @Test @@ -311,16 +305,19 @@ public void stopCollectingGauges_invalidMemoryCollectionFrequency_appInForegroun // PASS 1: Test with 0 doReturn(0L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.FOREGROUND); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + PerfSession fakeSession1 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession1); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); // PASS 2: Test with -ve value doReturn(-25L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - PerfSession fakeSession2 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.FOREGROUND); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + PerfSession fakeSession2 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession2); + fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); } @Test @@ -329,16 +326,21 @@ public void stopCollectingGauges_invalidGaugeCollectionFrequency_appInForeground doReturn(0L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); doReturn(0L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.FOREGROUND); + PerfSession fakeSession1 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession1); assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); // PASS 2: Test with -ve value doReturn(-25L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); doReturn(-25L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - PerfSession fakeSession2 = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.FOREGROUND); + PerfSession fakeSession2 = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession2); + // Since the prior session exists, it collects a single metric before stopping. + assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); } @@ -347,24 +349,22 @@ public void startCollectingGauges_validGaugeCollectionFrequency_appInForeground( doReturn(25L).when(mockConfigResolver).getSessionsCpuCaptureFrequencyForegroundMs(); doReturn(15L).when(mockConfigResolver).getSessionsMemoryCaptureFrequencyForegroundMs(); - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession, ApplicationProcessState.FOREGROUND); + PerfSession fakeSession = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); - assertThat(fakeScheduledExecutorService.getDelayToNextTask(TimeUnit.MILLISECONDS)) - .isEqualTo(15L * APPROX_NUMBER_OF_DATA_POINTS_PER_GAUGE_METRIC); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); + verify(fakeCpuGaugeCollector).startCollecting(eq(25L), ArgumentMatchers.any()); + verify(fakeMemoryGaugeCollector).startCollecting(eq(15L), ArgumentMatchers.any()); } @Test - public void testStartCollectingGaugesStartsAJobToConsumeTheGeneratedMetrics() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession, ApplicationProcessState.BACKGROUND); + public void testStartCollectingGaugesDoesNotStartAJobToConsumeTheGeneratedMetrics() { + PerfSession fakeSession = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); - assertThat(fakeScheduledExecutorService.getDelayToNextTask(TimeUnit.MILLISECONDS)) - .isEqualTo( - getMinimumBackgroundCollectionFrequency() - * APPROX_NUMBER_OF_DATA_POINTS_PER_GAUGE_METRIC); + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); CpuMetricReading fakeCpuMetricReading1 = createFakeCpuMetricReading(200, 100); CpuMetricReading fakeCpuMetricReading2 = createFakeCpuMetricReading(300, 200); @@ -379,21 +379,15 @@ public void testStartCollectingGaugesStartsAJobToConsumeTheGeneratedMetrics() { fakeMemoryGaugeCollector.memoryMetricReadings.add(fakeMemoryMetricReading2); fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - - assertThatCpuGaugeMetricWasSentToTransport( - "sessionId", recordedGaugeMetric, fakeCpuMetricReading1, fakeCpuMetricReading2); - - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", recordedGaugeMetric, fakeMemoryMetricReading1, fakeMemoryMetricReading2); + assertThatNothingWasLoggedToTransport(); } @Test public void testStopCollectingGaugesStopsCollectingAllGaugeMetrics() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); + PerfSession fakeSession = createTestPerfSession(); - testGaugeManager.startCollectingGauges(fakeSession, ApplicationProcessState.BACKGROUND); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.BACKGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession); verify(fakeCpuGaugeCollector) .startCollecting(eq(DEFAULT_CPU_GAUGE_COLLECTION_FREQUENCY_BG_MS), ArgumentMatchers.any()); @@ -405,9 +399,12 @@ public void testStopCollectingGaugesStopsCollectingAllGaugeMetrics() { @Test public void testStopCollectingGaugesCreatesOneLastJobToConsumeAnyPendingMetrics() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); - testGaugeManager.startCollectingGauges(fakeSession, ApplicationProcessState.BACKGROUND); - assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); + PerfSession fakeSession = createTestPerfSession(); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.BACKGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession); + + // It's not currently logging to transport. + assertThat(fakeScheduledExecutorService.isEmpty()).isTrue(); testGaugeManager.stopCollectingGauges(); assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); @@ -426,16 +423,17 @@ public void testStopCollectingGaugesCreatesOneLastJobToConsumeAnyPendingMetrics( GaugeMetric recordedGaugeMetric = getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); assertThatCpuGaugeMetricWasSentToTransport( - "sessionId", recordedGaugeMetric, fakeCpuMetricReading); + fakeSession.sessionId(), recordedGaugeMetric, fakeCpuMetricReading); assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", recordedGaugeMetric, fakeMemoryMetricReading); + fakeSession.sessionId(), recordedGaugeMetric, fakeMemoryMetricReading); } @Test - public void testGaugeManagerClearsTheQueueEachRun() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); + public void testGaugeManagerDoesNotClearTheQueueUnlessUpdated() { + PerfSession fakeSession = createTestPerfSession(); - testGaugeManager.startCollectingGauges(fakeSession, ApplicationProcessState.BACKGROUND); + testGaugeManager.setApplicationProcessState(ApplicationProcessState.FOREGROUND); + testGaugeManager.startCollectingGaugeMetrics(fakeSession); fakeCpuGaugeCollector.cpuMetricReadings.add(createFakeCpuMetricReading(200, 100)); fakeCpuGaugeCollector.cpuMetricReadings.add(createFakeCpuMetricReading(300, 400)); @@ -446,193 +444,16 @@ public void testGaugeManagerClearsTheQueueEachRun() { assertThat(fakeMemoryGaugeCollector.memoryMetricReadings).isNotEmpty(); fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - assertThat(fakeCpuGaugeCollector.cpuMetricReadings).isEmpty(); - assertThat(fakeMemoryGaugeCollector.memoryMetricReadings).isEmpty(); - - fakeCpuGaugeCollector.cpuMetricReadings.add(createFakeCpuMetricReading(200, 100)); - fakeMemoryGaugeCollector.memoryMetricReadings.add( - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 1234)); - fakeMemoryGaugeCollector.memoryMetricReadings.add( - createFakeAndroidMetricReading(/* currentUsedAppJavaHeapMemoryKb= */ 2345)); - assertThat(fakeCpuGaugeCollector.cpuMetricReadings).isNotEmpty(); assertThat(fakeMemoryGaugeCollector.memoryMetricReadings).isNotEmpty(); + testGaugeManager.updateGaugeCollection(ApplicationProcessState.FOREGROUND); + fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); assertThat(fakeCpuGaugeCollector.cpuMetricReadings).isEmpty(); assertThat(fakeMemoryGaugeCollector.memoryMetricReadings).isEmpty(); - } - @Test - public void testStartingGaugeManagerWithNewSessionIdButSameAppState() { - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - - // Start collecting Gauges. - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.BACKGROUND); - 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( - "sessionId", recordedGaugeMetric1, fakeCpuMetricReading1); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", 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 fakeSession2 = new PerfSession("sessionId2", new Clock()); - - // Start collecting gauges for new session, but same app state. - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.BACKGROUND); - - // The next sweep conducted by GaugeManager still associates metrics to old sessionId and state. - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric2 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - "sessionId", recordedGaugeMetric2, fakeCpuMetricReading2); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", 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.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - "sessionId2", recordedGaugeMetric3, fakeCpuMetricReading3); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId2", recordedGaugeMetric3, fakeMemoryMetricReading3); - } - - @Test - public void testStartGaugeManagerWithSameSessionIdButDifferentAppState() { - PerfSession fakeSession = new PerfSession("sessionId", new Clock()); - - // Start collecting Gauges. - testGaugeManager.startCollectingGauges(fakeSession, ApplicationProcessState.BACKGROUND); - 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( - "sessionId", recordedGaugeMetric1, fakeCpuMetricReading1); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", 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, ApplicationProcessState.FOREGROUND); - - // The next sweep conducted by GaugeManager still associates metrics to old sessionId and state. - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric2 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - "sessionId", recordedGaugeMetric2, fakeCpuMetricReading2); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", 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( - "sessionId", recordedGaugeMetric3, fakeCpuMetricReading3); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", recordedGaugeMetric3, fakeMemoryMetricReading3); - } - - @Test - public void testStartGaugeManagerWithNewSessionIdAndNewAppState() { - PerfSession fakeSession1 = new PerfSession("sessionId", new Clock()); - - // Start collecting Gauges. - testGaugeManager.startCollectingGauges(fakeSession1, ApplicationProcessState.BACKGROUND); - 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( - "sessionId", recordedGaugeMetric1, fakeCpuMetricReading1); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", 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 fakeSession2 = new PerfSession("sessionId2", new Clock()); - - // Start collecting gauges for new session and new app state - testGaugeManager.startCollectingGauges(fakeSession2, ApplicationProcessState.FOREGROUND); - - // The next sweep conducted by GaugeManager still associates metrics to old sessionId and state. - fakeScheduledExecutorService.simulateSleepExecutingAtMostOneTask(); - GaugeMetric recordedGaugeMetric2 = - getLastRecordedGaugeMetric(ApplicationProcessState.BACKGROUND, 1); - assertThatCpuGaugeMetricWasSentToTransport( - "sessionId", recordedGaugeMetric2, fakeCpuMetricReading2); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId", 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( - "sessionId2", recordedGaugeMetric3, fakeCpuMetricReading3); - assertThatMemoryGaugeMetricWasSentToTransport( - "sessionId2", recordedGaugeMetric3, fakeMemoryMetricReading3); + assertThat(fakeScheduledExecutorService.isEmpty()).isFalse(); } @Test @@ -658,7 +479,7 @@ public void testLogGaugeMetadataSendDataToTransport() { } @Test - public void testLogGaugeMetadataDoesntLogWhenGaugeMetadataManagerNotAvailable() { + public void testLogGaugeMetadataDoesNotLogWhenGaugeMetadataManagerNotAvailable() { testGaugeManager = new GaugeManager( @@ -711,6 +532,13 @@ public void testCollectGaugeMetricOnceCollectAllMetricsWhenCollectionIsEnabled() verify(fakeMemoryGaugeCollector).collectOnce(ArgumentMatchers.any()); } + private PerfSession createTestPerfSession() { + PerfSession testSession = + PerfSession.createWithId(UUID.randomUUID().toString().replace("-", "")); + testSession.setGaugeAndEventCollectionEnabled(true); + return testSession; + } + /** @return The minimum background collection frequency of all the Gauges. */ private long getMinimumBackgroundCollectionFrequency() { return DEFAULT_CPU_GAUGE_COLLECTION_FREQUENCY_BG_MS; @@ -751,6 +579,14 @@ private GaugeMetric getLastRecordedGaugeMetric( return argMetric.getValue(); } + /** + * Asserts that no metric was logged to transport. + */ + private void assertThatNothingWasLoggedToTransport() { + verify(mockTransportManager, never()) + .log((GaugeMetric) any(), any(ApplicationProcessState.class)); + } + private void assertThatCpuGaugeMetricWasSentToTransport( String sessionId, GaugeMetric recordedGaugeMetric, CpuMetricReading... cpuMetricReadings) { assertThat(recordedGaugeMetric.getSessionId()).isEqualTo(sessionId); diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/session/gauges/GaugeMetadataManagerTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/session/gauges/GaugeMetadataManagerTest.java index 292747121dd..1592f77e5ad 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/session/gauges/GaugeMetadataManagerTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/session/gauges/GaugeMetadataManagerTest.java @@ -15,26 +15,20 @@ package com.google.firebase.perf.session.gauges; import static com.google.common.truth.Truth.assertThat; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.mockito.MockitoAnnotations.initMocks; import static org.robolectric.Shadows.shadowOf; import android.app.ActivityManager; import android.content.Context; -import android.os.Environment; import androidx.test.core.app.ApplicationProvider; import com.google.firebase.perf.FirebasePerformanceTestBase; import com.google.firebase.perf.util.StorageUnit; -import java.io.File; import java.io.IOException; -import java.io.Writer; -import java.nio.file.Files; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.robolectric.RobolectricTestRunner; -import org.robolectric.shadows.ShadowEnvironment; /** Unit tests for {@link com.google.firebase.perf.session.gauges.GaugeMetadataManager} */ @RunWith(RobolectricTestRunner.class) @@ -49,12 +43,11 @@ public class GaugeMetadataManagerTest extends FirebasePerformanceTestBase { @Mock private Runtime runtime; private ActivityManager activityManager; - private Context appContext; @Before public void setUp() { initMocks(this); - appContext = ApplicationProvider.getApplicationContext(); + Context appContext = ApplicationProvider.getApplicationContext(); activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE); mockMemory(); @@ -90,62 +83,5 @@ public void testGetDeviceRamSize_returnsExpectedValue() throws IOException { int ramSize = testGaugeMetadataManager.getDeviceRamSizeKb(); assertThat(ramSize).isEqualTo(StorageUnit.BYTES.toKilobytes(DEVICE_RAM_SIZE_BYTES)); - assertThat(ramSize).isEqualTo(testGaugeMetadataManager.readTotalRAM(createFakeMemInfoFile())); } - - /** @return The file path of this fake file which can be used to read the file. */ - private String createFakeMemInfoFile() throws IOException { - // Due to file permission issues on forge, it's easiest to just write this file to the emulated - // robolectric external storage. - ShadowEnvironment.setExternalStorageState(Environment.MEDIA_MOUNTED); - - File file = new File(Environment.getExternalStorageDirectory(), "FakeProcMemInfoFile"); - Writer fileWriter; - - fileWriter = Files.newBufferedWriter(file.toPath(), UTF_8); - fileWriter.write(MEM_INFO_CONTENTS); - fileWriter.close(); - - return file.getAbsolutePath(); - } - - private static final String MEM_INFO_CONTENTS = - "MemTotal: " - + DEVICE_RAM_SIZE_KB - + " kB\n" - + "MemFree: 542404 kB\n" - + "MemAvailable: 1392324 kB\n" - + "Buffers: 64292 kB\n" - + "Cached: 826180 kB\n" - + "SwapCached: 4196 kB\n" - + "Active: 934768 kB\n" - + "Inactive: 743812 kB\n" - + "Active(anon): 582132 kB\n" - + "Inactive(anon): 241500 kB\n" - + "Active(file): 352636 kB\n" - + "Inactive(file): 502312 kB\n" - + "Unevictable: 5148 kB\n" - + "Mlocked: 256 kB\n" - + "SwapTotal: 524284 kB\n" - + "SwapFree: 484800 kB\n" - + "Dirty: 4 kB\n" - + "Writeback: 0 kB\n" - + "AnonPages: 789404 kB\n" - + "Mapped: 241928 kB\n" - + "Shmem: 30632 kB\n" - + "Slab: 122320 kB\n" - + "SReclaimable: 42552 kB\n" - + "SUnreclaim: 79768 kB\n" - + "KernelStack: 22816 kB\n" - + "PageTables: 35344 kB\n" - + "NFS_Unstable: 0 kB\n" - + "Bounce: 0 kB\n" - + "WritebackTmp: 0 kB\n" - + "CommitLimit: 2042280 kB\n" - + "Committed_AS: 76623352 kB\n" - + "VmallocTotal: 251658176 kB\n" - + "VmallocUsed: 232060 kB\n" - + "VmallocChunk: 251347444 kB\n" - + "NvMapMemFree: 48640 kB\n" - + "NvMapMemUsed: 471460 kB\n"; } diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/transport/TransportManagerTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/transport/TransportManagerTest.java index 5376265aa0e..2096fd10ab9 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/transport/TransportManagerTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/transport/TransportManagerTest.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.pm.PackageInfo; +import androidx.arch.core.executor.testing.InstantTaskExecutorRule; import androidx.test.core.app.ApplicationProvider; import com.google.android.datatransport.TransportFactory; import com.google.android.gms.tasks.Tasks; @@ -40,7 +41,6 @@ import com.google.firebase.perf.config.ConfigResolver; import com.google.firebase.perf.session.SessionManager; import com.google.firebase.perf.shadows.ShadowPreconditions; -import com.google.firebase.perf.util.Clock; import com.google.firebase.perf.util.Constants; import com.google.firebase.perf.util.Constants.CounterNames; import com.google.firebase.perf.v1.AndroidMemoryReading; @@ -59,6 +59,7 @@ import java.util.Map; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -88,6 +89,8 @@ public class TransportManagerTest extends FirebasePerformanceTestBase { @Mock private FlgTransport mockFlgTransport; @Captor private ArgumentCaptor perfMetricArgumentCaptor; + @Rule public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule(); + @Before public void setUp() { initMocks(this); @@ -105,7 +108,8 @@ public void setUp() { @After public void tearDown() { reset(mockFirebaseInstallationsApi); - FirebaseApp.clearInstancesForTest(); + reset(mockConfigResolver); + reset(mockRateLimiter); } // region Transport Initialization @@ -1168,8 +1172,9 @@ public void logGaugeMetric_globalCustomAttributesAreNotAdded() { public void logTraceMetric_sessionEnabled_doesNotStripOffSessionId() { TraceMetric.Builder validTrace = createValidTraceMetric().toBuilder(); List perfSessions = new ArrayList<>(); - perfSessions.add( - new com.google.firebase.perf.session.PerfSession("fakeSessionId", new Clock()).build()); + com.google.firebase.perf.session.PerfSession testSession = + com.google.firebase.perf.session.PerfSession.createWithId("fakeSessionId"); + perfSessions.add(testSession.build()); validTrace.addAllPerfSessions(perfSessions); testTransportManager.log(validTrace.build()); @@ -1186,8 +1191,9 @@ public void logNetworkMetric_sessionEnabled_doesNotStripOffSessionId() { NetworkRequestMetric.Builder validNetworkRequest = createValidNetworkRequestMetric().toBuilder(); List perfSessions = new ArrayList<>(); - perfSessions.add( - new com.google.firebase.perf.session.PerfSession("fakeSessionId", new Clock()).build()); + com.google.firebase.perf.session.PerfSession testSession = + com.google.firebase.perf.session.PerfSession.createWithId("fakeSessionId"); + perfSessions.add(testSession.build()); validNetworkRequest.clearPerfSessions().addAllPerfSessions(perfSessions); testTransportManager.log(validNetworkRequest.build()); diff --git a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/api/FirebaseSessionsDependencies.kt b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/api/FirebaseSessionsDependencies.kt index 8d3548c8f4b..4b636a155e0 100644 --- a/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/api/FirebaseSessionsDependencies.kt +++ b/firebase-sessions/src/main/kotlin/com/google/firebase/sessions/api/FirebaseSessionsDependencies.kt @@ -40,19 +40,6 @@ object FirebaseSessionsDependencies { */ @JvmStatic fun addDependency(subscriberName: SessionSubscriber.Name) { - if (subscriberName == SessionSubscriber.Name.PERFORMANCE) { - throw IllegalArgumentException( - """ - Incompatible versions of Firebase Perf and Firebase Sessions. - A safe combination would be: - firebase-sessions:1.1.0 - firebase-crashlytics:18.5.0 - firebase-perf:20.5.0 - For more information contact Firebase Support. - """ - .trimIndent() - ) - } if (dependencies.containsKey(subscriberName)) { Log.d(TAG, "Dependency $subscriberName already added.") return