For #20127: remove app_opened_all_startup integration.

upstream-sync
Michael Comella 3 years ago committed by mergify[bot]
parent 2c1d0ea185
commit be64ee4159

@ -205,8 +205,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
initVisualCompletenessQueueAndQueueTasks()
ProcessLifecycleOwner.get().lifecycle.addObserver(TelemetryLifecycleObserver(components.core.store))
components.appStartupTelemetry.onFenixApplicationOnCreate()
}
}

@ -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())
}
/**

@ -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))

@ -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()
}
}
}

@ -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<Events.appOpenedAllStartupKeys, String>?
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<Collections.saveButtonKeys, String>?
get() = mapOf(Collections.saveButtonKeys.fromScreen to fromScreen)

@ -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) }

@ -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
}

@ -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

@ -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…
Cancel
Save