Revert D52033328: Convert the last Unit Tests to Kotlin

Differential Revision:
D52033328

Original commit changeset: fabe19f88129

Original Phabricator Diff: D52033328

fbshipit-source-id: 3186196dd6df1bd720164894828f8fd1c9215710
This commit is contained in:
Qian Wu
2023-12-11 08:07:55 -08:00
committed by Facebook GitHub Bot
parent 9f28616650
commit 6b532c7ec4
9 changed files with 538 additions and 505 deletions
@@ -96,7 +96,7 @@ public class ReactHostImpl implements ReactHost {
private final Context mContext;
private final ReactHostDelegate mReactHostDelegate;
@Nullable private final ComponentFactory mComponentFactory;
private final ComponentFactory mComponentFactory;
private final ReactJsExceptionHandler mReactJsExceptionHandler;
private final DevSupportManager mDevSupportManager;
private final Executor mBGExecutor;
@@ -137,7 +137,7 @@ public class ReactHostImpl implements ReactHost {
public ReactHostImpl(
Context context,
ReactHostDelegate delegate,
@Nullable ComponentFactory componentFactory,
ComponentFactory componentFactory,
boolean allowPackagerServerAccess,
ReactJsExceptionHandler reactJsExceptionHandler,
boolean useDevSupport) {
@@ -155,7 +155,7 @@ public class ReactHostImpl implements ReactHost {
public ReactHostImpl(
Context context,
ReactHostDelegate delegate,
@Nullable ComponentFactory componentFactory,
ComponentFactory componentFactory,
Executor bgExecutor,
Executor uiExecutor,
ReactJsExceptionHandler reactJsExceptionHandler,
@@ -0,0 +1,71 @@
/*
* 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 static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import android.app.Activity;
import android.content.Context;
import com.facebook.react.bridge.JSIModuleType;
import com.facebook.react.uimanager.UIManagerModule;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
/** Tests {@link BridgelessReactContext} */
@RunWith(RobolectricTestRunner.class)
public class BridgelessReactContextTest {
private Context mContext;
private ReactHostImpl mReactHost;
private BridgelessReactContext mBridgelessReactContext;
@Before
public void setUp() {
mContext = Robolectric.buildActivity(Activity.class).create().get();
mReactHost = Mockito.mock(ReactHostImpl.class);
mBridgelessReactContext = new BridgelessReactContext(mContext, mReactHost);
}
@Test
public void getNativeModuleTest() {
UIManagerModule mUiManagerModule = Mockito.mock(UIManagerModule.class);
doReturn(mUiManagerModule)
.when(mReactHost)
.getNativeModule(ArgumentMatchers.<Class<UIManagerModule>>any());
UIManagerModule uiManagerModule =
mBridgelessReactContext.getNativeModule(UIManagerModule.class);
assertThat(uiManagerModule).isEqualTo(mUiManagerModule);
}
@Test(expected = UnsupportedOperationException.class)
public void getJSIModule_throwsException() {
mBridgelessReactContext.getJSIModule(JSIModuleType.TurboModuleManager);
}
// Disable this test for now due to mocking FabricUIManager fails
// @Test
// public void getJSIModuleTest() {
// FabricUIManager fabricUiManager = Mockito.mock(FabricUIManager.class);
// doReturn(fabricUiManager).when(mReactHost).getUIManager();
// assertThat(mBridgelessReactContext.getJSIModule(JSIModuleType.UIManager))
// .isEqualTo(fabricUiManager);
// }
@Test(expected = UnsupportedOperationException.class)
public void getCatalystInstance_throwsException() {
mBridgelessReactContext.getCatalystInstance();
}
}
@@ -1,70 +0,0 @@
/*
* 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 com.facebook.react.bridge.JSIModuleType
import com.facebook.react.fabric.FabricUIManager
import com.facebook.react.uimanager.UIManagerModule
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.ArgumentMatchers
import org.mockito.Mockito
import org.mockito.Mockito.doReturn
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
/** Tests [BridgelessReactContext] */
@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowSoLoader::class])
class BridgelessReactContextTest {
private lateinit var context: Context
private lateinit var reactHost: ReactHostImpl
private lateinit var bridgelessReactContext: BridgelessReactContext
@Before
fun setUp() {
context = Robolectric.buildActivity(Activity::class.java).create().get()
reactHost = Mockito.mock(ReactHostImpl::class.java)
bridgelessReactContext = BridgelessReactContext(context, reactHost)
}
@Test
fun getNativeModuleTest() {
val mUiManagerModule = Mockito.mock(UIManagerModule::class.java)
doReturn(mUiManagerModule)
.`when`(reactHost)
.getNativeModule(ArgumentMatchers.any<Class<UIManagerModule>>())
val uiManagerModule = bridgelessReactContext.getNativeModule(UIManagerModule::class.java)
Assertions.assertThat(uiManagerModule).isEqualTo(mUiManagerModule)
}
@Test(expected = UnsupportedOperationException::class)
fun getJSIModule_throwsException() {
bridgelessReactContext.getJSIModule(JSIModuleType.TurboModuleManager)
}
@Test
fun getJSIModuleTest() {
val fabricUiManager = Mockito.mock(FabricUIManager::class.java)
doReturn(fabricUiManager).`when`(reactHost).uiManager
Assertions.assertThat(bridgelessReactContext.getJSIModule(JSIModuleType.UIManager))
.isEqualTo(fabricUiManager)
}
@Test(expected = UnsupportedOperationException::class)
fun getCatalystInstance_throwsException() {
// Disable this test for now due to mocking FabricUIManager fails
bridgelessReactContext.catalystInstance
}
}
@@ -0,0 +1,187 @@
/*
* 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 static android.os.Looper.getMainLooper;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import com.facebook.react.MemoryPressureRouter;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.MemoryPressureListener;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.react.interfaces.TaskInterface;
import com.facebook.react.runtime.internal.bolts.TaskCompletionSource;
import com.facebook.react.uimanager.events.BlackHoleEventDispatcher;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.testutils.shadows.ShadowSoLoader;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.LooperMode;
/** Tests {@linkcom.facebook.react.runtime.ReactHostImpl} */
@Ignore("Ignore for now as these tests fail in OSS only")
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowSoLoader.class)
@LooperMode(LooperMode.Mode.PAUSED)
public class ReactHostTest {
private ReactHostDelegate mReactHostDelegate;
private ReactInstance mReactInstance;
private MemoryPressureRouter mMemoryPressureRouter;
private BridgelessDevSupportManager mDevSupportManager;
private JSBundleLoader mJSBundleLoader;
private ReactHostImpl mReactHost;
private ActivityController<Activity> mActivityController;
private ComponentFactory mComponentFactory;
private BridgelessReactContext mBridgelessReactContext;
@Before
public void setUp() throws Exception {
initMocks(this);
mActivityController = Robolectric.buildActivity(Activity.class).create().start().resume();
mReactHostDelegate = mock(ReactHostDelegate.class);
mReactInstance = mock(ReactInstance.class);
mMemoryPressureRouter = mock(MemoryPressureRouter.class);
mDevSupportManager = mock(BridgelessDevSupportManager.class);
mJSBundleLoader = mock(JSBundleLoader.class);
mComponentFactory = mock(ComponentFactory.class);
mBridgelessReactContext = mock(BridgelessReactContext.class);
// TODO This should be replaced with proper mocking once this test is un-ignored
// whenNew(ReactInstance.class).withAnyArguments().thenReturn(mReactInstance);
//
// whenNew(BridgelessReactContext.class).withAnyArguments().thenReturn(mBridgelessReactContext);
// whenNew(MemoryPressureRouter.class).withAnyArguments().thenReturn(mMemoryPressureRouter);
//
// whenNew(BridgelessDevSupportManager.class).withAnyArguments().thenReturn(mDevSupportManager);
doReturn(mJSBundleLoader).when(mReactHostDelegate).getJsBundleLoader();
mReactHost =
new ReactHostImpl(
mActivityController.get().getApplication(),
mReactHostDelegate,
mComponentFactory,
false,
null,
false);
TaskCompletionSource<Boolean> taskCompletionSource = new TaskCompletionSource<>();
taskCompletionSource.setResult(true);
// TODO This should be replaced with proper mocking once this test is un-ignored
// whenNew(TaskCompletionSource.class).withAnyArguments().thenReturn(taskCompletionSource);
}
@Test
public void getEventDispatcher_returnsBlackHoleEventDispatcher() {
EventDispatcher eventDispatcher = mReactHost.getEventDispatcher();
assertThat(eventDispatcher).isInstanceOf(BlackHoleEventDispatcher.class);
}
@Test
public void getUIManager_returnsNullIfNoInstance() {
UIManager uiManager = mReactHost.getUIManager();
assertThat(uiManager).isNull();
}
@Test
public void testGetDevSupportManager() {
assertThat(mReactHost.getDevSupportManager()).isEqualTo(mDevSupportManager);
}
@Test
public void testStart() throws Exception {
doNothing().when(mDevSupportManager).isPackagerRunning(any(PackagerStatusCallback.class));
assertThat(mReactHost.isInstanceInitialized()).isFalse();
waitForTaskUIThread(mReactHost.start());
assertThat(mReactHost.isInstanceInitialized()).isTrue();
assertThat(mReactHost.getCurrentReactContext()).isNotNull();
verify(mMemoryPressureRouter).addMemoryPressureListener((MemoryPressureListener) any());
}
private void startReactHost() throws Exception {
waitForTaskUIThread(mReactHost.start());
}
@Test
public void testDestroy() throws Exception {
startReactHost();
waitForTaskUIThread(mReactHost.destroy("Destroying from testing infra", null));
assertThat(mReactHost.isInstanceInitialized()).isFalse();
assertThat(mReactHost.getCurrentReactContext()).isNull();
}
@Test
public void testReload() throws Exception {
startReactHost();
ReactContext oldReactContext = mReactHost.getCurrentReactContext();
BridgelessReactContext newReactContext = mock(BridgelessReactContext.class);
assertThat(newReactContext).isNotEqualTo(oldReactContext);
// TODO This should be replaced with proper mocking once this test is un-ignored
// whenNew(BridgelessReactContext.class).withAnyArguments().thenReturn(newReactContext);
waitForTaskUIThread(mReactHost.reload("Reload from testing infra"));
assertThat(mReactHost.isInstanceInitialized()).isTrue();
assertThat(mReactHost.getCurrentReactContext()).isNotNull();
assertThat(mReactHost.getCurrentReactContext()).isEqualTo(newReactContext);
assertThat(mReactHost.getCurrentReactContext()).isNotEqualTo(oldReactContext);
}
@Test
public void testLifecycleStateChanges() throws Exception {
startReactHost();
assertThat(mReactHost.getLifecycleState()).isEqualTo(LifecycleState.BEFORE_CREATE);
mReactHost.onHostResume(mActivityController.get());
assertThat(mReactHost.getLifecycleState()).isEqualTo(LifecycleState.RESUMED);
mReactHost.onHostPause(mActivityController.get());
assertThat(mReactHost.getLifecycleState()).isEqualTo(LifecycleState.BEFORE_RESUME);
mReactHost.onHostDestroy(mActivityController.get());
assertThat(mReactHost.getLifecycleState()).isEqualTo(LifecycleState.BEFORE_CREATE);
}
private static <T> void waitForTaskUIThread(TaskInterface<T> task) throws InterruptedException {
boolean isTaskCompleted = false;
while (!isTaskCompleted) {
if (!task.waitForCompletion(4, TimeUnit.MILLISECONDS)) {
shadowOf(getMainLooper()).idle();
} else {
if (task.isCancelled() || task.isFaulted()) {
throw new RuntimeException("Task was cancelled or faulted. Error: " + task.getError());
}
isTaskCompleted = true;
}
}
}
}
@@ -1,203 +0,0 @@
/*
* 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.os.Looper
import com.facebook.react.MemoryPressureRouter
import com.facebook.react.bridge.JSBundleLoader
import com.facebook.react.bridge.MemoryPressureListener
import com.facebook.react.bridge.UIManager
import com.facebook.react.common.LifecycleState
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.devsupport.DisabledDevSupportManager
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback
import com.facebook.react.fabric.ComponentFactory
import com.facebook.react.interfaces.TaskInterface
import com.facebook.react.runtime.internal.bolts.TaskCompletionSource
import com.facebook.react.uimanager.events.BlackHoleEventDispatcher
import com.facebook.testutils.shadows.ShadowSoLoader
import java.util.concurrent.TimeUnit
import org.assertj.core.api.Assertions
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.MockedConstruction
import org.mockito.Mockito
import org.mockito.Mockito.withSettings
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows
import org.robolectric.android.controller.ActivityController
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
/** Tests [ReactHostImpl] */
@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowSoLoader::class])
@LooperMode(LooperMode.Mode.PAUSED)
@OptIn(UnstableReactNativeAPI::class)
class ReactHostTest {
private lateinit var reactHostDelegate: ReactHostDelegate
private lateinit var reactInstance: ReactInstance
private lateinit var memoryPressureRouter: MemoryPressureRouter
private lateinit var jSBundleLoader: JSBundleLoader
private lateinit var reactHost: ReactHostImpl
private lateinit var activityController: ActivityController<Activity>
private lateinit var componentFactory: ComponentFactory
private lateinit var bridgelessReactContext: BridgelessReactContext
private lateinit var mockedReactInstanceCtor: MockedConstruction<ReactInstance>
private lateinit var mockedDevSupportManagerCtor: MockedConstruction<BridgelessDevSupportManager>
private lateinit var mockedBridgelessReactContextCtor: MockedConstruction<BridgelessReactContext>
private lateinit var mockedMemoryPressureRouterCtor: MockedConstruction<MemoryPressureRouter>
private lateinit var mockedTaskCompletionSourceCtor: MockedConstruction<TaskCompletionSource<*>>
@Before
fun setUp() {
activityController = Robolectric.buildActivity(Activity::class.java).create().start().resume()
reactHostDelegate = Mockito.mock(ReactHostDelegate::class.java)
reactInstance = Mockito.mock(ReactInstance::class.java)
memoryPressureRouter = Mockito.mock(MemoryPressureRouter::class.java)
jSBundleLoader = Mockito.mock(JSBundleLoader::class.java)
componentFactory = Mockito.mock(ComponentFactory::class.java)
bridgelessReactContext = Mockito.mock(BridgelessReactContext::class.java)
mockedReactInstanceCtor = Mockito.mockConstruction(ReactInstance::class.java)
mockedDevSupportManagerCtor = Mockito.mockConstruction(BridgelessDevSupportManager::class.java)
mockedBridgelessReactContextCtor = Mockito.mockConstruction(BridgelessReactContext::class.java)
mockedMemoryPressureRouterCtor = Mockito.mockConstruction(MemoryPressureRouter::class.java)
Mockito.doReturn(jSBundleLoader).`when`(reactHostDelegate).jsBundleLoader
reactHost =
ReactHostImpl(
activityController.get().application,
reactHostDelegate,
componentFactory,
false,
{},
false)
val taskCompletionSource = TaskCompletionSource<Boolean>().apply { setResult(true) }
mockedTaskCompletionSourceCtor =
Mockito.mockConstruction(
TaskCompletionSource::class.java, withSettings().useConstructor(taskCompletionSource))
}
@After
fun tearDown() {
mockedReactInstanceCtor.close()
mockedDevSupportManagerCtor.close()
mockedBridgelessReactContextCtor.close()
mockedMemoryPressureRouterCtor.close()
mockedTaskCompletionSourceCtor.close()
}
@Test
fun getEventDispatcher_returnsBlackHoleEventDispatcher() {
val eventDispatcher = reactHost.eventDispatcher
Assertions.assertThat(eventDispatcher).isInstanceOf(BlackHoleEventDispatcher::class.java)
}
@Test
fun getUIManager_returnsNullIfNoInstance() {
val uiManager: UIManager? = reactHost.uiManager
Assertions.assertThat(uiManager).isNull()
}
@Test
fun testGetDevSupportManager() {
// BridgelessDevSupportManager is created only for debug
// we check if it was instantiated or if DisabledDevSupportManager was created (for release).
if (mockedDevSupportManagerCtor.constructed().isNotEmpty()) {
val devSupportManager = mockedDevSupportManagerCtor.constructed().first()
Assertions.assertThat(reactHost.devSupportManager).isEqualTo(devSupportManager)
} else {
Assertions.assertThat(reactHost.devSupportManager)
.isInstanceOf(DisabledDevSupportManager::class.java)
}
}
@Test
@Ignore("Test is currently failing in OSS and needs to be looked into")
fun testStart() {
val devSupportManager = mockedDevSupportManagerCtor.constructed().first()
Mockito.doNothing()
.`when`(devSupportManager)
.isPackagerRunning(ArgumentMatchers.any(PackagerStatusCallback::class.java))
Assertions.assertThat(reactHost.isInstanceInitialized).isFalse()
waitForTaskUIThread(reactHost.start())
Assertions.assertThat(reactHost.isInstanceInitialized).isTrue()
Assertions.assertThat(reactHost.currentReactContext).isNotNull()
Mockito.verify(memoryPressureRouter)
.addMemoryPressureListener(ArgumentMatchers.any<Any>() as MemoryPressureListener)
}
@Test
@Ignore("Test is currently failing in OSS and needs to be looked into")
fun testDestroy() {
startReactHost()
waitForTaskUIThread(reactHost.destroy("Destroying from testing infra", null))
Assertions.assertThat(reactHost.isInstanceInitialized).isFalse()
Assertions.assertThat(reactHost.currentReactContext).isNull()
}
@Test
@Ignore("Test is currently failing in OSS and needs to be looked into")
fun testReload() {
startReactHost()
val oldReactContext = reactHost.currentReactContext
val newReactContext = Mockito.mock(BridgelessReactContext::class.java)
Assertions.assertThat(newReactContext).isNotEqualTo(oldReactContext)
// TODO This should be replaced with proper mocking once this test is un-ignored
// whenNew(BridgelessReactContext.class).withAnyArguments().thenReturn(newReactContext);
waitForTaskUIThread(reactHost.reload("Reload from testing infra"))
Assertions.assertThat(reactHost.isInstanceInitialized).isTrue()
Assertions.assertThat(reactHost.currentReactContext).isNotNull()
Assertions.assertThat(reactHost.currentReactContext).isEqualTo(newReactContext)
Assertions.assertThat(reactHost.currentReactContext).isNotEqualTo(oldReactContext)
}
@Test
@Ignore("Test is currently failing in OSS and needs to be looked into")
fun testLifecycleStateChanges() {
startReactHost()
Assertions.assertThat(reactHost.lifecycleState).isEqualTo(LifecycleState.BEFORE_CREATE)
reactHost.onHostResume(activityController.get())
Assertions.assertThat(reactHost.lifecycleState).isEqualTo(LifecycleState.RESUMED)
reactHost.onHostPause(activityController.get())
Assertions.assertThat(reactHost.lifecycleState).isEqualTo(LifecycleState.BEFORE_RESUME)
reactHost.onHostDestroy(activityController.get())
Assertions.assertThat(reactHost.lifecycleState).isEqualTo(LifecycleState.BEFORE_CREATE)
}
private fun startReactHost() {
waitForTaskUIThread(reactHost.start())
}
companion object {
@Throws(InterruptedException::class)
private fun <T> waitForTaskUIThread(task: TaskInterface<T>) {
var isTaskCompleted = false
while (!isTaskCompleted) {
if (!task.waitForCompletion(4, TimeUnit.MILLISECONDS)) {
Shadows.shadowOf(Looper.getMainLooper()).idle()
} else {
if (task.isCancelled() || task.isFaulted()) {
throw RuntimeException("Task was cancelled or faulted. Error: " + task.getError())
}
isTaskCompleted = true
}
}
}
}
}
@@ -0,0 +1,253 @@
/*
* 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 static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.robolectric.shadows.ShadowInstrumentation.getInstrumentation;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import com.facebook.react.bridge.NativeMap;
import com.facebook.react.interfaces.fabric.SurfaceHandler;
import com.facebook.react.runtime.internal.bolts.Task;
import com.facebook.react.uimanager.events.EventDispatcher;
import java.util.concurrent.Callable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class ReactSurfaceTest {
@Mock ReactHostDelegate mReactHostDelegate;
@Mock EventDispatcher mEventDispatcher;
private ReactHostImpl mReactHost;
private Context mContext;
private ReactSurfaceImpl mReactSurface;
private TestSurfaceHandler mSurfaceHandler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = Robolectric.buildActivity(Activity.class).create().get();
mReactHost = spy(new ReactHostImpl(mContext, mReactHostDelegate, null, false, null, false));
doAnswer(mockedStartSurface()).when(mReactHost).startSurface(any(ReactSurfaceImpl.class));
doAnswer(mockedStartSurface()).when(mReactHost).prerenderSurface(any(ReactSurfaceImpl.class));
doAnswer(mockedStopSurface()).when(mReactHost).stopSurface(any(ReactSurfaceImpl.class));
doReturn(mEventDispatcher).when(mReactHost).getEventDispatcher();
mSurfaceHandler = new TestSurfaceHandler();
mReactSurface = new ReactSurfaceImpl(mSurfaceHandler, mContext);
mReactSurface.attachView(new ReactSurfaceView(mContext, mReactSurface));
}
@Test
public void testAttach() {
assertThat(mReactSurface.getReactHost()).isNull();
mReactSurface.attach(mReactHost);
assertThat(mReactSurface.getReactHost()).isEqualTo(mReactHost);
assertThat(mReactSurface.isAttached()).isTrue();
}
@Test(expected = IllegalStateException.class)
public void testAttachThrowExeption() {
mReactSurface.attach(mReactHost);
mReactSurface.attach(mReactHost);
}
@Test
public void testPrerender() throws InterruptedException {
mReactSurface.attach(mReactHost);
Task<Void> task = (Task<Void>) mReactSurface.prerender();
task.waitForCompletion();
verify(mReactHost).prerenderSurface(mReactSurface);
assertThat(mSurfaceHandler.isRunning).isTrue();
}
@Test
public void testStart() throws InterruptedException {
mReactSurface.attach(mReactHost);
assertThat(mReactHost.isSurfaceAttached(mReactSurface)).isFalse();
Task<Void> task = (Task<Void>) mReactSurface.start();
task.waitForCompletion();
verify(mReactHost).startSurface(mReactSurface);
assertThat(mSurfaceHandler.isRunning).isTrue();
}
@Test
public void testStop() throws InterruptedException {
mReactSurface.attach(mReactHost);
Task<Void> task = (Task<Void>) mReactSurface.start();
task.waitForCompletion();
task = (Task<Void>) mReactSurface.stop();
task.waitForCompletion();
verify(mReactHost).stopSurface(mReactSurface);
}
@Test
public void testClear() {
mReactSurface.getView().addView(new View(mContext));
mReactSurface.clear();
getInstrumentation().waitForIdleSync();
assertThat(mReactSurface.getView().getId()).isEqualTo(View.NO_ID);
assertThat(mReactSurface.getView().getChildCount()).isEqualTo(0);
}
@Test
public void testGetLayoutSpecs() {
int measureSpecWidth = Integer.MAX_VALUE;
int measureSpecHeight = Integer.MIN_VALUE;
assertThat(mSurfaceHandler.mWidthMeasureSpec).isNotEqualTo(measureSpecWidth);
assertThat(mSurfaceHandler.mHeightMeasureSpec).isNotEqualTo(measureSpecHeight);
mReactSurface.attach(mReactHost);
mReactSurface.updateLayoutSpecs(measureSpecWidth, measureSpecHeight, 2, 3);
assertThat(mSurfaceHandler.mWidthMeasureSpec).isEqualTo(measureSpecWidth);
assertThat(mSurfaceHandler.mHeightMeasureSpec).isEqualTo(measureSpecHeight);
}
@Test
public void testGetEventDispatcher() {
mReactSurface.attach(mReactHost);
assertThat(mReactSurface.getEventDispatcher()).isEqualTo(mEventDispatcher);
}
@Test
public void testStartStopHandlerCalls() throws InterruptedException {
mReactSurface.attach(mReactHost);
assertThat(mReactSurface.isRunning()).isFalse();
Task<Void> task = (Task<Void>) mReactSurface.start();
task.waitForCompletion();
assertThat(mReactSurface.isRunning()).isTrue();
task = (Task<Void>) mReactSurface.stop();
task.waitForCompletion();
assertThat(mReactSurface.isRunning()).isFalse();
}
private Answer<Task<Void>> mockedStartSurface() {
return new Answer<Task<Void>>() {
@Override
public Task<Void> answer(InvocationOnMock invocation) {
return Task.call(
new Callable<Void>() {
@Override
public Void call() {
mSurfaceHandler.start();
return null;
}
});
}
};
}
private Answer<Task<Boolean>> mockedStopSurface() {
return new Answer<Task<Boolean>>() {
@Override
public Task<Boolean> answer(InvocationOnMock invocation) {
return Task.call(
new Callable<Boolean>() {
@Override
public Boolean call() {
mSurfaceHandler.stop();
return true;
}
});
}
};
}
static class TestSurfaceHandler implements SurfaceHandler {
private boolean isRunning = false;
private int mSurfaceId = 0;
private int mHeightMeasureSpec = 0;
private int mWidthMeasureSpec = 0;
@Override
public void start() {
isRunning = true;
}
@Override
public void stop() {
isRunning = false;
}
@Override
public int getSurfaceId() {
return mSurfaceId;
}
@Override
public void setSurfaceId(int surfaceId) {
mSurfaceId = surfaceId;
}
@Override
public boolean isRunning() {
return isRunning;
}
@Override
public String getModuleName() {
return null;
}
@Override
public void setMountable(boolean mountable) {
// no-op
}
@Override
public void setLayoutConstraints(
int widthMeasureSpec,
int heightMeasureSpec,
int offsetX,
int offsetY,
boolean doLeftAndRightSwapInRTL,
boolean isRTL,
float pixelDensity) {
mWidthMeasureSpec = widthMeasureSpec;
mHeightMeasureSpec = heightMeasureSpec;
}
@Override
public void setProps(NativeMap props) {
// no-op
}
}
}
@@ -1,212 +0,0 @@
/*
* 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.bridge.NativeMap
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.interfaces.fabric.SurfaceHandler
import com.facebook.react.runtime.internal.bolts.Task
import com.facebook.react.uimanager.events.EventDispatcher
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.ArgumentMatchers
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.stubbing.Answer
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])
class ReactSurfaceTest {
private lateinit var reactHostDelegate: ReactHostDelegate
private lateinit var eventDispatcher: EventDispatcher
private lateinit var reactHost: ReactHostImpl
private lateinit var context: Context
private lateinit var reactSurface: ReactSurfaceImpl
private lateinit var surfaceHandler: TestSurfaceHandler
@Before
fun setUp() {
reactHostDelegate = mock(ReactHostDelegate::class.java)
eventDispatcher = mock(EventDispatcher::class.java)
context = Robolectric.buildActivity(Activity::class.java).create().get()
reactHost = Mockito.spy(ReactHostImpl(context, reactHostDelegate, null, false, {}, false))
Mockito.doAnswer(mockedStartSurface())
.`when`(reactHost)
.startSurface(ArgumentMatchers.any(ReactSurfaceImpl::class.java))
Mockito.doAnswer(mockedStartSurface())
.`when`(reactHost)
.prerenderSurface(ArgumentMatchers.any(ReactSurfaceImpl::class.java))
Mockito.doAnswer(mockedStopSurface())
.`when`(reactHost)
.stopSurface(ArgumentMatchers.any(ReactSurfaceImpl::class.java))
Mockito.doReturn(eventDispatcher).`when`(reactHost).eventDispatcher
surfaceHandler = TestSurfaceHandler()
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<Void>
task.waitForCompletion()
Mockito.verify(reactHost).prerenderSurface(reactSurface)
Assertions.assertThat(surfaceHandler.isRunning).isTrue()
}
@Test
@Throws(InterruptedException::class)
fun testStart() {
reactSurface.attach(reactHost)
Assertions.assertThat(reactHost.isSurfaceAttached(reactSurface)).isFalse()
val task = reactSurface.start() as Task<Void>
task.waitForCompletion()
Mockito.verify(reactHost).startSurface(reactSurface)
Assertions.assertThat(surfaceHandler.isRunning).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()
Mockito.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(0)
}
@Test
fun testGetLayoutSpecs() {
val measureSpecWidth = Int.MAX_VALUE
val measureSpecHeight = Int.MIN_VALUE
Assertions.assertThat(surfaceHandler.widthMeasureSpec).isNotEqualTo(measureSpecWidth)
Assertions.assertThat(surfaceHandler.heightMeasureSpec).isNotEqualTo(measureSpecHeight)
reactSurface.attach(reactHost)
reactSurface.updateLayoutSpecs(measureSpecWidth, measureSpecHeight, 2, 3)
Assertions.assertThat(surfaceHandler.widthMeasureSpec).isEqualTo(measureSpecWidth)
Assertions.assertThat(surfaceHandler.heightMeasureSpec).isEqualTo(measureSpecHeight)
}
@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(): Answer<Task<Void>> {
return Answer {
Task.call {
surfaceHandler.start()
null
}
}
}
private fun mockedStopSurface(): Answer<Task<Boolean>> {
return Answer {
Task.call {
surfaceHandler.stop()
true
}
}
}
internal class TestSurfaceHandler : SurfaceHandler {
override var isRunning = false
override val moduleName: String = "TestSurfaceHandler"
override val surfaceId
get() = _surfaceId
private var _surfaceId = 0
var heightMeasureSpec = 0
var widthMeasureSpec = 0
override fun start() {
isRunning = true
}
override fun stop() {
isRunning = false
}
override fun setMountable(mountable: Boolean) {
// no-op
}
override fun setLayoutConstraints(
widthMeasureSpec: Int,
heightMeasureSpec: Int,
offsetX: Int,
offsetY: Int,
doLeftAndRightSwapInRTL: Boolean,
isRTL: Boolean,
pixelDensity: Float
) {
this.widthMeasureSpec = widthMeasureSpec
this.heightMeasureSpec = heightMeasureSpec
}
override fun setProps(props: NativeMap) {
// no-op
}
override fun setSurfaceId(surfaceId: Int) {
_surfaceId = surfaceId
}
}
}
@@ -0,0 +1,24 @@
/*
* 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 org.mockito.configuration;
/**
* Disables the Mockito cache to prevent Mockito & Robolectric bugs.
*
* <p>Mockito loads this with reflection, so this class might appear unused.
*/
@SuppressWarnings("unused")
public class MockitoConfiguration extends DefaultMockitoConfiguration {
/* (non-Javadoc)
* @see org.mockito.configuration.IMockitoConfiguration#enableClassCache()
*/
public boolean enableClassCache() {
return false;
}
}
@@ -1,17 +0,0 @@
/*
* 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 org.mockito.configuration
/**
* Disables the Mockito cache to prevent Mockito & Robolectric bugs. Mockito loads this with
* reflection, so this class might appear unused.
*/
@Suppress("unused")
class MockitoConfiguration : DefaultMockitoConfiguration() {
override fun enableClassCache(): Boolean = false
}