tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 0e1f5e708cac33c9240857dacdb5d78eaec9c2f0
parent 6a0b366f7333c6d76cd663a63d50642450bffb46
Author: mcarare <48995920+mcarare@users.noreply.github.com>
Date:   Mon, 17 Nov 2025 09:06:29 +0000

Bug 1996626 - Refactor View extensions to improve testability. r=android-reviewers,avirvara

This patch refactors several view extension functions to extract their core logic into separate, pure functions. This improves the testability of the code by allowing unit tests to directly invoke the logic without needing to mock complex Android View dependencies.

The following functions were updated:
- `increaseTapArea`
- `isKeyboardVisible`
- `getKeyboardHeight`

Differential Revision: https://phabricator.services.mozilla.com/D272434

Diffstat:
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/View.kt | 37+++++++++++++++++++++++++++----------
Mmobile/android/fenix/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt | 73++++++++++++++++++++++++++++++++-----------------------------------------
2 files changed, 59 insertions(+), 51 deletions(-)

diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/View.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/View.kt @@ -24,12 +24,17 @@ import org.mozilla.fenix.components.Components fun View.settings() = context.components.settings fun View.increaseTapArea(@Dimension(unit = DP) extraDps: Int) { - val dips = extraDps.dpToPx(resources.displayMetrics) + val extraPx = extraDps.dpToPx(resources.displayMetrics) + increaseTapAreaInternal(extraPx) +} + +@VisibleForTesting +internal fun View.increaseTapAreaInternal(extraPx: Int) { val parent = this.parent as View parent.post { val touchRect = Rect() getHitRect(touchRect) - touchRect.inset(-dips, -dips) + touchRect.inset(-extraPx, -extraPx) parent.touchDelegate = TouchDelegate(touchRect, this) } } @@ -94,8 +99,13 @@ fun View.getWindowInsets(): WindowInsetsCompat? { */ fun View.isKeyboardVisible(): Boolean { // Since we have insets, we don't need to guess what the keyboard height is. + return isKeyboardVisible(getKeyboardHeight()) +} + +@VisibleForTesting +internal fun isKeyboardVisible(keyboardHeight: Int): Boolean { val minimumKeyboardHeight = 0 - return getKeyboardHeight() > minimumKeyboardHeight + return keyboardHeight > minimumKeyboardHeight } @VisibleForTesting @@ -108,14 +118,21 @@ internal fun View.getWindowVisibleDisplayFrame(): Rect = with(Rect()) { * Calculates the height of the onscreen keyboard. */ fun View.getKeyboardHeight(): Int { - val windowRect = getWindowVisibleDisplayFrame() - val statusBarHeight = windowRect.top - var keyboardHeight = rootView.height - (windowRect.height() + statusBarHeight) - getWindowInsets()?.let { - keyboardHeight -= it.bottom() - } + return getKeyboardHeight( + rootViewHeight = rootView.height, + windowVisibleDisplayFrame = getWindowVisibleDisplayFrame(), + bottomInset = getWindowInsets()?.bottom() ?: 0, + ) +} - return keyboardHeight +@VisibleForTesting +internal fun getKeyboardHeight( + rootViewHeight: Int, + windowVisibleDisplayFrame: Rect, + bottomInset: Int, +): Int { + val statusBarHeight = windowVisibleDisplayFrame.top + return rootViewHeight - (windowVisibleDisplayFrame.height() + statusBarHeight) - bottomInset } /** diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt @@ -5,7 +5,6 @@ package org.mozilla.fenix.ext import android.graphics.Rect -import android.util.DisplayMetrics import android.view.View import android.view.WindowInsets import android.widget.FrameLayout @@ -16,13 +15,12 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.mockk -import io.mockk.mockkStatic import io.mockk.slot import io.mockk.verify -import mozilla.components.support.ktx.android.util.dpToPx import mozilla.components.support.test.robolectric.testContext -import mozilla.components.support.utils.ext.bottom import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -31,24 +29,18 @@ import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class ViewTest { - @MockK private lateinit var view: View + @MockK(relaxed = true) private lateinit var view: View - @MockK private lateinit var parent: FrameLayout - - @MockK private lateinit var displayMetrics: DisplayMetrics + @MockK(relaxed = true) private lateinit var parent: FrameLayout @Before fun setup() { MockKAnnotations.init(this) - mockkStatic("mozilla.components.support.ktx.android.util.DisplayMetricsKt") - mockkStatic("mozilla.components.support.utils.ext.WindowInsetsCompatKt") - mockkStatic("org.mozilla.fenix.ext.ViewKt") every { view.context } answers { testContext } every { view.resources.getDimensionPixelSize(any()) } answers { testContext.resources.getDimensionPixelSize(firstArg()) } - every { view.resources.displayMetrics } returns displayMetrics every { view.parent } returns parent every { parent.touchDelegate = any() } just Runs every { parent.post(any()) } answers { @@ -62,13 +54,12 @@ class ViewTest { @Test fun `test increase touch area`() { val hitRect = Rect(30, 40, 50, 60) - val dp = 10 val px = 20 val outRect = slot<Rect>() - every { dp.dpToPx(displayMetrics) } returns px every { view.getHitRect(capture(outRect)) } answers { outRect.captured.set(hitRect) } - view.increaseTapArea(dp) + view.increaseTapAreaInternal(px) + val expected = Rect(10, 20, 70, 80) assertEquals(expected.left, outRect.captured.left) assertEquals(expected.top, outRect.captured.top) @@ -94,31 +85,31 @@ class ViewTest { val rootInsets: WindowInsets = mockk(relaxed = true) every { view.rootWindowInsets } returns rootInsets - assertEquals(WindowInsetsCompat.toWindowInsetsCompat(rootInsets), view.getWindowInsets()) + // Construct the expected object directly instead of mocking the static method + val expectedInsets = WindowInsetsCompat.toWindowInsetsCompat(rootInsets) + assertEquals(expectedInsets, view.getWindowInsets()) } @Test fun `getKeyboardHeight accounts for status bar and navigation bar`() { - val windowInsetsCompat: WindowInsetsCompat = mockk() + val result = getKeyboardHeight( + rootViewHeight = 1000, + windowVisibleDisplayFrame = Rect(0, 50, 1000, 500), + bottomInset = 50, + ) - every { view.getWindowVisibleDisplayFrame() } returns Rect(0, 50, 1000, 500) - every { view.rootView.height } returns 1000 - every { view.getWindowInsets() } returns windowInsetsCompat - every { windowInsetsCompat.bottom() } returns 50 - - assertEquals(450, view.getKeyboardHeight()) + assertEquals(450, result) } @Test fun `isKeyboardVisible returns false when the keyboard height is 0`() { - every { view.getKeyboardHeight() } returns 0 - assertEquals(false, view.isKeyboardVisible()) + assertFalse(isKeyboardVisible(keyboardHeight = 0)) } @Test fun `isKeyboardVisible returns true when the keyboard height is greater than 0`() { - every { view.getKeyboardHeight() } returns 100 - assertEquals(true, view.isKeyboardVisible()) + // Test the pure logic directly + assertTrue(isKeyboardVisible(keyboardHeight = 100)) } @Test @@ -141,23 +132,23 @@ class ViewTest { @Test fun `getKeyboardHeight returns the keyboard height when keyboard is considered open`() { - val windowVisibleDisplayFrame = Rect(0, 0, 500, 1000) - val keyboardHeight = 500 - every { view.getWindowVisibleDisplayFrame() } returns windowVisibleDisplayFrame - every { view.rootView.height } returns windowVisibleDisplayFrame.bottom.plus(keyboardHeight) - every { view.rootWindowInsets } returns null - - assertEquals(keyboardHeight, view.getKeyboardHeight()) + // Test the pure calculation logic directly + val result = getKeyboardHeight( + rootViewHeight = 1500, + windowVisibleDisplayFrame = Rect(0, 0, 500, 1000), + bottomInset = 0, + ) + assertEquals(500, result) } @Test fun `getKeyboardHeight returns zero when keyboard is considered closed`() { - val windowVisibleDisplayFrame = Rect(0, 0, 500, 1000) - val keyboardHeight = 0 - every { view.getWindowVisibleDisplayFrame() } returns windowVisibleDisplayFrame - every { view.rootView.height } returns windowVisibleDisplayFrame.bottom.plus(keyboardHeight) - every { view.rootWindowInsets } returns null - - assertEquals(keyboardHeight, view.getKeyboardHeight()) + // Test the pure calculation logic directly + val result = getKeyboardHeight( + rootViewHeight = 1000, + windowVisibleDisplayFrame = Rect(0, 0, 500, 1000), + bottomInset = 0, + ) + assertEquals(0, result) } }