chromium/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java

// Copyright 2019 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.search_engines.settings;

import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.test.filters.SmallTest;

import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
import org.chromium.chrome.browser.settings.MainSettings;
import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
import org.chromium.chrome.test.ChromeBrowserTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
import org.chromium.components.policy.test.annotations.Policies;
import org.chromium.components.search_engines.TemplateUrl;
import org.chromium.components.search_engines.TemplateUrlService;
import org.chromium.components.search_engines.TemplateUrlService.LoadListener;

import java.util.List;

/** Tests for Search Engine Settings. */
@RunWith(ChromeJUnit4ClassRunner.class)
public class SearchEngineSettingsTest {
    private final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();

    private final SettingsActivityTestRule<SearchEngineSettings> mSearchEngineSettingsTestRule =
            new SettingsActivityTestRule<>(SearchEngineSettings.class);

    private final SettingsActivityTestRule<MainSettings> mMainSettingsTestRule =
            new SettingsActivityTestRule<>(MainSettings.class);

    // We need to destroy the SettingsActivity before tearing down the mock sign-in environment
    // setup in ChromeBrowserTestRule to avoid code crash.
    @Rule
    public final RuleChain mRuleChain =
            RuleChain.outerRule(mBrowserTestRule)
                    .around(mMainSettingsTestRule)
                    .around(mSearchEngineSettingsTestRule);

    private TemplateUrlService mTemplateUrlService;

    /** Change search engine and make sure it works correctly. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisableIf.Build(hardware_is = "sprout", message = "crashes on android-one: crbug.com/540720")
    public void testSearchEnginePreference() throws Exception {
        ensureTemplateUrlServiceLoaded();

        mSearchEngineSettingsTestRule.startSettingsActivity();

        // Set the second search engine as the default using TemplateUrlService.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SearchEngineSettings pref = mSearchEngineSettingsTestRule.getFragment();
                    pref.setValueForTesting("1");

                    // Ensure that the second search engine in the list is selected.
                    Assert.assertNotNull(pref);
                    Assert.assertEquals("1", pref.getValueForTesting());

                    // Simulate selecting the third search engine, ensure that TemplateUrlService is
                    // updated.
                    String keyword2 = pref.setValueForTesting("2");
                    Assert.assertEquals(
                            keyword2,
                            mTemplateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());

                    // Simulate selecting the fourth search engine.
                    String keyword3 = pref.getKeywordFromIndexForTesting(3);
                    String url = mTemplateUrlService.getSearchEngineUrlFromTemplateUrl(keyword3);
                    keyword3 = pref.setValueForTesting("3");
                    Assert.assertEquals(
                            keyword3,
                            mTemplateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
                });
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({@Policies.Item(key = "DefaultSearchProviderEnabled", string = "false")})
    public void testSearchEnginePreference_DisabledIfNoDefaultSearchEngine() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    ChromeBrowserInitializer.getInstance().handleSynchronousStartup();
                });

        ensureTemplateUrlServiceLoaded();
        CriteriaHelper.pollUiThread(() -> mTemplateUrlService.isDefaultSearchManaged());

        mMainSettingsTestRule.startSettingsActivity();

        final MainSettings mainSettings = mMainSettingsTestRule.getFragment();

        final Preference searchEnginePref =
                waitForPreference(mainSettings, MainSettings.PREF_SEARCH_ENGINE);

        CriteriaHelper.pollUiThread(
                () -> {
                    Criteria.checkThat(searchEnginePref.getFragment(), Matchers.nullValue());
                });
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    ManagedPreferenceDelegate managedPrefDelegate =
                            mainSettings.getManagedPreferenceDelegateForTest();
                    Assert.assertTrue(
                            managedPrefDelegate.isPreferenceControlledByPolicy(searchEnginePref));
                });
    }

    /**
     * Make sure that when a user switches to a search engine that uses HTTP, the location
     * permission is not added.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisabledTest(message = "crbug.com/540706")
    @DisableIf.Build(hardware_is = "sprout", message = "fails on android-one: crbug.com/540706")
    public void testSearchEnginePreferenceHttp() throws Exception {
        ensureTemplateUrlServiceLoaded();

        mSearchEngineSettingsTestRule.startSettingsActivity();

        // Set the first search engine as the default using TemplateUrlService.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SearchEngineSettings pref = mSearchEngineSettingsTestRule.getFragment();
                    pref.setValueForTesting("0");
                });

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    // Ensure that the first search engine in the list is selected.
                    SearchEngineSettings pref = mSearchEngineSettingsTestRule.getFragment();
                    Assert.assertNotNull(pref);
                    Assert.assertEquals("0", pref.getValueForTesting());

                    // Simulate selecting a search engine that uses HTTP.
                    int index = indexOfFirstHttpSearchEngine(pref);
                    String keyword = pref.setValueForTesting(Integer.toString(index));

                    Assert.assertEquals(
                            keyword,
                            mTemplateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
                });
    }

    private int indexOfFirstHttpSearchEngine(SearchEngineSettings pref) {
        List<TemplateUrl> urls = mTemplateUrlService.getTemplateUrls();
        int index;
        for (index = 0; index < urls.size(); ++index) {
            String keyword = pref.getKeywordFromIndexForTesting(index);
            String url = mTemplateUrlService.getSearchEngineUrlFromTemplateUrl(keyword);
            if (url.startsWith("http:")) {
                return index;
            }
        }
        Assert.fail();
        return index;
    }

    private void ensureTemplateUrlServiceLoaded() throws Exception {
        // Make sure the template_url_service is loaded.
        final CallbackHelper onTemplateUrlServiceLoadedHelper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    if (mTemplateUrlService == null) {
                        mTemplateUrlService =
                                TemplateUrlServiceFactory.getForProfile(
                                        ProfileManager.getLastUsedRegularProfile());
                    }
                    if (mTemplateUrlService.isLoaded()) {
                        onTemplateUrlServiceLoadedHelper.notifyCalled();
                    } else {
                        mTemplateUrlService.registerLoadListener(
                                new LoadListener() {
                                    @Override
                                    public void onTemplateUrlServiceLoaded() {
                                        onTemplateUrlServiceLoadedHelper.notifyCalled();
                                    }
                                });
                        mTemplateUrlService.load();
                    }
                });
        onTemplateUrlServiceLoadedHelper.waitForCallback(0);
    }

    private static Preference waitForPreference(
            final PreferenceFragmentCompat prefFragment, final String preferenceKey) {
        CriteriaHelper.pollUiThread(
                () -> {
                    Criteria.checkThat(
                            "Expected valid preference for: " + preferenceKey,
                            prefFragment.findPreference(preferenceKey),
                            Matchers.notNullValue());
                });

        return ThreadUtils.runOnUiThreadBlocking(() -> prefFragment.findPreference(preferenceKey));
    }
}