/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.react.modules.timing; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ExecutorToken; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.CatalystInstance; import com.facebook.react.bridge.JavaOnlyArray; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.common.SystemClock; import com.facebook.react.modules.core.ChoreographerCompat; import com.facebook.react.modules.core.JSTimersExecution; import com.facebook.react.modules.core.ReactChoreographer; import com.facebook.react.modules.core.Timing; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.modules.junit4.rule.PowerMockRule; import org.robolectric.RobolectricTestRunner; import static org.mockito.Mockito.*; /** * Tests for {@link Timing}. */ // DISABLED, BROKEN https://circleci.com/gh/facebook/react-native/12068 // t=13905097 @PrepareForTest({Arguments.class, SystemClock.class, ReactChoreographer.class}) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) @RunWith(RobolectricTestRunner.class) public class TimingModuleTest { private static final long FRAME_TIME_NS = 17 * 1000 * 1000; // 17 ms private Timing mTiming; private ReactChoreographer mReactChoreographerMock; private PostFrameCallbackHandler mPostFrameCallbackHandler; private PostFrameIdleCallbackHandler mIdlePostFrameCallbackHandler; private long mCurrentTimeNs; private JSTimersExecution mJSTimersMock; private ExecutorToken mExecutorTokenMock; @Rule public PowerMockRule rule = new PowerMockRule(); @Before public void prepareModules() { PowerMockito.mockStatic(Arguments.class); when(Arguments.createArray()).thenAnswer( new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return new JavaOnlyArray(); } }); PowerMockito.mockStatic(SystemClock.class); when(SystemClock.uptimeMillis()).thenReturn(mCurrentTimeNs / 1000000); when(SystemClock.currentTimeMillis()).thenReturn(mCurrentTimeNs / 1000000); when(SystemClock.nanoTime()).thenReturn(mCurrentTimeNs); mReactChoreographerMock = mock(ReactChoreographer.class); PowerMockito.mockStatic(ReactChoreographer.class); when(ReactChoreographer.getInstance()).thenReturn(mReactChoreographerMock); CatalystInstance reactInstance = mock(CatalystInstance.class); ReactApplicationContext reactContext = mock(ReactApplicationContext.class); when(reactContext.getCatalystInstance()).thenReturn(reactInstance); mCurrentTimeNs = 0; mPostFrameCallbackHandler = new PostFrameCallbackHandler(); mIdlePostFrameCallbackHandler = new PostFrameIdleCallbackHandler(); doAnswer(mPostFrameCallbackHandler) .when(mReactChoreographerMock) .postFrameCallback( eq(ReactChoreographer.CallbackType.TIMERS_EVENTS), any(ChoreographerCompat.FrameCallback.class)); doAnswer(mIdlePostFrameCallbackHandler) .when(mReactChoreographerMock) .postFrameCallback( eq(ReactChoreographer.CallbackType.IDLE_EVENT), any(ChoreographerCompat.FrameCallback.class)); mTiming = new Timing(reactContext, mock(DevSupportManager.class)); mJSTimersMock = mock(JSTimersExecution.class); mExecutorTokenMock = mock(ExecutorToken.class); when(reactContext.getJSModule(mExecutorTokenMock, JSTimersExecution.class)).thenReturn(mJSTimersMock); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { ((Runnable) invocation.getArguments()[0]).run(); return null; } }).when(reactContext).runOnJSQueueThread(any(Runnable.class)); mTiming.initialize(); } private void stepChoreographerFrame() { ChoreographerCompat.FrameCallback callback = mPostFrameCallbackHandler.getAndResetFrameCallback(); ChoreographerCompat.FrameCallback idleCallback = mIdlePostFrameCallbackHandler.getAndResetFrameCallback(); mCurrentTimeNs += FRAME_TIME_NS; when(SystemClock.uptimeMillis()).thenReturn(mCurrentTimeNs / 1000000); if (callback != null) { callback.doFrame(mCurrentTimeNs); } if (idleCallback != null) { idleCallback.doFrame(mCurrentTimeNs); } } @Test public void testSimpleTimer() { mTiming.onHostResume(); mTiming.createTimer(mExecutorTokenMock, 1, 1, 0, false); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(1)); reset(mJSTimersMock); stepChoreographerFrame(); verifyNoMoreInteractions(mJSTimersMock); } @Test public void testSimpleRecurringTimer() { mTiming.createTimer(mExecutorTokenMock, 100, 1, 0, true); mTiming.onHostResume(); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(100)); reset(mJSTimersMock); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(100)); } @Test public void testCancelRecurringTimer() { mTiming.onHostResume(); mTiming.createTimer(mExecutorTokenMock, 105, 1, 0, true); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(105)); reset(mJSTimersMock); mTiming.deleteTimer(mExecutorTokenMock, 105); stepChoreographerFrame(); verifyNoMoreInteractions(mJSTimersMock); } @Test public void testPausingAndResuming() { mTiming.onHostResume(); mTiming.createTimer(mExecutorTokenMock, 41, 1, 0, true); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); reset(mJSTimersMock); mTiming.onHostPause(); stepChoreographerFrame(); verifyNoMoreInteractions(mJSTimersMock); reset(mJSTimersMock); mTiming.onHostResume(); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); } @Test public void testHeadlessJsTaskInBackground() { mTiming.onHostPause(); mTiming.onHeadlessJsTaskStart(42); mTiming.createTimer(mExecutorTokenMock, 41, 1, 0, true); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); reset(mJSTimersMock); mTiming.onHeadlessJsTaskFinish(42); stepChoreographerFrame(); verifyNoMoreInteractions(mJSTimersMock); } @Test public void testHeadlessJsTaskInForeground() { mTiming.onHostResume(); mTiming.onHeadlessJsTaskStart(42); mTiming.createTimer(mExecutorTokenMock, 41, 1, 0, true); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); reset(mJSTimersMock); mTiming.onHeadlessJsTaskFinish(42); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); reset(mJSTimersMock); mTiming.onHostPause(); verifyNoMoreInteractions(mJSTimersMock); } @Test public void testHeadlessJsTaskIntertwine() { mTiming.onHostResume(); mTiming.onHeadlessJsTaskStart(42); mTiming.createTimer(mExecutorTokenMock, 41, 1, 0, true); mTiming.onHostPause(); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); reset(mJSTimersMock); mTiming.onHostResume(); mTiming.onHeadlessJsTaskFinish(42); stepChoreographerFrame(); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(41)); reset(mJSTimersMock); mTiming.onHostPause(); stepChoreographerFrame(); verifyNoMoreInteractions(mJSTimersMock); } @Test public void testSetTimeoutZero() { mTiming.createTimer(mExecutorTokenMock, 100, 0, 0, false); verify(mJSTimersMock).callTimers(JavaOnlyArray.of(100)); } @Test public void testIdleCallback() { mTiming.onHostResume(); mTiming.setSendIdleEvents(mExecutorTokenMock, true); stepChoreographerFrame(); verify(mJSTimersMock).callIdleCallbacks(SystemClock.currentTimeMillis()); } private static class PostFrameIdleCallbackHandler implements Answer<Void> { private ChoreographerCompat.FrameCallback mFrameCallback; @Override public Void answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); mFrameCallback = (ChoreographerCompat.FrameCallback) args[1]; return null; } public ChoreographerCompat.FrameCallback getAndResetFrameCallback() { ChoreographerCompat.FrameCallback callback = mFrameCallback; mFrameCallback = null; return callback; } } private static class PostFrameCallbackHandler implements Answer<Void> { private ChoreographerCompat.FrameCallback mFrameCallback; @Override public Void answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); mFrameCallback = (ChoreographerCompat.FrameCallback) args[1]; return null; } public ChoreographerCompat.FrameCallback getAndResetFrameCallback() { ChoreographerCompat.FrameCallback callback = mFrameCallback; mFrameCallback = null; return callback; } } }