diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index f971d2ff04..143e5c164d 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -205,8 +205,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { initVisualCompletenessQueueAndQueueTasks() ProcessLifecycleOwner.get().lifecycle.addObserver(TelemetryLifecycleObserver(components.core.store)) - - components.appStartupTelemetry.onFenixApplicationOnCreate() } } diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 14de0b6676..228b11531f 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -257,7 +257,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { captureSnapshotTelemetryMetrics() - startupTelemetryOnCreateCalled(intent.toSafeIntent(), savedInstanceState != null) + startupTelemetryOnCreateCalled(intent.toSafeIntent()) startupPathProvider.attachOnActivityOnCreate(lifecycle, intent) startupTypeTelemetry = StartupTypeTelemetry(components.startupStateProvider, startupPathProvider).apply { attachOnHomeActivityOnCreate(lifecycle) @@ -268,17 +268,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { StartupTimeline.onActivityCreateEndHome(this) // DO NOT MOVE ANYTHING BELOW HERE. } - protected open fun startupTelemetryOnCreateCalled( - safeIntent: SafeIntent, - hasSavedInstanceState: Boolean - ) { - // This function gets overridden by subclasses. - components.appStartupTelemetry.onHomeActivityOnCreate( - safeIntent, - hasSavedInstanceState, - homeActivityInitTimeStampNanoSeconds, rootContainer - ) - + private fun startupTelemetryOnCreateCalled(safeIntent: SafeIntent) { + // We intentionally only record this in HomeActivity and not ExternalBrowserActivity (e.g. + // PWAs) so we don't include more unpredictable code paths in the results. components.performance.coldStartupDurationTelemetry.onHomeActivityOnCreate( components.performance.visualCompletenessQueue, components.startupStateProvider, @@ -287,17 +279,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { ) } - override fun onRestart() { - // DO NOT MOVE ANYTHING ABOVE THIS.. - // we are measuring startup time for hot startup type - startupTelemetryOnRestartCalled() - super.onRestart() - } - - private fun startupTelemetryOnRestartCalled() { - components.appStartupTelemetry.onHomeActivityOnRestart(rootContainer) - } - @CallSuper override fun onResume() { super.onResume() @@ -368,8 +349,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { "finishing" to isFinishing.toString() ) ) - - components.appStartupTelemetry.onStop() } final override fun onPause() { @@ -515,8 +494,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { .toSafeIntent() .let(::getIntentAllSource) ?.also { components.analytics.metrics.track(Event.AppReceivedIntent(it)) } - - components.appStartupTelemetry.onHomeActivityOnNewIntent(intent.toSafeIntent()) } /** diff --git a/app/src/main/java/org/mozilla/fenix/components/Components.kt b/app/src/main/java/org/mozilla/fenix/components/Components.kt index 7c124c1b56..6eb0a70e5d 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Components.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt @@ -25,7 +25,6 @@ import org.mozilla.fenix.R import org.mozilla.fenix.autofill.AutofillConfirmActivity import org.mozilla.fenix.autofill.AutofillSearchActivity import org.mozilla.fenix.autofill.AutofillUnlockActivity -import org.mozilla.fenix.components.metrics.AppStartupTelemetry import org.mozilla.fenix.ext.settings import org.mozilla.fenix.perf.AppStartReasonProvider import org.mozilla.fenix.perf.StartupActivityLog @@ -120,8 +119,6 @@ class Components(private val context: Context) { } } - val appStartupTelemetry by lazyMonitored { AppStartupTelemetry(analytics.metrics) } - @Suppress("MagicNumber") val addonUpdater by lazyMonitored { DefaultAddonUpdater(context, AddonUpdater.Frequency(12, TimeUnit.HOURS)) diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurement.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurement.kt deleted file mode 100644 index 5481045550..0000000000 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurement.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.components.metrics - -import android.os.Process -import android.os.SystemClock -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.ERROR -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.WARM -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.HOT -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.COLD -import org.mozilla.fenix.perf.Stat - -/** - * Handles the logic of figuring out launch time for cold, warm and hot startup. - */ -class AppLaunchTimeMeasurement(private val stats: Stat = Stat()) { - - private var isOnPreDrawCalled = false - - private var applicationOnCreateTimeStampNanoSeconds: Long? = null - private var homeActivityInitTimeStampNanoSeconds: Long? = null - private var homeActivityOnRestartTimeStampNanoSeconds: Long? = null - // we are considering screen to be visible after the first pre draw call. - private var homeActivityOnPreDrawTimeStampNanoSeconds: Long? = null - - fun onHomeActivityOnCreate(activityInitNanos: Long) { - this.homeActivityInitTimeStampNanoSeconds = activityInitNanos - } - - fun onHomeActivityOnRestart(activityOnRestartNanos: Long = SystemClock.elapsedRealtimeNanos()) { - homeActivityOnRestartTimeStampNanoSeconds = activityOnRestartNanos - } - - fun onFirstFramePreDraw(activityOnPreDrawNanos: Long = SystemClock.elapsedRealtimeNanos()) { - isOnPreDrawCalled = true - homeActivityOnPreDrawTimeStampNanoSeconds = activityOnPreDrawNanos - } - - /** - * if we have both start and finish time for launch, return the difference otherwise return null. - */ - suspend fun getApplicationLaunchTime(startupType: Type): Long? = withContext(Dispatchers.IO) { - when { - // one use case is user launching the app and quicky pressing back button. in that case - // there will be no onPredraw call but activity will call onStop(). - !isOnPreDrawCalled -> { - null - } - else -> { - when (startupType) { - COLD -> { - applicationOnCreateTimeStampNanoSeconds = - stats.getProcessStartTimeStampNano(Process.myPid()) - homeActivityOnPreDrawTimeStampNanoSeconds!!.minus( - applicationOnCreateTimeStampNanoSeconds!! - ) - } - WARM -> { - homeActivityOnPreDrawTimeStampNanoSeconds!!.minus( - homeActivityInitTimeStampNanoSeconds!! - ) - } - HOT -> { - homeActivityOnPreDrawTimeStampNanoSeconds!!.minus( - homeActivityOnRestartTimeStampNanoSeconds!! - ) - } - ERROR -> { - null - } - } - } - } - } -} diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/AppStartupTelemetry.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/AppStartupTelemetry.kt deleted file mode 100644 index 333fda67f8..0000000000 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/AppStartupTelemetry.kt +++ /dev/null @@ -1,199 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.components.metrics - -import android.content.Intent -import android.view.View -import androidx.annotation.VisibleForTesting -import androidx.core.view.doOnPreDraw -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.OnLifecycleEvent -import androidx.lifecycle.ProcessLifecycleOwner -import mozilla.components.support.utils.SafeIntent -import org.mozilla.fenix.components.metrics.Event.AppAllStartup -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.APP_ICON -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.CUSTOM_TAB -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.LINK -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.UNKNOWN -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.ERROR -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.HOT -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.COLD -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.WARM -import org.mozilla.fenix.perf.runBlockingIncrement -import java.lang.reflect.Modifier.PRIVATE - -/** - * Tracks application startup source, type, launch time, and whether or not activity has - * savedInstance to restore the activity from. - * Sample = [source = COLD, type = APP_ICON, hasSavedInstanceState = false,launchTimeNanoSeconds = 1824000000] - * The basic idea is to collect these metrics from different phases of startup through - * [AppAllStartup] and finally report them on Activity's onResume() function. - * - * **THIS CLASS HAS A KNOWN FLAW:** for COLD start, it doesn't take into account if the process is - * already running when the app starts, possibly inflating results (e.g. a Service started the - * process 20min ago and only now is HomeActivity launching). Future telemetry implementations should - * probably move in the ideological direction of [org.mozilla.fenix.perf.ColdStartupDurationTelemetry]: - * simplicity rather than comprehensiveness. - */ -@Suppress("TooManyFunctions") -class AppStartupTelemetry( - private val metrics: MetricController, - @VisibleForTesting(otherwise = PRIVATE) - var appLaunchTimeMeasurement: AppLaunchTimeMeasurement = AppLaunchTimeMeasurement() -) : LifecycleObserver { - - init { - ProcessLifecycleOwner.get().lifecycle.addObserver(this) - } - - private var isMetricRecordedSinceAppWasForegrounded = false - private var wasAppCreateCalledBeforeActivityCreate = false - - private var onCreateData: AppAllStartup? = null - private var onRestartData: Pair? = null - private var onNewIntentData: Source? = null - - fun onFenixApplicationOnCreate() { - wasAppCreateCalledBeforeActivityCreate = true - } - - fun onHomeActivityOnCreate( - safeIntent: SafeIntent, - hasSavedInstanceState: Boolean, - homeActivityInitTimeStampNanoSeconds: Long, - rootContainer: View - ) { - setOnCreateData(safeIntent, hasSavedInstanceState, homeActivityInitTimeStampNanoSeconds, false) - rootContainer.doOnPreDraw { - onPreDraw() - } - } - - fun onExternalAppBrowserOnCreate( - safeIntent: SafeIntent, - hasSavedInstanceState: Boolean, - homeActivityInitTimeStampNanoSeconds: Long, - rootContainer: View - ) { - setOnCreateData(safeIntent, hasSavedInstanceState, homeActivityInitTimeStampNanoSeconds, true) - rootContainer.doOnPreDraw { - onPreDraw() - } - } - - fun onHomeActivityOnRestart(rootContainer: View) { - // DO NOT MOVE ANYTHING ABOVE THIS.. - // we are measuring startup time for hot startup type - appLaunchTimeMeasurement.onHomeActivityOnRestart() - - // we are not setting [Source] in this method since source is derived from an intent. - // therefore source gets set in onNewIntent(). - onRestartData = Pair(HOT, null) - - rootContainer.doOnPreDraw { - onPreDraw() - } - } - - fun onHomeActivityOnNewIntent(safeIntent: SafeIntent) { - // we are only setting [Source] in this method since source is derived from an intent]. - // other metric fields are set in onRestart() - onNewIntentData = getStartupSourceFromIntent(safeIntent, false) - } - - private fun setOnCreateData( - safeIntent: SafeIntent, - hasSavedInstanceState: Boolean, - homeActivityInitTimeStampNanoSeconds: Long, - isExternalAppBrowserActivity: Boolean - ) { - onCreateData = AppAllStartup( - getStartupSourceFromIntent(safeIntent, isExternalAppBrowserActivity), - getAppStartupType(), - hasSavedInstanceState - ) - appLaunchTimeMeasurement.onHomeActivityOnCreate(homeActivityInitTimeStampNanoSeconds) - wasAppCreateCalledBeforeActivityCreate = false - } - - private fun getAppStartupType(): Type { - return if (wasAppCreateCalledBeforeActivityCreate) COLD else WARM - } - - private fun getStartupSourceFromIntent( - intent: SafeIntent, - isExternalAppBrowserActivity: Boolean - ): Source { - return when { - // since the intent action is same (ACTION_VIEW) for both CUSTOM_TAB and LINK. - // we have to make sure that we are checking for CUSTOM_TAB condition first as this - // check does not rely on intent action - isExternalAppBrowserActivity -> CUSTOM_TAB - intent.isLauncherIntent -> APP_ICON - intent.action == Intent.ACTION_VIEW -> LINK - // one of the unknown case is app switcher, where we go to the recent tasks to launch - // Fenix. - else -> UNKNOWN - } - } - - private suspend fun recordMetric() { - if (!isMetricRecordedSinceAppWasForegrounded) { - val appAllStartup: AppAllStartup = if (onCreateData != null) { - onCreateData!! - } else { - mergeOnRestartAndOnNewIntentIntoStartup() - } - appAllStartup.launchTime = appLaunchTimeMeasurement.getApplicationLaunchTime(appAllStartup.type) - metrics.track(appAllStartup) - isMetricRecordedSinceAppWasForegrounded = true - } - // we don't want any weird previous states to persist on our next metric record. - onCreateData = null - onNewIntentData = null - onRestartData = null - appLaunchTimeMeasurement = AppLaunchTimeMeasurement() - } - - private fun mergeOnRestartAndOnNewIntentIntoStartup(): AppAllStartup { - return AppAllStartup( - onNewIntentData ?: UNKNOWN, - onRestartData?.first ?: ERROR, - onRestartData?.second - ) - } - - @OnLifecycleEvent(Lifecycle.Event.ON_STOP) - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - fun onApplicationOnStop() { - // application was backgrounded, we need to record the new metric type if - // application was to come to foreground again. - // Therefore we set the isMetricRecorded flag to false. - isMetricRecordedSinceAppWasForegrounded = false - } - - /** - *record the timestamp for the first frame drawn - */ - @VisibleForTesting(otherwise = PRIVATE) - fun onPreDraw() { - // DO NOT MOVE ANYTHING ABOVE THIS.. - // we are measuring startup time here. - appLaunchTimeMeasurement.onFirstFramePreDraw() - } - - /** - * record the metrics, blocking the main thread to make sure we get our metrics recorded before - * the application potentially closes. - */ - fun onStop() { - runBlockingIncrement { - recordMetric() - } - } -} diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt index 261744a36c..d48165b0f4 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt @@ -407,35 +407,6 @@ sealed class Event { get() = hashMapOf(Events.appOpenedAllStartupKeys.source to source.name) } - data class AppAllStartup( - val source: Source, - val type: Type, - val hasSavedInstanceState: Boolean? = null, - var launchTime: Long? = null - ) : Event() { - enum class Source { APP_ICON, LINK, CUSTOM_TAB, UNKNOWN } - enum class Type { COLD, WARM, HOT, ERROR } - - override val extras: Map? - get() { - val extrasMap = hashMapOf( - Events.appOpenedAllStartupKeys.source to source.toString(), - Events.appOpenedAllStartupKeys.type to type.toString() - ) - // we are only sending hasSavedInstanceState whenever we get data from - // activity's oncreate() method. - if (hasSavedInstanceState != null) { - extrasMap[Events.appOpenedAllStartupKeys.hasSavedInstanceState] = - hasSavedInstanceState.toString() - } - if (launchTime != null) { - extrasMap[Events.appOpenedAllStartupKeys.firstFramePreDrawNanos] = - launchTime.toString() - } - return extrasMap - } - } - data class CollectionSaveButtonPressed(val fromScreen: String) : Event() { override val extras: Map? get() = mapOf(Collections.saveButtonKeys.fromScreen to fromScreen) diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt index 01d916ea65..3d74f86c32 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt @@ -118,10 +118,6 @@ private val Event.wrapper: EventWrapper<*>? { Events.appReceivedIntent.record(it) }, { Events.appReceivedIntentKeys.valueOf(it) } ) - is Event.AppAllStartup -> EventWrapper( - { Events.appOpenedAllStartup.record(it) }, - { Events.appOpenedAllStartupKeys.valueOf(it) } - ) is Event.SearchBarTapped -> EventWrapper( { Events.searchBarTapped.record(it) }, { Events.searchBarTappedKeys.valueOf(it) } diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt index a7546b3789..0c67ac93ee 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt @@ -52,19 +52,6 @@ open class ExternalAppBrowserActivity : HomeActivity() { final override fun getIntentSessionId(intent: SafeIntent) = intent.getSessionId() - override fun startupTelemetryOnCreateCalled(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) { - components.appStartupTelemetry.onExternalAppBrowserOnCreate( - safeIntent, - hasSavedInstanceState, - // HomeActivity is init before ExternalAppBrowserActivity so we use that time. - homeActivityInitTimeStampNanoSeconds, - rootContainer - ) - - // coldStartupDurationTelemetry.onHomeActivityOnCreate is intentionally omitted so we don't - // include even more unpredictable code paths in the results. - } - override fun navigateToBrowserOnColdStart() { // No-op for external app } diff --git a/app/src/main/java/org/mozilla/fenix/perf/ColdStartupDurationTelemetry.kt b/app/src/main/java/org/mozilla/fenix/perf/ColdStartupDurationTelemetry.kt index 28d757c260..33bf211eda 100644 --- a/app/src/main/java/org/mozilla/fenix/perf/ColdStartupDurationTelemetry.kt +++ b/app/src/main/java/org/mozilla/fenix/perf/ColdStartupDurationTelemetry.kt @@ -18,7 +18,7 @@ private val logger = Logger("ColdStartupDuration") /** * A class to record COLD start up telemetry. This class is intended to improve upon our mistakes from the - * [org.mozilla.fenix.components.metrics.AppStartupTelemetry] class by being simple-to-implement and + * AppStartupTelemetry class by being simple-to-implement and * simple-to-analyze (i.e. works in GLAM) rather than being a "perfect" and comprehensive measurement. * * This class relies on external state providers like [StartupStateProvider] that are tricky to diff --git a/app/src/test/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurementTest.kt b/app/src/test/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurementTest.kt deleted file mode 100644 index e22aaac9a8..0000000000 --- a/app/src/test/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurementTest.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.components.metrics - -import android.os.SystemClock -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.HOT -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.COLD -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.ERROR -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.WARM -import org.mozilla.fenix.perf.Stat - -class AppLaunchTimeMeasurementTest { - - @MockK - private lateinit var statMock: Stat - - private lateinit var appLaunchTimeMeasurement: AppLaunchTimeMeasurement - private val startTime = SystemClock.elapsedRealtimeNanos() - private val endTime = SystemClock.elapsedRealtimeNanos() + 1 - - @Before - fun setUp() { - MockKAnnotations.init(this) - appLaunchTimeMeasurement = AppLaunchTimeMeasurement(statMock) - - every { statMock.getProcessStartTimeStampNano(any()) } returns startTime - } - - @Test - fun `WHEN application is launched with cold startup THEN report the correct value`() { - runBlocking { - appLaunchTimeMeasurement.onFirstFramePreDraw(endTime) - - val actualResult = endTime.minus(startTime) - assertTrue(appLaunchTimeMeasurement.getApplicationLaunchTime(COLD) == actualResult) - } - } - - @Test - fun `WHEN application is launch with warm startup THEN report the correct value`() { - appLaunchTimeMeasurement.onHomeActivityOnCreate(startTime) - appLaunchTimeMeasurement.onFirstFramePreDraw(endTime) - - val actualResult = endTime.minus(startTime) - runBlocking { - assertTrue(appLaunchTimeMeasurement.getApplicationLaunchTime(WARM) == actualResult) - } - } - - @Test - fun `WHEN application is launch with hot startup THEN report the correct value`() { - appLaunchTimeMeasurement.onHomeActivityOnRestart(startTime) - appLaunchTimeMeasurement.onFirstFramePreDraw(endTime) - - val actualResult = endTime.minus(startTime) - runBlocking { - assertTrue(appLaunchTimeMeasurement.getApplicationLaunchTime(HOT) == actualResult) - } - } - - @Test - fun `WHEN getting launch time before onDraw() is called THEN report the correct value`() { - appLaunchTimeMeasurement.onHomeActivityOnCreate(startTime) - - val actualResult = null - runBlocking { - assertTrue(appLaunchTimeMeasurement.getApplicationLaunchTime(ERROR) == actualResult) - } - } -} diff --git a/app/src/test/java/org/mozilla/fenix/components/metrics/AppStartupTelemetryTest.kt b/app/src/test/java/org/mozilla/fenix/components/metrics/AppStartupTelemetryTest.kt deleted file mode 100644 index 1a878feffb..0000000000 --- a/app/src/test/java/org/mozilla/fenix/components/metrics/AppStartupTelemetryTest.kt +++ /dev/null @@ -1,314 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.components.metrics - -import android.content.Intent -import android.os.SystemClock -import android.view.View -import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.RelaxedMockK -import io.mockk.verify -import io.mockk.MockKAnnotations -import io.mockk.coEvery -import io.mockk.every -import io.mockk.clearMocks -import mozilla.components.support.utils.toSafeIntent -import org.junit.Before -import org.junit.Test -import org.junit.Assert.assertTrue -import org.junit.runner.RunWith -import org.mozilla.fenix.GleanMetrics.Events -import org.mozilla.fenix.GleanMetrics.Events.appOpenedAllStartupKeys.firstFramePreDrawNanos -import org.mozilla.fenix.GleanMetrics.Events.appOpenedAllStartupKeys.hasSavedInstanceState -import org.mozilla.fenix.GleanMetrics.Events.appOpenedAllStartupKeys.source -import org.mozilla.fenix.GleanMetrics.Events.appOpenedAllStartupKeys.type -import org.mozilla.fenix.components.metrics.Event.AppAllStartup -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.APP_ICON -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.UNKNOWN -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.CUSTOM_TAB -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.LINK -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.COLD -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.WARM -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.HOT -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.ERROR -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner - -@RunWith(FenixRobolectricTestRunner::class) -class AppStartupTelemetryTest { - - @MockK - private lateinit var metricControllerMock: MetricController - @MockK - private lateinit var intentMock: Intent - @RelaxedMockK - private lateinit var appLaunchTimeMeasurementMock: AppLaunchTimeMeasurement - @RelaxedMockK - private lateinit var rootContainerMock: View - - private lateinit var appStartupTelemetry: AppStartupTelemetry - - private val homeActivityInitTime = SystemClock.elapsedRealtimeNanos() - private val onPreDrawTime = SystemClock.elapsedRealtimeNanos() + 1 - - @Before - fun setup() { - MockKAnnotations.init(this) - appStartupTelemetry = AppStartupTelemetry(metricControllerMock, appLaunchTimeMeasurementMock) - - coEvery { appLaunchTimeMeasurementMock.getApplicationLaunchTime(any()) } returns onPreDrawTime.minus(homeActivityInitTime) - every { metricControllerMock.track(any()) } returns Unit - } - - @Test - fun `WHEN application is launch for the first time through application icon THEN records the correct values`() { - setupIntentMock(APP_ICON) - - appStartupTelemetry.onFenixApplicationOnCreate() - appStartupTelemetry.onHomeActivityOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(APP_ICON, COLD, false, onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `WHEN application is launch for the first time through a url link THEN records the correct values`() { - setupIntentMock(LINK) - - appStartupTelemetry.onFenixApplicationOnCreate() - appStartupTelemetry.onHomeActivityOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(LINK, COLD, false, onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `WHEN application is launch for the first time through an custom tab THEN records the correct values`() { - setupIntentMock(CUSTOM_TAB) - - appStartupTelemetry.onFenixApplicationOnCreate() - appStartupTelemetry.onExternalAppBrowserOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(CUSTOM_TAB, COLD, false, onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through app icon and HomeActivity is recreated THEN records the correct values`() { - setupIntentMock(APP_ICON) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onHomeActivityOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(APP_ICON, WARM, false, onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through url link and HomeActivity is recreated THEN records the correct values`() { - setupIntentMock(LINK) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onHomeActivityOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(LINK, WARM, false, onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through custom tab and ExternalAppBrowserActivity is recreated THEN records the correct values`() { - setupIntentMock(CUSTOM_TAB) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onExternalAppBrowserOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(CUSTOM_TAB, WARM, false, onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through app icon and HomeActivity is restarted THEN records the correct values`() { - setupIntentMock(APP_ICON) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onHomeActivityOnRestart(rootContainerMock) - appStartupTelemetry.onHomeActivityOnNewIntent(intentMock.toSafeIntent()) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(APP_ICON, HOT, launchTime = onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through url link and HomeActivity is restarted THEN records the correct values`() { - setupIntentMock(LINK) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onHomeActivityOnRestart(rootContainerMock) - appStartupTelemetry.onHomeActivityOnNewIntent(intentMock.toSafeIntent()) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(LINK, HOT, launchTime = onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `WHEN application is launched and onStop() is called twice THEN metric is reported only once`() { - setupIntentMock(LINK) - appStartupTelemetry.onExternalAppBrowserOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - appStartupTelemetry.onStop() - - verify(exactly = 1) { metricControllerMock.track(any()) } - } - - @Test - fun `GIVEN application is in background WHEN application is launched again through unknown source and HomeActivity exists THEN records the correct values`() { - setupIntentMock(UNKNOWN) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onHomeActivityOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(UNKNOWN, WARM, false, onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `GIVEN application exists and is backgrounded WHEN application started again through app icon but HomeActivity is recreated from savedInstanceState THEN records the correct values`() { - setupIntentMock(APP_ICON) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onHomeActivityOnCreate(intentMock.toSafeIntent(), true, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(APP_ICON, WARM, true, onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - private fun launchApplicationAndPutApplicationInBackground() { - appStartupTelemetry.onFenixApplicationOnCreate() - appStartupTelemetry.onHomeActivityOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - appStartupTelemetry.appLaunchTimeMeasurement = appLaunchTimeMeasurementMock - - // have to clear the mock function calls so it doesnt interfere with tests - clearMocks(metricControllerMock, answers = false) - - appStartupTelemetry.onApplicationOnStop() - } - - @Test - fun `GIVEN application is in background WHEN application is launched again HomeActivity only calls onResume THEN records the correct values`() { - setupIntentMock(UNKNOWN) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(UNKNOWN, ERROR, launchTime = onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `GIVEN application is in background WHEN application is launched again HomeActivity calls onRestart but not onNewIntent THEN records the correct values`() { - setupIntentMock(APP_ICON) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onHomeActivityOnRestart(rootContainerMock) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(UNKNOWN, HOT, launchTime = onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `WHEN application is launched and onStop is called before onPreDraw THEN records the correct values`() { - setupIntentMock(APP_ICON) - coEvery { appLaunchTimeMeasurementMock.getApplicationLaunchTime(any()) } returns null - - appStartupTelemetry.onFenixApplicationOnCreate() - appStartupTelemetry.onHomeActivityOnCreate(intentMock.toSafeIntent(), false, homeActivityInitTime, rootContainerMock) - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(APP_ICON, COLD, false) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `GIVEN application is in background WHEN application is launched again and HomeActivity calls onNewIntent but not onRestart THEN records the correct values`() { - setupIntentMock(APP_ICON) - launchApplicationAndPutApplicationInBackground() - - appStartupTelemetry.onHomeActivityOnNewIntent(intentMock.toSafeIntent()) - appStartupTelemetry.onPreDraw() - appStartupTelemetry.onStop() - - val validMetric = AppAllStartup(APP_ICON, ERROR, launchTime = onPreDrawTime.minus(homeActivityInitTime)) - verify(exactly = 1) { metricControllerMock.track(validMetric) } - } - - @Test - fun `WHEN AppAllStartup does not have savedInstanceState THEN do not return savedInstanceState`() { - val expectedExtra: Map? = hashMapOf( - source to APP_ICON.toString(), - type to HOT.toString(), - firstFramePreDrawNanos to onPreDrawTime.minus(homeActivityInitTime).toString()) - - val appAllStartup = AppAllStartup(APP_ICON, HOT, launchTime = onPreDrawTime.minus(homeActivityInitTime)) - - assertTrue(appAllStartup.extras!! == expectedExtra) - } - - @Test - fun `WHEN AppAllStartup have savedInstanceState THEN return savedInstanceState `() { - val expectedExtra: Map? = hashMapOf( - source to APP_ICON.toString(), - type to COLD.toString(), - hasSavedInstanceState to true.toString(), - firstFramePreDrawNanos to onPreDrawTime.minus(homeActivityInitTime).toString()) - - val appAllStartup = AppAllStartup(APP_ICON, COLD, true, onPreDrawTime.minus(homeActivityInitTime)) - - assertTrue(appAllStartup.extras!! == expectedExtra) - } - - private fun setupIntentMock(source: Source) { - when (source) { - APP_ICON -> { - every { intentMock.action } returns Intent.ACTION_MAIN - every { intentMock.categories } returns setOf(Intent.CATEGORY_LAUNCHER) - } - LINK, CUSTOM_TAB -> { - every { intentMock.action } returns Intent.ACTION_VIEW - every { intentMock.categories } returns emptySet() - } - UNKNOWN -> { - every { intentMock.action } returns Intent.ACTION_MAIN - every { intentMock.categories } returns emptySet() - } - } - } -}