You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iceraven-browser/app/src/test/java/org/mozilla/fenix/components/metrics/AppStartupTelemetryTest.kt

315 lines
14 KiB
Kotlin

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