chromium/content/public/android/javatests/src/org/chromium/content/browser/VSyncPausedTest.java

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.content.browser;

import androidx.test.filters.MediumTest;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.content_shell_apk.ContentShellActivity;
import org.chromium.content_shell_apk.ContentShellActivityTestRule;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/** Tests pausing the VSync loop for a WindowAndroid. */
@RunWith(BaseJUnit4ClassRunner.class)
public class VSyncPausedTest {
    @Rule
    public ContentShellActivityTestRule mActivityTestRule = new ContentShellActivityTestRule();

    private static final String VSYNC_HTML = "content/test/data/android/vsync.html";
    private static final String CALL_RAF = "window.requestAnimationFrame(onAnimationFrame);";

    private CallbackHelper mOnTitleUpdatedHelper;
    private String mTitle;

    private WebContentsObserver mObserver;
    private ContentShellActivity mActivity;

    @Before
    public void setUp() throws Exception {
        mActivity =
                mActivityTestRule.launchContentShellWithUrl(
                        UrlUtils.getIsolatedTestFileUrl(VSYNC_HTML));
        mActivityTestRule.waitForActiveShellToBeDoneLoading();
        final WebContents webContents = mActivity.getActiveWebContents();
        mObserver =
                ThreadUtils.runOnUiThreadBlocking(
                        () ->
                                new WebContentsObserver(webContents) {
                                    @Override
                                    public void titleWasSet(String title) {
                                        mTitle = title;
                                        mOnTitleUpdatedHelper.notifyCalled();
                                    }
                                });
        mOnTitleUpdatedHelper = new CallbackHelper();
    }

    @After
    public void tearDown() {
        ThreadUtils.runOnUiThreadBlocking(() -> mObserver.destroy());
    }

    @Test
    @MediumTest
    public void testPauseVSync() throws Throwable {
        int callCount = mOnTitleUpdatedHelper.getCallCount();
        JavaScriptUtils.executeJavaScriptAndWaitForResult(
                mActivity.getActiveWebContents(), CALL_RAF);
        mOnTitleUpdatedHelper.waitForCallback(callCount);
        Assert.assertEquals("1", mTitle);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mActivity
                            .getActiveShell()
                            .getWebContents()
                            .getTopLevelNativeWindow()
                            .setVSyncPaused(true);
                });
        callCount = mOnTitleUpdatedHelper.getCallCount();
        JavaScriptUtils.executeJavaScriptAndWaitForResult(
                mActivity.getActiveWebContents(), CALL_RAF);
        try {
            mOnTitleUpdatedHelper.waitForCallback(callCount, 1, 1, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            // Timeout is expected.
        }
        // There may be a VSync already propagating before we pause VSync, so we may receive a
        // single extra VSync.
        String expected = "2";
        if (mTitle.equals("2")) {
            expected = "3";
            callCount = mOnTitleUpdatedHelper.getCallCount();
            // Make sure we don't receive another extra VSync.
            JavaScriptUtils.executeJavaScriptAndWaitForResult(
                    mActivity.getActiveWebContents(), CALL_RAF);
            try {
                mOnTitleUpdatedHelper.waitForCallback(callCount, 1, 1, TimeUnit.SECONDS);
            } catch (TimeoutException e) {
                // Timeout is expected.
            }
            Assert.assertEquals("2", mTitle);
        }
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mActivity
                            .getActiveShell()
                            .getWebContents()
                            .getTopLevelNativeWindow()
                            .setVSyncPaused(false);
                });
        mOnTitleUpdatedHelper.waitForCallback(callCount);
        Assert.assertEquals(expected, mTitle);
    }
}