[fenix] Tab group count telemetry (https://github.com/mozilla-mobile/fenix/pull/22479)
* For https://github.com/mozilla-mobile/fenix/issues/22410 - Refactored tab sorter metrics into a middleware * For https://github.com/mozilla-mobile/fenix/issues/22410 - Created distribution metric for tab group sizes * For https://github.com/mozilla-mobile/fenix/issues/22410 - Created tests for tabs tray middleware * For https://github.com/mozilla-mobile/fenix/issues/22410 - Merge fixes * For https://github.com/mozilla-mobile/fenix/issues/22410 - Added PR number to metric * For https://github.com/mozilla-mobile/fenix/issues/22410 - Fixed unit tests post merge. Added waitUntilIdle to new tests. * For https://github.com/mozilla-mobile/fenix/issues/22410 - Added missing line to middleware to have the Store process actions * For https://github.com/mozilla-mobile/fenix/issues/22410 - Updated metric expiration to December * For https://github.com/mozilla-mobile/fenix/issues/22410 - PR Feedback * For https://github.com/mozilla-mobile/fenix/issues/22410 - Removed else from middleware whenpull/600/head
parent
2f8ad2cd2a
commit
97e59a9717
@ -0,0 +1,71 @@
|
||||
/* 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.tabstray
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import mozilla.components.lib.state.Middleware
|
||||
import mozilla.components.lib.state.MiddlewareContext
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
|
||||
/**
|
||||
* [Middleware] that reacts to various [TabsTrayAction]s.
|
||||
*
|
||||
* @property metrics reference to the configured [MetricController] to record general page load events.
|
||||
*/
|
||||
class TabsTrayMiddleware(
|
||||
private val metrics: MetricController
|
||||
) : Middleware<TabsTrayState, TabsTrayAction> {
|
||||
|
||||
private var shouldReportInactiveTabMetrics: Boolean = true
|
||||
private var shouldReportSearchGroupMetrics: Boolean = true
|
||||
|
||||
override fun invoke(
|
||||
context: MiddlewareContext<TabsTrayState, TabsTrayAction>,
|
||||
next: (TabsTrayAction) -> Unit,
|
||||
action: TabsTrayAction
|
||||
) {
|
||||
next(action)
|
||||
|
||||
when (action) {
|
||||
is TabsTrayAction.UpdateInactiveTabs -> {
|
||||
if (shouldReportInactiveTabMetrics) {
|
||||
shouldReportInactiveTabMetrics = false
|
||||
metrics.track(Event.InactiveTabsCountUpdate(action.tabs.size))
|
||||
metrics.track(Event.TabsTrayHasInactiveTabs(action.tabs.size))
|
||||
}
|
||||
}
|
||||
is TabsTrayAction.UpdateSearchGroupTabs -> {
|
||||
if (shouldReportSearchGroupMetrics) {
|
||||
shouldReportSearchGroupMetrics = false
|
||||
|
||||
metrics.track(Event.SearchTermGroupCount(action.groups.size))
|
||||
|
||||
if (action.groups.isNotEmpty()) {
|
||||
val tabsPerGroup = action.groups.map { it.tabs.size }
|
||||
val averageTabsPerGroup = tabsPerGroup.average()
|
||||
metrics.track(Event.AverageTabsPerSearchTermGroup(averageTabsPerGroup))
|
||||
|
||||
val tabGroupSizeMapping = tabsPerGroup.map { generateTabGroupSizeMappedValue(it) }
|
||||
metrics.track(Event.SearchTermGroupSizeDistribution(tabGroupSizeMapping))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@VisibleForTesting
|
||||
/**
|
||||
* This follows the logic outlined in metrics.yaml for "search_terms.group_size_distribution"
|
||||
*/
|
||||
internal fun generateTabGroupSizeMappedValue(size: Int): Long =
|
||||
when (size) {
|
||||
2 -> 1L
|
||||
in 3..5 -> 2L
|
||||
in 6..10 -> 3L
|
||||
else -> 4L
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/* 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.tabstray
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.support.test.libstate.ext.waitUntilIdle
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.tabstray.browser.TabGroup
|
||||
|
||||
class TabsTrayMiddlewareTest {
|
||||
|
||||
private lateinit var store: TabsTrayStore
|
||||
private lateinit var tabsTrayMiddleware: TabsTrayMiddleware
|
||||
private lateinit var metrics: MetricController
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
metrics = mockk(relaxed = true)
|
||||
tabsTrayMiddleware = TabsTrayMiddleware(
|
||||
metrics
|
||||
)
|
||||
store = TabsTrayStore(
|
||||
middlewares = listOf(tabsTrayMiddleware),
|
||||
initialState = TabsTrayState()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN search term groups are updated AND there is at least one group THEN report the average tabs per group`() {
|
||||
store.dispatch(TabsTrayAction.UpdateSearchGroupTabs(generateSearchTermTabGroupsForAverage()))
|
||||
store.waitUntilIdle()
|
||||
verify { metrics.track(Event.AverageTabsPerSearchTermGroup(5.0)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN search term groups are updated AND there is at least one group THEN report the distribution of tab sizes`() {
|
||||
store.dispatch(TabsTrayAction.UpdateSearchGroupTabs(generateSearchTermTabGroupsForDistribution()))
|
||||
store.waitUntilIdle()
|
||||
verify { metrics.track(Event.SearchTermGroupSizeDistribution(listOf(3L, 2L, 1L, 4L))) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN search term groups are updated THEN report the count of search term tab groups`() {
|
||||
store.dispatch(TabsTrayAction.UpdateSearchGroupTabs(emptyList()))
|
||||
store.waitUntilIdle()
|
||||
verify { metrics.track(Event.SearchTermGroupCount(0)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN inactive tabs are updated THEN report the count of inactive tabs`() {
|
||||
store.dispatch(TabsTrayAction.UpdateInactiveTabs(emptyList()))
|
||||
store.waitUntilIdle()
|
||||
verify { metrics.track(Event.TabsTrayHasInactiveTabs(0)) }
|
||||
verify { metrics.track(Event.InactiveTabsCountUpdate(0)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenerateTabGroupSizeMappedValue() {
|
||||
assertEquals(1L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(2))
|
||||
assertEquals(2L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(3))
|
||||
assertEquals(2L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(4))
|
||||
assertEquals(2L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(5))
|
||||
assertEquals(3L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(6))
|
||||
assertEquals(3L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(7))
|
||||
assertEquals(3L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(8))
|
||||
assertEquals(3L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(9))
|
||||
assertEquals(3L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(10))
|
||||
assertEquals(4L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(11))
|
||||
assertEquals(4L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(12))
|
||||
assertEquals(4L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(20))
|
||||
assertEquals(4L, tabsTrayMiddleware.generateTabGroupSizeMappedValue(50))
|
||||
}
|
||||
|
||||
private fun generateSearchTermTabGroupsForAverage(): List<TabGroup> {
|
||||
val group1 = TabGroup("", mockk(relaxed = true), 0L)
|
||||
val group2 = TabGroup("", mockk(relaxed = true), 0L)
|
||||
val group3 = TabGroup("", mockk(relaxed = true), 0L)
|
||||
|
||||
every { group1.tabs.size } returns 8
|
||||
every { group2.tabs.size } returns 4
|
||||
every { group3.tabs.size } returns 3
|
||||
|
||||
return listOf(group1, group2, group3)
|
||||
}
|
||||
|
||||
private fun generateSearchTermTabGroupsForDistribution(): List<TabGroup> {
|
||||
val group1 = TabGroup("", mockk(relaxed = true), 0L)
|
||||
val group2 = TabGroup("", mockk(relaxed = true), 0L)
|
||||
val group3 = TabGroup("", mockk(relaxed = true), 0L)
|
||||
val group4 = TabGroup("", mockk(relaxed = true), 0L)
|
||||
|
||||
every { group1.tabs.size } returns 8
|
||||
every { group2.tabs.size } returns 4
|
||||
every { group3.tabs.size } returns 2
|
||||
every { group4.tabs.size } returns 12
|
||||
|
||||
return listOf(group1, group2, group3, group4)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue