blob: 3292f72c2dca47d986957223aea57d960246c6d9 [file] [log] [blame]
/*
* Copyright 2023 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc;
import static java.lang.Math.ceil;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import androidx.test.runner.AndroidJUnit4;
import java.time.Duration;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowChoreographer;
@RunWith(AndroidJUnit4.class)
@Config(manifest = Config.NONE)
public class RenderSynchronizerTest {
private static final Duration FRAME_DELAY = Duration.ofMillis(16);
private static final float DEFAULT_FRAME_RATE = 30f;
@Mock private RenderSynchronizer.Listener mockListener;
private RenderSynchronizer renderSynchronizer;
private Duration refreshDelay;
private float frameRate;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowChoreographer.setPaused(true);
}
private void init(float displayRefreshRate, float renderFrameRate) {
refreshDelay = Duration.ofMillis((long)ceil(1000 / displayRefreshRate));
frameRate = renderFrameRate;
ShadowChoreographer.setFrameDelay(refreshDelay);
renderSynchronizer = new RenderSynchronizer(renderFrameRate);
renderSynchronizer.registerListener(mockListener);
}
@Test
public void renderWindowOpensOnFirstFrame() {
init(/* displayRefreshRate= */60f, /* renderFrameRate= */30f);
advanceOneRefreshCycle();
verify(mockListener).onRenderWindowOpen();
}
@Test
public void doubleDisplayRefreshRate() {
init(/* displayRefreshRate= */60f, /* renderFrameRate= */30f);
InOrder inOrder = inOrder(mockListener);
for (int i = 0; i < 10; ++i) {
advanceOneRefreshCycle();
inOrder.verify(mockListener).onRenderWindowOpen();
advanceOneRefreshCycle();
inOrder.verify(mockListener).onRenderWindowClose();
}
}
@Test
public void trippleRefreshRate() {
init(/* displayRefreshRate= */90f, /* renderFrameRate= */30f);
InOrder inOrder = inOrder(mockListener);
for (int i = 0; i < 10; ++i) {
advanceOneRefreshCycle();
inOrder.verify(mockListener).onRenderWindowOpen();
advanceOneRefreshCycle();
inOrder.verify(mockListener).onRenderWindowClose();
advanceOneRefreshCycle();
// No action expected, window stays closed/
}
}
@Test
public void equalRefreshRate() {
init(/* displayRefreshRate= */30f, /* renderFrameRate= */30f);
for (int i = 0; i < 10; ++i) {
advanceOneRefreshCycle();
}
verify(mockListener, times(10)).onRenderWindowOpen();
verify(mockListener, times(0)).onRenderWindowClose();
}
@Test
public void halfRefreshRate() {
init(/* displayRefreshRate= */30f, /* renderFrameRate= */60f);
for (int i = 0; i < 10; ++i) {
advanceOneRefreshCycle();
}
verify(mockListener, times(10)).onRenderWindowOpen();
verify(mockListener, times(0)).onRenderWindowClose();
}
private void advanceOneRefreshCycle() {
advanceBy(refreshDelay);
}
private void advanceBy(Duration duration) {
ShadowLooper.idleMainLooper(duration.toMillis(), MILLISECONDS);
}
}