From 59e1fc81e528bd055f5dee1d3ca1f1c7ed40af0a Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 11 Mar 2024 09:09:52 -0700 Subject: [PATCH] Move lifecycle listener inside JavaTimerManager (#43338) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/43338 Reafactor JavaTimerManager so more code is shared between bridge and bridgeless. Note that HeadlessJSTaskContext is not currently configured when using bridgeless. Changelog: [Internal] Reviewed By: rshest Differential Revision: D54496604 fbshipit-source-id: 2a61294267df372e69f8316dd8f8059625d0a2bd --- .../ReactAndroid/api/ReactAndroid.api | 19 +-- .../react/modules/core/JavaTimerManager.java | 13 +- .../react/modules/core/TimingModule.java | 77 +++------- .../facebook/react/runtime/ReactInstance.java | 21 +-- .../react/modules/timing/TimingModuleTest.kt | 141 ++++++++++-------- 5 files changed, 119 insertions(+), 152 deletions(-) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 8eadbcf684f539..24fe604b68b387 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -3032,7 +3032,7 @@ public abstract interface class com/facebook/react/modules/core/JavaScriptTimerE public abstract fun emitTimeDriftWarning (Ljava/lang/String;)V } -public class com/facebook/react/modules/core/JavaTimerManager { +public class com/facebook/react/modules/core/JavaTimerManager : com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/jstasks/HeadlessJsTaskEventListener { public fun (Lcom/facebook/react/bridge/ReactApplicationContext;Lcom/facebook/react/modules/core/JavaScriptTimerExecutor;Lcom/facebook/react/modules/core/ReactChoreographer;Lcom/facebook/react/devsupport/interfaces/DevSupportManager;)V public fun createAndMaybeCallTimer (IIDZ)V public fun createTimer (IJZ)V @@ -3078,27 +3078,18 @@ public final class com/facebook/react/modules/core/ReactChoreographer$CallbackTy public static fun values ()[Lcom/facebook/react/modules/core/ReactChoreographer$CallbackType; } -public final class com/facebook/react/modules/core/TimingModule : com/facebook/fbreact/specs/NativeTimingSpec, com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/jstasks/HeadlessJsTaskEventListener { +public final class com/facebook/react/modules/core/TimingModule : com/facebook/fbreact/specs/NativeTimingSpec, com/facebook/react/modules/core/JavaScriptTimerExecutor { public fun (Lcom/facebook/react/bridge/ReactApplicationContext;Lcom/facebook/react/devsupport/interfaces/DevSupportManager;)V + public fun callIdleCallbacks (D)V + public fun callTimers (Lcom/facebook/react/bridge/WritableArray;)V public fun createTimer (DDDZ)V public fun deleteTimer (D)V + public fun emitTimeDriftWarning (Ljava/lang/String;)V public fun initialize ()V public fun invalidate ()V - public fun onHeadlessJsTaskFinish (I)V - public fun onHeadlessJsTaskStart (I)V - public fun onHostDestroy ()V - public fun onHostPause ()V - public fun onHostResume ()V public fun setSendIdleEvents (Z)V } -public class com/facebook/react/modules/core/TimingModule$BridgeTimerExecutor : com/facebook/react/modules/core/JavaScriptTimerExecutor { - public fun (Lcom/facebook/react/modules/core/TimingModule;)V - public fun callIdleCallbacks (D)V - public fun callTimers (Lcom/facebook/react/bridge/WritableArray;)V - public fun emitTimeDriftWarning (Ljava/lang/String;)V -} - public class com/facebook/react/modules/debug/DevSettingsModule : com/facebook/fbreact/specs/NativeDevSettingsSpec { public fun (Lcom/facebook/react/bridge/ReactApplicationContext;Lcom/facebook/react/devsupport/interfaces/DevSupportManager;)V public fun addListener (Ljava/lang/String;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java index 5f5e1abb8f64d3..95a74b35bfb318 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java @@ -12,12 +12,14 @@ import androidx.annotation.Nullable; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableArray; import com.facebook.react.common.SystemClock; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.jstasks.HeadlessJsTaskContext; +import com.facebook.react.jstasks.HeadlessJsTaskEventListener; import java.util.Comparator; import java.util.PriorityQueue; import java.util.concurrent.atomic.AtomicBoolean; @@ -28,7 +30,7 @@ * *

This is used by the NativeModule {@link TimingModule}. */ -public class JavaTimerManager { +public class JavaTimerManager implements LifecycleEventListener, HeadlessJsTaskEventListener { // These timing constants should be kept in sync with the ones in `JSTimers.js`. // The minimum time in milliseconds left in the frame to call idle callbacks. @@ -196,19 +198,24 @@ public int compare(Timer lhs, Timer rhs) { } }); mTimerIdsToTimers = new SparseArray<>(); + + mReactApplicationContext.addLifecycleEventListener(this); } + @Override public void onHostPause() { isPaused.set(true); clearFrameCallback(); maybeIdleCallback(); } + @Override public void onHostDestroy() { clearFrameCallback(); maybeIdleCallback(); } + @Override public void onHostResume() { isPaused.set(false); // TODO(5195192) Investigate possible problems related to restarting all tasks at the same @@ -217,6 +224,7 @@ public void onHostResume() { maybeSetChoreographerIdleCallback(); } + @Override public void onHeadlessJsTaskStart(int taskId) { if (!isRunningTasks.getAndSet(true)) { setChoreographerCallback(); @@ -224,6 +232,7 @@ public void onHeadlessJsTaskStart(int taskId) { } } + @Override public void onHeadlessJsTaskFinish(int taskId) { HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(mReactApplicationContext); @@ -235,6 +244,8 @@ public void onHeadlessJsTaskFinish(int taskId) { } public void onInstanceDestroy() { + mReactApplicationContext.removeLifecycleEventListener(this); + clearFrameCallback(); clearChoreographerIdleCallback(); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java index 73149ef7374250..af70e66e3ebe2b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java @@ -8,49 +8,16 @@ package com.facebook.react.modules.core; import com.facebook.fbreact.specs.NativeTimingSpec; -import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.WritableArray; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.jstasks.HeadlessJsTaskContext; -import com.facebook.react.jstasks.HeadlessJsTaskEventListener; import com.facebook.react.module.annotations.ReactModule; /** Native module for JS timer execution. Timers fire on frame boundaries. */ @ReactModule(name = NativeTimingSpec.NAME) -public final class TimingModule extends NativeTimingSpec - implements LifecycleEventListener, HeadlessJsTaskEventListener { - - public class BridgeTimerExecutor implements JavaScriptTimerExecutor { - @Override - public void callTimers(WritableArray timerIDs) { - ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); - - if (reactApplicationContext != null) { - reactApplicationContext.getJSModule(JSTimers.class).callTimers(timerIDs); - } - } - - @Override - public void callIdleCallbacks(double frameTime) { - ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); - - if (reactApplicationContext != null) { - reactApplicationContext.getJSModule(JSTimers.class).callIdleCallbacks(frameTime); - } - } - - @Override - public void emitTimeDriftWarning(String warningMessage) { - ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); - - if (reactApplicationContext != null) { - reactApplicationContext.getJSModule(JSTimers.class).emitTimeDriftWarning(warningMessage); - } - } - } - +public final class TimingModule extends NativeTimingSpec implements JavaScriptTimerExecutor { private final JavaTimerManager mJavaTimerManager; public TimingModule(ReactApplicationContext reactContext, DevSupportManager devSupportManager) { @@ -58,18 +25,14 @@ public TimingModule(ReactApplicationContext reactContext, DevSupportManager devS mJavaTimerManager = new JavaTimerManager( - reactContext, - new BridgeTimerExecutor(), - ReactChoreographer.getInstance(), - devSupportManager); + reactContext, this, ReactChoreographer.getInstance(), devSupportManager); } @Override public void initialize() { - getReactApplicationContext().addLifecycleEventListener(this); HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(getReactApplicationContext()); - headlessJsTaskContext.addTaskEventListener(this); + headlessJsTaskContext.addTaskEventListener(mJavaTimerManager); } @Override @@ -97,28 +60,30 @@ public void setSendIdleEvents(final boolean sendIdleEvents) { } @Override - public void onHostResume() { - mJavaTimerManager.onHostResume(); - } + public void callTimers(WritableArray timerIDs) { + ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); - @Override - public void onHostPause() { - mJavaTimerManager.onHostPause(); + if (reactApplicationContext != null) { + reactApplicationContext.getJSModule(JSTimers.class).callTimers(timerIDs); + } } @Override - public void onHostDestroy() { - mJavaTimerManager.onHostDestroy(); - } + public void callIdleCallbacks(double frameTime) { + ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); - @Override - public void onHeadlessJsTaskStart(int taskId) { - mJavaTimerManager.onHeadlessJsTaskStart(taskId); + if (reactApplicationContext != null) { + reactApplicationContext.getJSModule(JSTimers.class).callIdleCallbacks(frameTime); + } } @Override - public void onHeadlessJsTaskFinish(int taskId) { - mJavaTimerManager.onHeadlessJsTaskFinish(taskId); + public void emitTimeDriftWarning(String warningMessage) { + ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); + + if (reactApplicationContext != null) { + reactApplicationContext.getJSModule(JSTimers.class).emitTimeDriftWarning(warningMessage); + } } @Override @@ -127,9 +92,9 @@ public void invalidate() { HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(reactApplicationContext); - headlessJsTaskContext.removeTaskEventListener(this); + headlessJsTaskContext.removeTaskEventListener(mJavaTimerManager); + mJavaTimerManager.onInstanceDestroy(); - reactApplicationContext.removeLifecycleEventListener(this); } @VisibleForTesting diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java index 1a748d6c7298a4..2de6a51fa0ff4b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java @@ -21,7 +21,6 @@ import com.facebook.react.bridge.JSBundleLoader; import com.facebook.react.bridge.JSBundleLoaderDelegate; import com.facebook.react.bridge.JavaScriptContextHolder; -import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeMap; import com.facebook.react.bridge.NativeModule; @@ -143,6 +142,7 @@ final class ReactInstance { if (useDevSupport) { devSupportManager.startInspector(); } + JSTimerExecutor jsTimerExecutor = createJSTimerExecutor(); mJavaTimerManager = new JavaTimerManager( @@ -151,24 +151,6 @@ final class ReactInstance { ReactChoreographer.getInstance(), devSupportManager); - mBridgelessReactContext.addLifecycleEventListener( - new LifecycleEventListener() { - @Override - public void onHostResume() { - mJavaTimerManager.onHostResume(); - } - - @Override - public void onHostPause() { - mJavaTimerManager.onHostPause(); - } - - @Override - public void onHostDestroy() { - mJavaTimerManager.onHostDestroy(); - } - }); - JSRuntimeFactory jsRuntimeFactory = mDelegate.getJsRuntimeFactory(); BindingsInstaller bindingsInstaller = mDelegate.getBindingsInstaller(); // Notify JS if profiling is enabled @@ -448,6 +430,7 @@ public Collection getNativeModules() { mQueueConfiguration.destroy(); mTurboModuleManager.invalidate(); mFabricUIManager.invalidate(); + mJavaTimerManager.onInstanceDestroy(); mHybridData.resetNative(); mComponentNameResolverManager = null; mUIConstantsProviderManager = null; diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.kt index 116f1c122872eb..86011414ab33a7 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.kt @@ -7,14 +7,19 @@ package com.facebook.react.modules.timing +import android.content.Context import android.view.Choreographer.FrameCallback import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.CatalystInstance import com.facebook.react.bridge.JavaOnlyArray +import com.facebook.react.bridge.JavaOnlyMap import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.WritableArray import com.facebook.react.common.SystemClock import com.facebook.react.devsupport.interfaces.DevSupportManager +import com.facebook.react.jstasks.HeadlessJsTaskConfig +import com.facebook.react.jstasks.HeadlessJsTaskContext +import com.facebook.react.modules.appregistry.AppRegistry import com.facebook.react.modules.core.JSTimers import com.facebook.react.modules.core.ReactChoreographer import com.facebook.react.modules.core.ReactChoreographer.CallbackType @@ -27,9 +32,12 @@ import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.MockedStatic +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock import org.mockito.Mockito.mockStatic import org.mockito.Mockito.reset +import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever @@ -43,12 +51,14 @@ class TimingModuleTest { const val FRAME_TIME_NS = 17 * 1000 * 1000 } + private lateinit var reactContext: ReactApplicationContext + private lateinit var headlessContext: HeadlessJsTaskContext private lateinit var timingModule: TimingModule private lateinit var reactChoreographerMock: ReactChoreographer private lateinit var postFrameCallbackHandler: PostFrameCallbackHandler private lateinit var idlePostFrameCallbackHandler: PostFrameCallbackHandler private var currentTimeNs = 0L - private lateinit var jSTimersMock: JSTimers + private lateinit var jsTimersMock: JSTimers private lateinit var arguments: MockedStatic private lateinit var systemClock: MockedStatic private lateinit var reactChoreographer: MockedStatic @@ -82,9 +92,11 @@ class TimingModuleTest { .thenAnswer { reactChoreographerMock } val reactInstance = mock(CatalystInstance::class.java) - val reactContext = mock(ReactApplicationContext::class.java) - whenever(reactContext.catalystInstance).thenReturn(reactInstance) - whenever(reactContext.hasActiveReactInstance()).thenReturn(true) + reactContext = spy(ReactApplicationContext(mock(Context::class.java))) + doReturn(reactInstance).`when`(reactContext).catalystInstance + doReturn(true).`when`(reactContext).hasActiveReactInstance() + + headlessContext = HeadlessJsTaskContext.getInstance(reactContext) postFrameCallbackHandler = PostFrameCallbackHandler() idlePostFrameCallbackHandler = PostFrameCallbackHandler() @@ -95,7 +107,6 @@ class TimingModuleTest { .thenAnswer { return@thenAnswer postFrameCallbackHandler.answer(it) } - whenever( reactChoreographerMock.postFrameCallback( eq(CallbackType.IDLE_EVENT), any(FrameCallback::class.java))) @@ -104,12 +115,17 @@ class TimingModuleTest { } timingModule = TimingModule(reactContext, mock(DevSupportManager::class.java)) - jSTimersMock = mock(JSTimers::class.java) - whenever(reactContext.getJSModule(JSTimers::class.java)).thenReturn(jSTimersMock) - whenever(reactContext.runOnJSQueueThread(any(Runnable::class.java))).thenAnswer { invocation -> - (invocation.arguments[0] as Runnable).run() - return@thenAnswer true - } + jsTimersMock = mock(JSTimers::class.java) + doReturn(jsTimersMock).`when`(reactContext).getJSModule(JSTimers::class.java) + doReturn(mock(AppRegistry::class.java)) + .`when`(reactContext) + .getJSModule(AppRegistry::class.java) + doAnswer({ invocation -> + (invocation.arguments[0] as Runnable).run() + return@doAnswer true + }) + .`when`(reactContext) + .runOnJSQueueThread(any(Runnable::class.java)) timingModule.initialize() } @@ -134,111 +150,113 @@ class TimingModuleTest { @Test fun testSimpleTimer() { - timingModule.onHostResume() + reactContext.onHostResume(null) timingModule.createTimer(1.0, 1.0, 0.0, false) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(1.0)) - reset(jSTimersMock) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(1.0)) + reset(jsTimersMock) stepChoreographerFrame() - verifyNoMoreInteractions(jSTimersMock) + verifyNoMoreInteractions(jsTimersMock) } @Test fun testSimpleRecurringTimer() { timingModule.createTimer(100.0, 1.0, 0.0, true) - timingModule.onHostResume() + reactContext.onHostResume(null) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(100.0)) - reset(jSTimersMock) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(100.0)) + reset(jsTimersMock) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(100.0)) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(100.0)) } @Test fun testCancelRecurringTimer() { - timingModule.onHostResume() + reactContext.onHostResume(null) timingModule.createTimer(105.0, 1.0, 0.0, true) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(105.0)) - reset(jSTimersMock) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(105.0)) + reset(jsTimersMock) timingModule.deleteTimer(105.0) stepChoreographerFrame() - verifyNoMoreInteractions(jSTimersMock) + verifyNoMoreInteractions(jsTimersMock) } @Test fun testPausingAndResuming() { - timingModule.onHostResume() + reactContext.onHostResume(null) timingModule.createTimer(41.0, 1.0, 0.0, true) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(41.0)) - reset(jSTimersMock) - timingModule.onHostPause() + verify(jsTimersMock).callTimers(JavaOnlyArray.of(41.0)) + reset(jsTimersMock) + reactContext.onHostPause() stepChoreographerFrame() - verifyNoMoreInteractions(jSTimersMock) - reset(jSTimersMock) - timingModule.onHostResume() + verifyNoMoreInteractions(jsTimersMock) + reset(jsTimersMock) + reactContext.onHostResume(null) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(41.0)) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(41.0)) } @Test fun testHeadlessJsTaskInBackground() { - timingModule.onHostPause() - timingModule.onHeadlessJsTaskStart(42) + reactContext.onHostPause() + val taskConfig = HeadlessJsTaskConfig("foo", JavaOnlyMap()) + val taskId = headlessContext.startTask(taskConfig) timingModule.createTimer(41.0, 1.0, 0.0, true) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(41.0)) - reset(jSTimersMock) - timingModule.onHeadlessJsTaskFinish(42) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(41.0)) + reset(jsTimersMock) + headlessContext.finishTask(taskId) stepChoreographerFrame() - verifyNoMoreInteractions(jSTimersMock) + verifyNoMoreInteractions(jsTimersMock) } @Test fun testHeadlessJsTaskInForeground() { - timingModule.onHostResume() - timingModule.onHeadlessJsTaskStart(42) + val taskConfig = HeadlessJsTaskConfig("foo", JavaOnlyMap()) + val taskId = headlessContext.startTask(taskConfig) + reactContext.onHostResume(null) timingModule.createTimer(41.0, 1.0, 0.0, true) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(41.0)) - reset(jSTimersMock) - timingModule.onHeadlessJsTaskFinish(42) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(41.0)) + reset(jsTimersMock) + headlessContext.finishTask(taskId) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(41.0)) - reset(jSTimersMock) - timingModule.onHostPause() - verifyNoMoreInteractions(jSTimersMock) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(41.0)) + reset(jsTimersMock) + reactContext.onHostPause() + verifyNoMoreInteractions(jsTimersMock) } @Test fun testHeadlessJsTaskIntertwine() { - timingModule.onHostResume() - timingModule.onHeadlessJsTaskStart(42) timingModule.createTimer(41.0, 1.0, 0.0, true) - timingModule.onHostPause() + reactContext.onHostPause() + val taskConfig = HeadlessJsTaskConfig("foo", JavaOnlyMap()) + val taskId = headlessContext.startTask(taskConfig) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(41.0)) - reset(jSTimersMock) - timingModule.onHostResume() - timingModule.onHeadlessJsTaskFinish(42) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(41.0)) + reset(jsTimersMock) + reactContext.onHostResume(null) + headlessContext.finishTask(taskId) stepChoreographerFrame() - verify(jSTimersMock).callTimers(JavaOnlyArray.of(41.0)) - reset(jSTimersMock) - timingModule.onHostPause() + verify(jsTimersMock).callTimers(JavaOnlyArray.of(41.0)) + reset(jsTimersMock) + reactContext.onHostPause() stepChoreographerFrame() - verifyNoMoreInteractions(jSTimersMock) + verifyNoMoreInteractions(jsTimersMock) } @Test fun testSetTimeoutZero() { timingModule.createTimer(100.0, 0.0, 0.0, false) - verify(jSTimersMock).callTimers(JavaOnlyArray.of(100.0)) + verify(jsTimersMock).callTimers(JavaOnlyArray.of(100.0)) } @Test fun testActiveTimersInRange() { - timingModule.onHostResume() + reactContext.onHostResume(null) assertThat(timingModule.hasActiveTimersInRange(100)).isFalse timingModule.createTimer(41.0, 1.0, 0.0, true) assertThat(timingModule.hasActiveTimersInRange(100)).isFalse // Repeating @@ -250,13 +268,12 @@ class TimingModuleTest { @Test fun testIdleCallback() { timingModule.setSendIdleEvents(true) - timingModule.onHostResume() + reactContext.onHostResume(null) stepChoreographerFrame() - verify(jSTimersMock).callIdleCallbacks(SystemClock.currentTimeMillis().toDouble()) + verify(jsTimersMock).callIdleCallbacks(SystemClock.currentTimeMillis().toDouble()) } private class PostFrameCallbackHandler : Answer { - private var frameCallback: FrameCallback? = null override fun answer(invocation: InvocationOnMock) {