chromium/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java

// Copyright 2014 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.chrome.browser.accessibility;

import android.content.Context;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;

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.shared_preferences.SharedPreferencesManager;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.test.ChromeBrowserTestRule;
import org.chromium.components.browser_ui.accessibility.FontSizePrefs;
import org.chromium.components.browser_ui.accessibility.FontSizePrefs.FontSizePrefsObserver;

/**
 * Tests for {@link FontSizePrefs}.
 *
 * <p>TODO(crbug.com/40214849): This tests the class in //components/browser_ui, but we don't have a
 * good way of testing with native code there.
 */
@RunWith(BaseJUnit4ClassRunner.class)
public class FontSizePrefsTest {
    @Rule public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();

    private static final float EPSILON = 0.001f;
    private FontSizePrefs mFontSizePrefs;

    @Before
    public void setUp() {
        resetSharedPrefs();
        Context context = ApplicationProvider.getApplicationContext();
        mFontSizePrefs = getFontSizePrefs(context);
        setSystemFontScaleForTest(1.0f);
    }

    private void resetSharedPrefs() {
        SharedPreferencesManager prefs = ChromeSharedPreferences.getInstance();
        prefs.removeKey(ChromePreferenceKeys.FONT_USER_SET_FORCE_ENABLE_ZOOM);
        prefs.removeKey(ChromePreferenceKeys.FONT_USER_FONT_SCALE_FACTOR);
    }

    @Test
    @SmallTest
    @Feature({"Accessibility"})
    public void testForceEnableZoom() {
        Assert.assertEquals(false, getForceEnableZoom());

        TestingObserver observer = createAndAddFontSizePrefsObserver();

        setUserFontScaleFactor(1.5f);
        Assert.assertEquals(true, getForceEnableZoom());
        observer.assertConsistent();
        setUserFontScaleFactor(0.7f);
        Assert.assertEquals(false, getForceEnableZoom());
        observer.assertConsistent();

        setForceEnableZoomFromUser(true);
        Assert.assertEquals(true, getForceEnableZoom());
        observer.assertConsistent();
        setUserFontScaleFactor(1.5f);
        Assert.assertEquals(true, getForceEnableZoom());
        observer.assertConsistent();
        setUserFontScaleFactor(0.7f);
        Assert.assertEquals(true, getForceEnableZoom());
        observer.assertConsistent();

        setForceEnableZoomFromUser(false);
        Assert.assertEquals(false, getForceEnableZoom());
        observer.assertConsistent();
        setUserFontScaleFactor(1.5f);
        Assert.assertEquals(true, getForceEnableZoom());
        observer.assertConsistent();
        setUserFontScaleFactor(0.7f);
        Assert.assertEquals(false, getForceEnableZoom());
        observer.assertConsistent();

        // Force enable zoom should depend on fontScaleFactor, not on userFontScaleFactor.
        setSystemFontScaleForTest(2.0f);
        Assert.assertEquals(true, getForceEnableZoom());
        observer.assertConsistent();
        setSystemFontScaleForTest(1.0f);
        Assert.assertEquals(false, getForceEnableZoom());
        observer.assertConsistent();
    }

