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.
177 lines
7.3 KiB
Kotlin
177 lines
7.3 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.Called
|
|
import io.mockk.MockKAnnotations
|
|
import io.mockk.every
|
|
import io.mockk.impl.annotations.MockK
|
|
import io.mockk.spyk
|
|
import io.mockk.verify
|
|
import mozilla.components.service.glean.private.BooleanMetricType
|
|
import mozilla.components.service.glean.private.QuantityMetricType
|
|
import mozilla.components.service.glean.private.TimespanMetricType
|
|
import org.junit.Before
|
|
import org.junit.Test
|
|
import org.mozilla.fenix.GleanMetrics.StartupTimeline as Telemetry
|
|
|
|
private const val PRIMARY_TICKS = 100L
|
|
private const val SECONDARY_TICKS = 50L
|
|
|
|
class StartupFrameworkStartMeasurementTest {
|
|
|
|
private lateinit var metrics: StartupFrameworkStartMeasurement
|
|
private lateinit var stat: Stat
|
|
|
|
// We'd prefer to use the Glean test methods over these mocks but they require us to add
|
|
// Robolectric and it's not worth the impact on test duration.
|
|
@MockK private lateinit var telemetry: Telemetry
|
|
@MockK(relaxed = true) private lateinit var frameworkPrimary: TimespanMetricType
|
|
@MockK(relaxed = true) private lateinit var frameworkSecondary: TimespanMetricType
|
|
@MockK(relaxed = true) private lateinit var frameworkStartError: BooleanMetricType
|
|
@MockK(relaxed = true) private lateinit var clockTicksPerSecondV2: QuantityMetricType
|
|
|
|
private var clockTicksPerSecondValue = -1L
|
|
|
|
private var elapsedRealtimeNanos = -1L
|
|
private var processStartTimeTicks = -1L
|
|
|
|
@Before
|
|
fun setUp() {
|
|
MockKAnnotations.init(this)
|
|
|
|
elapsedRealtimeNanos = -1
|
|
processStartTimeTicks = -1
|
|
|
|
// This value is hard-coded in the OS so we default to it as it's the expected value.
|
|
clockTicksPerSecondValue = PRIMARY_TICKS
|
|
|
|
stat = spyk(object : Stat() {
|
|
override val clockTicksPerSecond: Long get() = clockTicksPerSecondValue
|
|
})
|
|
every { stat.getProcessStartTimeTicks(any()) } answers { processStartTimeTicks }
|
|
val getElapsedRealtimeNanos = { elapsedRealtimeNanos }
|
|
|
|
every { telemetry.frameworkPrimary } returns frameworkPrimary
|
|
every { telemetry.frameworkSecondary } returns frameworkSecondary
|
|
every { telemetry.frameworkStartError } returns frameworkStartError
|
|
every { telemetry.clockTicksPerSecondV2 } returns clockTicksPerSecondV2
|
|
|
|
metrics = StartupFrameworkStartMeasurement(stat, telemetry, getElapsedRealtimeNanos)
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN app init is invalid WHEN metrics are set THEN frameworkStartError is set to true`() {
|
|
setProcessAppInitAndMetrics(processStart = 10, appInit = -1)
|
|
verifyFrameworkStartError()
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN app init is not called WHEN metrics are set THEN frameworkStartError is set to true`() {
|
|
metrics.setExpensiveMetric()
|
|
verifyFrameworkStartError()
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN app init is set to valid values and clock ticks per second is 100 WHEN metrics are set THEN frameworkPrimary is set with the correct value`() {
|
|
clockTicksPerSecondValue = PRIMARY_TICKS
|
|
setProcessAppInitAndMetrics(processStart = 166_636_813, appInit = 1_845_312_345_673_925)
|
|
verifyFrameworkStartSuccess(178_944_220_000_000, isPrimary = true) // calculated by hand.
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN app init is set to valid values and clock ticks per second is not 100 WHEN metrics are set THEN frameworkSecondary is set with the correct value`() {
|
|
clockTicksPerSecondValue = SECONDARY_TICKS
|
|
setProcessAppInitAndMetrics(processStart = 166_636_813, appInit = 1_845_312_345_673_925)
|
|
verifyFrameworkStartSuccess(178_944_220_000_000, isPrimary = false) // calculated by hand.
|
|
}
|
|
|
|
@Test // this overlaps with the success case test.
|
|
fun `GIVEN app init has valid values and clock ticks per second is 100 WHEN onAppInit is called twice and metrics are set THEN frameworkPrimary uses the first app init value`() {
|
|
clockTicksPerSecondValue = PRIMARY_TICKS
|
|
testAppInitCalledTwice(isPrimary = true)
|
|
}
|
|
|
|
@Test // this overlaps with the success case test.
|
|
fun `GIVEN app init has valid values and clock ticks per second is not 100 WHEN onAppInit is called twice and metrics are set THEN frameworkSecondary uses the first app init value`() {
|
|
clockTicksPerSecondValue = SECONDARY_TICKS
|
|
testAppInitCalledTwice(isPrimary = false)
|
|
}
|
|
|
|
private fun testAppInitCalledTwice(isPrimary: Boolean) {
|
|
processStartTimeTicks = 166_636_813
|
|
|
|
elapsedRealtimeNanos = 1_845_312_345_673_925
|
|
metrics.onApplicationInit()
|
|
elapsedRealtimeNanos = 1_945_312_345_673_925
|
|
metrics.onApplicationInit()
|
|
|
|
metrics.setExpensiveMetric()
|
|
verifyFrameworkStartSuccess(178_944_220_000_000, isPrimary) // calculated by hand.
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN app init have valid values and clock ticks per second is 100 WHEN metrics are set twice THEN frameworkPrimary is only set once`() {
|
|
clockTicksPerSecondValue = PRIMARY_TICKS
|
|
testMetricsSetTwice(isPrimary = true)
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN app init have valid values and clock ticks per second is not 100 WHEN metrics are set twice THEN frameworkSecondary is only set once`() {
|
|
clockTicksPerSecondValue = SECONDARY_TICKS
|
|
testMetricsSetTwice(isPrimary = false)
|
|
}
|
|
|
|
private fun testMetricsSetTwice(isPrimary: Boolean) {
|
|
setProcessAppInitAndMetrics(10, 100)
|
|
metrics.setExpensiveMetric()
|
|
|
|
val (setMetric, unsetMetric) = getSetAndUnsetMetric(isPrimary)
|
|
verify(exactly = 1) { setMetric.setRawNanos(any()) }
|
|
verify { unsetMetric wasNot Called }
|
|
verify(exactly = 1) { clockTicksPerSecondV2.set(any()) }
|
|
verify { frameworkStartError wasNot Called }
|
|
}
|
|
|
|
private fun setProcessAppInitAndMetrics(processStart: Long, appInit: Long) {
|
|
processStartTimeTicks = processStart
|
|
|
|
elapsedRealtimeNanos = appInit
|
|
metrics.onApplicationInit()
|
|
|
|
metrics.setExpensiveMetric()
|
|
}
|
|
|
|
private fun verifyFrameworkStartSuccess(nanos: Long, isPrimary: Boolean) {
|
|
val (setMetric, unsetMetric) = getSetAndUnsetMetric(isPrimary)
|
|
verify { setMetric.setRawNanos(nanos) }
|
|
verify { unsetMetric wasNot Called }
|
|
|
|
val expectedClockTicksPerSecond = getExpectedClockTicksPerSecond(isPrimary)
|
|
verify { clockTicksPerSecondV2.set(expectedClockTicksPerSecond) }
|
|
verify { frameworkStartError wasNot Called }
|
|
}
|
|
|
|
private fun verifyFrameworkStartError() {
|
|
verify { frameworkStartError.set(true) }
|
|
verify { frameworkPrimary wasNot Called }
|
|
verify { frameworkSecondary wasNot Called }
|
|
verify { clockTicksPerSecondV2 wasNot Called }
|
|
}
|
|
|
|
private fun getSetAndUnsetMetric(isPrimary: Boolean): Pair<TimespanMetricType, TimespanMetricType> {
|
|
return if (isPrimary) {
|
|
Pair(frameworkPrimary, frameworkSecondary)
|
|
} else {
|
|
Pair(frameworkSecondary, frameworkPrimary)
|
|
}
|
|
}
|
|
|
|
// This hard-codes some data that's passed into the test but I don't want to spend more time
|
|
// so I don't bother cleaning it up now.
|
|
private fun getExpectedClockTicksPerSecond(isPrimary: Boolean): Long =
|
|
if (isPrimary) PRIMARY_TICKS else SECONDARY_TICKS
|
|
}
|