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/perf/StartupStateProviderTest.kt

308 lines
12 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.perf
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.IntentReceiverActivity
import org.mozilla.fenix.perf.AppStartReasonProvider.StartReason
import org.mozilla.fenix.perf.StartupActivityLog.LogEntry
class StartupStateProviderTest {
private lateinit var provider: StartupStateProvider
@MockK private lateinit var startupActivityLog: StartupActivityLog
@MockK private lateinit var startReasonProvider: AppStartReasonProvider
private lateinit var logEntries: MutableList<LogEntry>
private val homeActivityClass = HomeActivity::class.java
private val irActivityClass = IntentReceiverActivity::class.java
@Before
fun setUp() {
MockKAnnotations.init(this)
provider = StartupStateProvider(startupActivityLog, startReasonProvider)
logEntries = mutableListOf()
every { startupActivityLog.log } returns logEntries
every { startReasonProvider.reason } returns StartReason.ACTIVITY // default to minimize repetition.
}
@Test
fun `GIVEN the app started for an activity WHEN is cold start THEN cold start is true`() {
forEachColdStartEntries { index ->
assertTrue("$index", provider.isColdStartForStartedActivity(homeActivityClass))
}
}
@Test
fun `GIVEN the app started for an activity WHEN warm start THEN cold start is false`() {
forEachWarmStartEntries { index ->
assertFalse("$index", provider.isColdStartForStartedActivity(homeActivityClass))
}
}
@Test
fun `GIVEN the app started for an activity WHEN hot start THEN cold start is false` () {
forEachHotStartEntries { index ->
assertFalse("$index", provider.isColdStartForStartedActivity(homeActivityClass))
}
}
@Test
fun `GIVEN the app started for an activity WHEN is cold start THEN warm start is false`() {
forEachColdStartEntries { index ->
assertFalse("$index", provider.isWarmStartForStartedActivity(homeActivityClass))
}
}
@Test
fun `GIVEN the app started for an activity WHEN is warm start THEN warm start is true`() {
forEachWarmStartEntries { index ->
assertTrue("$index", provider.isWarmStartForStartedActivity(homeActivityClass))
}
}
@Test
fun `GIVEN the app started for an activity WHEN is hot start THEN warm start is false` () {
forEachHotStartEntries { index ->
assertFalse("$index", provider.isWarmStartForStartedActivity(homeActivityClass))
}
}
@Test
fun `GIVEN the app started for an activity WHEN we launched HA through a drawing IntentRA THEN start up is not cold`() {
// These entries mimic observed behavior for local code changes.
logEntries.addAll(listOf(
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityStarted(irActivityClass),
LogEntry.AppStarted,
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.ActivityStopped(irActivityClass)
))
assertFalse(provider.isColdStartForStartedActivity(homeActivityClass))
}
@Test
fun `GIVEN the app started for an activity WHEN we launched HA through a drawing IntentRA THEN start up is not warm`() {
// These entries mimic observed behavior for local code changes.
logEntries.addAll(listOf(
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityStarted(irActivityClass),
LogEntry.AppStarted,
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.ActivityStopped(irActivityClass)
))
assertFalse(provider.isWarmStartForStartedActivity(homeActivityClass))
}
@Test
fun `GIVEN the app started for an activity WHEN two HomeActivities are created THEN start up is not cold`() {
// We're making an assumption about how this would work based on previous observed patterns.
// AIUI, we should never have more than one HomeActivity.
logEntries.addAll(listOf(
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted,
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.ActivityStopped(homeActivityClass)
))
assertFalse(provider.isColdStartForStartedActivity(homeActivityClass))
}
@Test
fun `GIVEN the app started for an activity WHEN an activity hasn't been created yet THEN start up is not cold`() {
assertFalse(provider.isColdStartForStartedActivity(homeActivityClass))
}
@Test
fun `GIVEN the app started for an activity WHEN an activity hasn't started yet THEN start up is not cold`() {
logEntries.addAll(listOf(
LogEntry.ActivityCreated(homeActivityClass)
))
assertFalse(provider.isColdStartForStartedActivity(homeActivityClass))
}
@Test
fun `GIVEN the app did not start for an activity WHEN is cold is checked THEN it returns false`() {
every { startReasonProvider.reason } returns StartReason.NON_ACTIVITY
assertFalse(provider.isColdStartForStartedActivity(homeActivityClass))
forEachColdStartEntries { index ->
assertFalse("$index", provider.isColdStartForStartedActivity(homeActivityClass))
}
}
@Test
fun `GIVEN the app has been stopped WHEN is cold short circuit is called THEN it returns true`() {
logEntries.add(LogEntry.AppStopped)
assertTrue(provider.shouldShortCircuitColdStart())
}
@Test
fun `GIVEN the app has not been stopped WHEN is cold short circuit is called THEN it returns false`() {
assertFalse(provider.shouldShortCircuitColdStart())
}
@Test
fun `GIVEN the app has not been stopped WHEN an activity has not been created THEN it's not a warm start`() {
assertFalse(provider.isWarmStartForStartedActivity(homeActivityClass))
}
@Test
fun `GIVEN the app has been stopped WHEN an activity has not been created THEN it's not a warm start`() {
logEntries.add(LogEntry.AppStopped)
assertFalse(provider.isWarmStartForStartedActivity(homeActivityClass))
}
@Test
fun `GIVEN the app has been stopped WHEN an activity has not been started THEN it's not a warm start`() {
logEntries.addAll(listOf(
LogEntry.AppStopped,
LogEntry.ActivityCreated(homeActivityClass)
))
assertFalse(provider.isWarmStartForStartedActivity(homeActivityClass))
}
private fun forEachColdStartEntries(block: (index: Int) -> Unit) {
// These entries mimic observed behavior.
//
// MAIN: open HomeActivity directly.
val coldStartEntries = listOf(listOf(
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
// VIEW: open non-drawing IntentReceiverActivity, then HomeActivity.
), listOf(
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
))
forEachStartEntry(coldStartEntries, block)
}
private fun forEachWarmStartEntries(block: (index: Int) -> Unit) {
// These entries mimic observed behavior. We test both truncated (i.e. the current behavior
// with the optimization to prevent an infinite log) and untruncated (the behavior without
// such an optimization).
//
// truncated MAIN: open HomeActivity directly.
val warmStartEntries = listOf(listOf(
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
// untruncated MAIN: open HomeActivity directly.
), listOf(
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted,
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
// truncated VIEW: open non-drawing IntentReceiverActivity, then HomeActivity.
), listOf(
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
// untruncated VIEW: open non-drawing IntentReceiverActivity, then HomeActivity.
), listOf(
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted,
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
))
forEachStartEntry(warmStartEntries, block)
}
private fun forEachHotStartEntries(block: (index: Int) -> Unit) {
// These entries mimic observed behavior. We test both truncated (i.e. the current behavior
// with the optimization to prevent an infinite log) and untruncated (the behavior without
// such an optimization).
//
// truncated MAIN: open HomeActivity directly.
val hotStartEntries = listOf(listOf(
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
// untruncated MAIN: open HomeActivity directly.
), listOf(
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted,
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
// truncated VIEW: open non-drawing IntentReceiverActivity, then HomeActivity.
), listOf(
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
// untruncated VIEW: open non-drawing IntentReceiverActivity, then HomeActivity.
), listOf(
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityCreated(homeActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted,
LogEntry.AppStopped,
LogEntry.ActivityStopped(homeActivityClass),
LogEntry.ActivityCreated(irActivityClass),
LogEntry.ActivityStarted(homeActivityClass),
LogEntry.AppStarted
))
forEachStartEntry(hotStartEntries, block)
}
private fun forEachStartEntry(entries: List<List<LogEntry>>, block: (index: Int) -> Unit) {
entries.forEachIndexed { index, startEntry ->
logEntries.clear()
logEntries.addAll(startEntry)
block(index)
}
}
}