blob: e3bab925163208c6fc4a72e8a9b46227d71f97f2 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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.preferences;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.ProcessInitException;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.FlakyTest;
import org.chromium.base.test.util.RetryOnFailure;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.accessibility.FontSizePrefs;
import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
import org.chromium.chrome.browser.preferences.website.ContentSettingValues;
import org.chromium.chrome.browser.preferences.website.PermissionInfo;
import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge;
import org.chromium.chrome.browser.search_engines.TemplateUrl;
import org.chromium.chrome.browser.search_engines.TemplateUrlService;
import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener;
import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.ActivityUtils;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.UiUtils;
import org.chromium.policy.test.annotations.Policies;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
/**
* Tests for the Settings menu.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
public class PreferencesTest {
@Rule
public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
/**
* Launches the preferences menu and starts the preferences activity named fragmentName.
* Returns the activity that was started.
*/
public static Preferences startPreferences(Instrumentation instrumentation,
String fragmentName) {
Context context = instrumentation.getTargetContext();
Intent intent = PreferencesLauncher.createIntentForSettingsPage(context, fragmentName);
Activity activity = instrumentation.startActivitySync(intent);
Assert.assertTrue(activity instanceof Preferences);
return (Preferences) activity;
}
public static void clickPreference(PreferenceFragment fragment, Preference preference) {
try {
Method performClick = Preference.class.getDeclaredMethod("performClick",
PreferenceScreen.class);
performClick.invoke(preference, fragment.getPreferenceScreen());
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
/**
* 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")
@RetryOnFailure
public void testSearchEnginePreference() throws Exception {
ensureTemplateUrlServiceLoaded();
final Preferences prefActivity =
startPreferences(InstrumentationRegistry.getInstrumentation(),
SearchEnginePreference.class.getName());
// Set the second search engine as the default using TemplateUrlService.
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
SearchEnginePreference pref =
(SearchEnginePreference) prefActivity.getFragmentForTest();
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, and location permission granted by default for the new engine.
String keyword2 = pref.setValueForTesting("2");
TemplateUrlService templateUrlService = TemplateUrlService.getInstance();
Assert.assertEquals(keyword2,
templateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
Assert.assertEquals(
ContentSettingValues.ALLOW, locationPermissionForSearchEngine(keyword2));
// Simulate selecting the fourth search engine and but set a blocked permission
// first and ensure that location permission is NOT granted.
String keyword3 = pref.getKeywordFromIndexForTesting(3);
String url = templateUrlService.getSearchEngineUrlFromTemplateUrl(keyword3);
WebsitePreferenceBridge.nativeSetGeolocationSettingForOrigin(
url, url, ContentSettingValues.BLOCK, false);
keyword3 = pref.setValueForTesting("3");
Assert.assertEquals(keyword3,
TemplateUrlService.getInstance()
.getDefaultSearchEngineTemplateUrl()
.getKeyword());
Assert.assertEquals(
ContentSettingValues.BLOCK, locationPermissionForSearchEngine(keyword3));
Assert.assertEquals(
ContentSettingValues.ASK, locationPermissionForSearchEngine(keyword2));
// Make sure a pre-existing ALLOW value does not get deleted when switching away
// from a search engine. For this to work we need to change the DSE's content
// setting to allow for search engine 3 before changing to search engine 2.
// Otherwise the block setting will cause the content setting for search engine 2
// to be reset when we switch to it.
WebsitePreferenceBridge.nativeSetGeolocationSettingForOrigin(
url, url, ContentSettingValues.ALLOW, false);
keyword2 = pref.getKeywordFromIndexForTesting(2);
url = templateUrlService.getSearchEngineUrlFromTemplateUrl(keyword2);
WebsitePreferenceBridge.nativeSetGeolocationSettingForOrigin(
url, url, ContentSettingValues.ALLOW, false);
keyword2 = pref.setValueForTesting("2");
Assert.assertEquals(keyword2,
TemplateUrlService.getInstance()
.getDefaultSearchEngineTemplateUrl()
.getKeyword());
Assert.assertEquals(
ContentSettingValues.ALLOW, locationPermissionForSearchEngine(keyword2));
pref.setValueForTesting("3");
Assert.assertEquals(
ContentSettingValues.ALLOW, locationPermissionForSearchEngine(keyword2));
}
});
}
@Test
@SmallTest
@Feature({"Preferences"})
@Policies.Add({ @Policies.Item(key = "DefaultSearchProviderEnabled", string = "false") })
public void testSearchEnginePreference_DisabledIfNoDefaultSearchEngine() throws Exception {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
try {
ChromeBrowserInitializer.getInstance(InstrumentationRegistry.getTargetContext())
.handleSynchronousStartup();
} catch (ProcessInitException e) {
Assert.fail("Unable to initialize process: " + e);
}
}
});
ensureTemplateUrlServiceLoaded();
CriteriaHelper.pollUiThread(Criteria.equals(true, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return TemplateUrlService.getInstance().isDefaultSearchManaged();
}
}));
Preferences preferenceActivity = ActivityUtils.waitForActivity(
InstrumentationRegistry.getInstrumentation(), Preferences.class);
final MainPreferences mainPreferences =
ActivityUtils.waitForFragmentToAttach(preferenceActivity, MainPreferences.class);
final Preference searchEnginePref =
waitForPreference(mainPreferences, MainPreferences.PREF_SEARCH_ENGINE);
CriteriaHelper.pollUiThread(Criteria.equals(null, new Callable<Object>() {
@Override
public Object call() throws Exception {
return searchEnginePref.getFragment();
}
}));
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
ManagedPreferenceDelegate managedPrefDelegate =
mainPreferences.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.
*/
/*
* @SmallTest
* @Feature({"Preferences"})
* BUG=crbug.com/540706
*/
@Test
@FlakyTest
@DisableIf.Build(hardware_is = "sprout", message = "fails on android-one: crbug.com/540706")
public void testSearchEnginePreferenceHttp() throws Exception {
ensureTemplateUrlServiceLoaded();
final Preferences prefActivity =
startPreferences(InstrumentationRegistry.getInstrumentation(),
SearchEnginePreference.class.getName());
// Set the first search engine as the default using TemplateUrlService.
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
SearchEnginePreference pref =
(SearchEnginePreference) prefActivity.getFragmentForTest();
pref.setValueForTesting("0");
}
});
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
// Ensure that the first search engine in the list is selected.
SearchEnginePreference pref =
(SearchEnginePreference) prefActivity.getFragmentForTest();
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));
TemplateUrlService templateUrlService = TemplateUrlService.getInstance();
Assert.assertEquals(keyword,
templateUrlService.getDefaultSearchEngineTemplateUrl().getKeyword());
Assert.assertEquals(
ContentSettingValues.ASK, locationPermissionForSearchEngine(keyword));
}
});
}
private int indexOfFirstHttpSearchEngine(SearchEnginePreference pref) {
TemplateUrlService templateUrlService = TemplateUrlService.getInstance();
List<TemplateUrl> urls = templateUrlService.getTemplateUrls();
int index;
for (index = 0; index < urls.size(); ++index) {
String keyword = pref.getKeywordFromIndexForTesting(index);
String url = templateUrlService.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(new Runnable() {
@Override
public void run() {
if (TemplateUrlService.getInstance().isLoaded()) {
onTemplateUrlServiceLoadedHelper.notifyCalled();
} else {
TemplateUrlService.getInstance().registerLoadListener(new LoadListener() {
@Override
public void onTemplateUrlServiceLoaded() {
onTemplateUrlServiceLoadedHelper.notifyCalled();
}
});
TemplateUrlService.getInstance().load();
}
}
});
onTemplateUrlServiceLoadedHelper.waitForCallback(0);
}
private @ContentSettingValues int locationPermissionForSearchEngine(String keyword) {
String url = TemplateUrlService.getInstance().getSearchEngineUrlFromTemplateUrl(keyword);
PermissionInfo locationSettings =
new PermissionInfo(PermissionInfo.Type.GEOLOCATION, url, null, false);
@ContentSettingValues
int locationPermission = locationSettings.getContentSetting();
return locationPermission;
}
/**
* Tests setting FontScaleFactor and ForceEnableZoom in AccessibilityPreferences and ensures
* that ForceEnableZoom changes corresponding to FontScaleFactor.
*/
@Test
@SmallTest
@Feature({"Accessibility"})
public void testAccessibilityPreferences() throws Exception {
String accessibilityPrefClassname = AccessibilityPreferences.class.getName();
AccessibilityPreferences accessibilityPref = (AccessibilityPreferences) startPreferences(
InstrumentationRegistry.getInstrumentation(), accessibilityPrefClassname)
.getFragmentForTest();
SeekBarPreference textScalePref = (SeekBarPreference) accessibilityPref.findPreference(
AccessibilityPreferences.PREF_TEXT_SCALE);
SeekBarLinkedCheckBoxPreference forceEnableZoomPref =
(SeekBarLinkedCheckBoxPreference) accessibilityPref
.findPreference(AccessibilityPreferences.PREF_FORCE_ENABLE_ZOOM);
NumberFormat percentFormat = NumberFormat.getPercentInstance();
// Arbitrary value 0.4f to be larger and smaller than threshold.
float fontSmallerThanThreshold =
FontSizePrefs.FORCE_ENABLE_ZOOM_THRESHOLD_MULTIPLIER - 0.4f;
float fontBiggerThanThreshold =
FontSizePrefs.FORCE_ENABLE_ZOOM_THRESHOLD_MULTIPLIER + 0.4f;
// Set the textScaleFactor above the threshold.
userSetTextScale(accessibilityPref, textScalePref, fontBiggerThanThreshold);
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
// Since above the threshold, this will check the force enable zoom button.
Assert.assertEquals(
percentFormat.format(fontBiggerThanThreshold), textScalePref.getSummary());
Assert.assertTrue(forceEnableZoomPref.isChecked());
assertFontSizePrefs(true, fontBiggerThanThreshold);
// Set the textScaleFactor below the threshold.
userSetTextScale(accessibilityPref, textScalePref, fontSmallerThanThreshold);
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
// Since below the threshold and userSetForceEnableZoom is false, this will uncheck
// the force enable zoom button.
Assert.assertEquals(
percentFormat.format(fontSmallerThanThreshold), textScalePref.getSummary());
Assert.assertFalse(forceEnableZoomPref.isChecked());
assertFontSizePrefs(false, fontSmallerThanThreshold);
userSetTextScale(accessibilityPref, textScalePref, fontBiggerThanThreshold);
// Sets onUserSetForceEnableZoom to be true.
userSetForceEnableZoom(accessibilityPref, forceEnableZoomPref, true);
UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
// Since userSetForceEnableZoom is true, when the text scale is moved below the threshold
// ForceEnableZoom should remain checked.
userSetTextScale(accessibilityPref, textScalePref, fontSmallerThanThreshold);
Assert.assertTrue(forceEnableZoomPref.isChecked());
assertFontSizePrefs(true, fontSmallerThanThreshold);
}
@Test
@SmallTest
@Policies.Add({ @Policies.Item(key = "PasswordManagerEnabled", string = "false") })
public void testSavePasswordsPreferences_ManagedAndDisabled() throws ExecutionException {
ThreadUtils.runOnUiThreadBlocking(() -> {
try {
ChromeBrowserInitializer.getInstance().handleSynchronousStartup();
} catch (ProcessInitException e) {
Assert.fail("Unable to initialize process: " + e);
}
});
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return PrefServiceBridge.getInstance().isRememberPasswordsManaged();
}
});
PreferencesTest.startPreferences(
InstrumentationRegistry.getInstrumentation(), MainPreferences.class.getName());
onView(withText(R.string.prefs_saved_passwords_title)).perform(click());
onView(withText(R.string.prefs_saved_passwords)).check(matches(isDisplayed()));
}
private void assertFontSizePrefs(final boolean expectedForceEnableZoom,
final float expectedFontScale) {
final Context targetContext = InstrumentationRegistry.getTargetContext();
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
FontSizePrefs fontSizePrefs = FontSizePrefs.getInstance(targetContext);
Assert.assertEquals(expectedForceEnableZoom, fontSizePrefs.getForceEnableZoom());
Assert.assertEquals(expectedFontScale, fontSizePrefs.getFontScaleFactor(), 0.001f);
}
});
}
private static void userSetTextScale(final AccessibilityPreferences accessibilityPref,
final SeekBarPreference textScalePref, final float textScale) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
accessibilityPref.onPreferenceChange(textScalePref, textScale);
}
});
}
private static void userSetForceEnableZoom(final AccessibilityPreferences accessibilityPref,
final SeekBarLinkedCheckBoxPreference forceEnableZoomPref, final boolean enabled) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
accessibilityPref.onPreferenceChange(forceEnableZoomPref, enabled);
}
});
}
private static Preference waitForPreference(final PreferenceFragment prefFragment,
final String preferenceKey) throws ExecutionException {
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return prefFragment.findPreference(preferenceKey) != null;
}
});
return ThreadUtils.runOnUiThreadBlocking(new Callable<Preference>() {
@Override
public Preference call() throws Exception {
return prefFragment.findPreference(preferenceKey);
}
});
}
}