Reland: Add tests for DisplayMetricsHolder (#53165)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53165

This is a re-land of D78981753
Those tests were OOM-ing because we were using a old version of robolectric.
I've bumped it and this should fix it.

Changelog:
[Internal] [Changed] -

Reviewed By: lenaic

Differential Revision: D79883742

fbshipit-source-id: 4c2c640d6b601ec07d0a4a12cd7b86a879740a41
This commit is contained in:
Nicola Corti
2025-08-08 07:09:32 -07:00
committed by Facebook GitHub Bot
parent afb6294335
commit e16def43c9
3 changed files with 170 additions and 2 deletions
@@ -139,6 +139,6 @@ public object DisplayMetricsHolder {
(checkNotNull(screenDisplayMetrics).heightPixels - verticalInsets).toFloat().pxToDp())
}
private fun encodeFloatsToLong(width: Float, height: Float): Long =
internal fun encodeFloatsToLong(width: Float, height: Float): Long =
(width.toRawBits().toLong()) shl 32 or (height.toRawBits().toLong())
}
@@ -0,0 +1,168 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.uimanager
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.util.DisplayMetrics
import android.view.View
import android.view.Window
import android.view.WindowInsets
import com.facebook.react.bridge.WritableMap
import com.facebook.testutils.shadows.ShadowNativeLoader
import com.facebook.testutils.shadows.ShadowNativeMap
import com.facebook.testutils.shadows.ShadowReadableNativeMap
import com.facebook.testutils.shadows.ShadowSoLoader
import com.facebook.testutils.shadows.ShadowWritableNativeMap
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(
shadows =
[
ShadowSoLoader::class,
ShadowNativeLoader::class,
ShadowNativeMap::class,
ShadowWritableNativeMap::class,
ShadowReadableNativeMap::class,
])
class DisplayMetricsHolderTest {
private lateinit var context: Context
private lateinit var displayMetrics: DisplayMetrics
@Before
fun setUp() {
context = RuntimeEnvironment.getApplication()
displayMetrics = context.resources.displayMetrics
DisplayMetricsHolder.setWindowDisplayMetrics(null)
DisplayMetricsHolder.setScreenDisplayMetrics(null)
}
@After
fun tearDown() {
DisplayMetricsHolder.setWindowDisplayMetrics(null)
DisplayMetricsHolder.setScreenDisplayMetrics(null)
}
@Test(expected = IllegalStateException::class)
fun getWindowDisplayMetrics_failsIfDisplayMetricsIsNotInitialized() {
DisplayMetricsHolder.getWindowDisplayMetrics()
}
@Test(expected = IllegalStateException::class)
fun getScreenDisplayMetrics_failsIfDisplayMetricsIsNotInitialized() {
DisplayMetricsHolder.getScreenDisplayMetrics()
}
@Test
fun setAndGetWindowDisplayMetrics_returnsSetValue() {
DisplayMetricsHolder.setWindowDisplayMetrics(displayMetrics)
val result = DisplayMetricsHolder.getWindowDisplayMetrics()
assertThat(result).isEqualTo(displayMetrics)
}
@Test
fun setAndGetScreenDisplayMetrics_returnsSetValue() {
DisplayMetricsHolder.setScreenDisplayMetrics(displayMetrics)
val result = DisplayMetricsHolder.getScreenDisplayMetrics()
assertThat(result).isEqualTo(displayMetrics)
}
@Test
fun initDisplayMetrics_setsMetrics() {
DisplayMetricsHolder.initDisplayMetrics(context)
assertThat(DisplayMetricsHolder.getWindowDisplayMetrics()).isNotNull()
assertThat(DisplayMetricsHolder.getScreenDisplayMetrics()).isNotNull()
}
@Test
fun initDisplayMetricsIfNotInitialized_onlyInitializesOnce() {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
val firstWindow = DisplayMetricsHolder.getWindowDisplayMetrics()
val firstScreen = DisplayMetricsHolder.getScreenDisplayMetrics()
// Should not reinitialize
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
val secondWindow = DisplayMetricsHolder.getWindowDisplayMetrics()
val secondScreen = DisplayMetricsHolder.getScreenDisplayMetrics()
assertThat(secondWindow).isEqualTo(firstWindow)
assertThat(secondScreen).isEqualTo(firstScreen)
}
@Test(expected = IllegalStateException::class)
fun getDisplayMetricsWritableMap_failsIfNotInitialized() {
DisplayMetricsHolder.getDisplayMetricsWritableMap(1.0)
}
@Test
fun getDisplayMetricsWritableMap_returnsCorrectMap() {
// Use the official initialization method to ensure both metrics are set
DisplayMetricsHolder.initDisplayMetrics(context)
val map: WritableMap = DisplayMetricsHolder.getDisplayMetricsWritableMap(1.0)
assertThat(map.hasKey("windowPhysicalPixels")).isTrue()
assertThat(map.hasKey("screenPhysicalPixels")).isTrue()
val windowMap = map.getMap("windowPhysicalPixels")
val screenMap = map.getMap("screenPhysicalPixels")
checkNotNull(windowMap)
checkNotNull(screenMap)
assertThat(windowMap.hasKey("width")).isTrue()
assertThat(windowMap.hasKey("height")).isTrue()
assertThat(windowMap.hasKey("scale")).isTrue()
assertThat(windowMap.hasKey("fontScale")).isTrue()
assertThat(windowMap.hasKey("densityDpi")).isTrue()
}
@Test
@TargetApi(30)
fun getEncodedScreenSizeWithoutVerticalInsets_returnsEncodedValue() {
DisplayMetricsHolder.initDisplayMetrics(context)
val activity: Activity = mock()
val window: Window = mock()
val decorView: View = mock()
val windowInsets: WindowInsets = mock()
whenever(activity.window).thenReturn(window)
whenever(window.decorView).thenReturn(decorView)
whenever(decorView.rootWindowInsets).thenReturn(windowInsets)
whenever(windowInsets.getInsets(anyInt()))
.thenReturn(android.graphics.Insets.of(10, 20, 10, 20))
// Should return a non-zero encoded value
val encoded = DisplayMetricsHolder.getEncodedScreenSizeWithoutVerticalInsets(activity)
assertThat(encoded).isNotZero()
}
@Test
fun getEncodedScreenSizeWithoutVerticalInsets_returnsZeroIfActivityIsNull() {
val encoded = DisplayMetricsHolder.getEncodedScreenSizeWithoutVerticalInsets(null)
assertThat(encoded).isZero()
}
@Test
fun encodeFloatsToLong_encodesWidthAndHeightCorrectly() {
val width = 123.45f
val height = 67.89f
val encoded = DisplayMetricsHolder.encodeFloatsToLong(width, height)
val decodedWidth = Float.fromBits((encoded shr 32).toInt())
val decodedHeight = Float.fromBits(encoded.toInt())
assertThat(decodedWidth).isEqualTo(width)
assertThat(decodedHeight).isEqualTo(height)
}
}
@@ -35,7 +35,7 @@ mockito-kotlin = "3.2.0"
nexus-publish = "2.0.0"
okhttp = "4.9.2"
okio = "2.9.0"
robolectric = "4.9.2"
robolectric = "4.15.1"
soloader = "0.12.1"
uiautomator = "2.3.0"
xstream = "1.4.20"