    @Test
    @SmallTest
    @Feature({"Accessibility"})
    public void testFontScaleFactor() {
        Assert.assertEquals(1f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(1f, getFontScaleFactor(), EPSILON);

        TestingObserver observer = createAndAddFontSizePrefsObserver();

        setUserFontScaleFactor(1.5f);
        Assert.assertEquals(1.5f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(1.5f, getFontScaleFactor(), EPSILON);
        observer.assertConsistent();

        setUserFontScaleFactor(0.7f);
        Assert.assertEquals(0.7f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(0.7f, getFontScaleFactor(), EPSILON);
        observer.assertConsistent();

        // Force enable zoom shouldn't affect font scale factor.
        setForceEnableZoomFromUser(true);
        Assert.assertEquals(0.7f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(0.7f, getFontScaleFactor(), EPSILON);
        observer.assertConsistent();

        setForceEnableZoomFromUser(false);
        Assert.assertEquals(0.7f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(0.7f, getFontScaleFactor(), EPSILON);
        observer.assertConsistent();

        // System font scale should affect fontScaleFactor, but not userFontScaleFactor.
        setSystemFontScaleForTest(1.3f);
        Assert.assertEquals(0.7f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(0.7f * 1.3f, getFontScaleFactor(), EPSILON);
        observer.assertConsistent();

        setUserFontScaleFactor(1.5f);
        Assert.assertEquals(1.5f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(1.5f * 1.3f, getFontScaleFactor(), EPSILON);
        observer.assertConsistent();

        setSystemFontScaleForTest(0.8f);
        Assert.assertEquals(1.5f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(1.5f * 0.8f, getFontScaleFactor(), EPSILON);
        observer.assertConsistent();
    }

    @Test
    @SmallTest
    @Feature({"Accessibility"})
    public void testUpgradeToUserFontScaleFactor() {
        setSystemFontScaleForTest(1.3f);
        setUserFontScaleFactor(1.5f);

        // Delete PREF_USER_FONT_SCALE_FACTOR. This simulates the condition just after upgrading to
        // M51, when userFontScaleFactor was added.
        SharedPreferencesManager prefs = ChromeSharedPreferences.getInstance();
        prefs.removeKey(ChromePreferenceKeys.FONT_USER_FONT_SCALE_FACTOR);

        // Intial userFontScaleFactor should be set to fontScaleFactor / systemFontScale.
        Assert.assertEquals(1.5f, getUserFontScaleFactor(), EPSILON);
        Assert.assertEquals(1.5f * 1.3f, getFontScaleFactor(), EPSILON);
    }

    private class TestingObserver implements FontSizePrefsObserver {
        private float mUserFontScaleFactor = getUserFontScaleFactor();
        private float mFontScaleFactor = getFontScaleFactor();
        private boolean mForceEnableZoom = getForceEnableZoom();

        @Override
        public void onFontScaleFactorChanged(float fontScaleFactor, float userFontScaleFactor) {
            mFontScaleFactor = fontScaleFactor;
            mUserFontScaleFactor = userFontScaleFactor;
        }

        @Override
        public void onForceEnableZoomChanged(boolean enabled) {
            mForceEnableZoom = enabled;
        }

        private void assertConsistent() {
            ThreadUtils.runOnUiThreadBlocking(
                    () -> {
                        Assert.assertEquals(
                                getUserFontScaleFactor(), mUserFontScaleFactor, EPSILON);
                        Assert.assertEquals(getFontScaleFactor(), mFontScaleFactor, EPSILON);
                        Assert.assertEquals(getForceEnableZoom(), mForceEnableZoom);
                    });
        }
    }

    private FontSizePrefs getFontSizePrefs(final Context context) {
        return ThreadUtils.runOnUiThreadBlocking(
                () -> FontSizePrefs.getInstance(ProfileManager.getLastUsedRegularProfile()));
    }

    private TestingObserver createAndAddFontSizePrefsObserver() {
        return ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    TestingObserver observer = new TestingObserver();
                    mFontSizePrefs.addObserver(observer);
                    return observer;
                });
    }

    private void setUserFontScaleFactor(final float fontsize) {
        ThreadUtils.runOnUiThreadBlocking(() -> mFontSizePrefs.setUserFontScaleFactor(fontsize));
    }

    private float getUserFontScaleFactor() {
        return ThreadUtils.runOnUiThreadBlocking(() -> mFontSizePrefs.getUserFontScaleFactor());
    }

    private float getFontScaleFactor() {
        return ThreadUtils.runOnUiThreadBlocking(() -> mFontSizePrefs.getFontScaleFactor());
    }

    private void setForceEnableZoomFromUser(final boolean enabled) {
        ThreadUtils.runOnUiThreadBlocking(() -> mFontSizePrefs.setForceEnableZoomFromUser(enabled));
    }

    private boolean getForceEnableZoom() {
        return ThreadUtils.runOnUiThreadBlocking(() -> mFontSizePrefs.getForceEnableZoom());
    }

    private void setSystemFontScaleForTest(final float systemFontScale) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mFontSizePrefs.setSystemFontScaleForTest(systemFontScale);
                    mFontSizePrefs.onSystemFontScaleChanged();
                });
    }
}