[fenix] For https://github.com/mozilla-mobile/fenix/issues/20127: remove app_opened_all_startup integration.
parent
bda8a1da30
commit
f59df714f8
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Type, Boolean?>? = 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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Events.appOpenedAllStartupKeys, String>? = 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<Events.appOpenedAllStartupKeys, String>? = 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue