diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index 28667675b69..abd659e6076 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -46,7 +46,7 @@ buildPreferenceControllers( Context context, Lifecycle lifecycle) { final List controllers = new ArrayList<>(); - controllers.add(new AutoBrightnessPreferenceController(context)); + controllers.add(new AutoBrightnessPreferenceController(context, KEY_AUTO_BRIGHTNESS)); controllers.add(new AutoRotatePreferenceController(context)); controllers.add(new CameraGesturePreferenceController(context)); controllers.add(new DozePreferenceController(context)); @@ -100,7 +103,7 @@ public class DisplaySettings extends DashboardFragment { controllers.add(new DoubleTapScreenPreferenceController( context, lifecycle, ambientDisplayConfig, UserHandle.myUserId())); controllers.add(new TapToWakePreferenceController(context)); - controllers.add(new TimeoutPreferenceController(context)); + controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT)); controllers.add(new VrDisplayPreferenceController(context)); controllers.add(new WallpaperPreferenceController(context)); controllers.add(new ThemePreferenceController(context)); diff --git a/src/com/android/settings/display/AutoBrightnessPreferenceController.java b/src/com/android/settings/display/AutoBrightnessPreferenceController.java index 869c8f0f6e4..f36ed3662f7 100644 --- a/src/com/android/settings/display/AutoBrightnessPreferenceController.java +++ b/src/com/android/settings/display/AutoBrightnessPreferenceController.java @@ -36,10 +36,11 @@ import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL; public class AutoBrightnessPreferenceController extends PreferenceController implements Preference.OnPreferenceChangeListener { - private static final String KEY_AUTO_BRIGHTNESS = "auto_brightness"; + private final String mAutoBrightnessKey; - public AutoBrightnessPreferenceController(Context context) { + public AutoBrightnessPreferenceController(Context context, String key) { super(context); + mAutoBrightnessKey = key; } @Override @@ -50,7 +51,7 @@ public class AutoBrightnessPreferenceController extends PreferenceController imp @Override public String getPreferenceKey() { - return KEY_AUTO_BRIGHTNESS; + return mAutoBrightnessKey; } @Override @@ -75,7 +76,7 @@ public class AutoBrightnessPreferenceController extends PreferenceController imp valueMap.put(SCREEN_BRIGHTNESS_MODE_MANUAL, false); final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(mContext, - getClass().getName(), KEY_AUTO_BRIGHTNESS, + getClass().getName(), mAutoBrightnessKey, mContext.getString(R.string.display_settings)); return new InlineSwitchPayload(SCREEN_BRIGHTNESS_MODE, diff --git a/src/com/android/settings/display/TimeoutPreferenceController.java b/src/com/android/settings/display/TimeoutPreferenceController.java index d4096568d26..b2890c28577 100644 --- a/src/com/android/settings/display/TimeoutPreferenceController.java +++ b/src/com/android/settings/display/TimeoutPreferenceController.java @@ -35,10 +35,11 @@ public class TimeoutPreferenceController extends PreferenceController implements /** If there is no setting in the provider, use this. */ public static final int FALLBACK_SCREEN_TIMEOUT_VALUE = 30000; - private static final String KEY_SCREEN_TIMEOUT = "screen_timeout"; + private final String mScreenTimeoutKey; - public TimeoutPreferenceController(Context context) { + public TimeoutPreferenceController(Context context, String key) { super(context); + mScreenTimeoutKey = key; } @Override @@ -48,7 +49,7 @@ public class TimeoutPreferenceController extends PreferenceController implements @Override public String getPreferenceKey() { - return KEY_SCREEN_TIMEOUT; + return mScreenTimeoutKey; } @Override diff --git a/src/com/android/settings/fuelgauge/BatterySaverController.java b/src/com/android/settings/fuelgauge/BatterySaverController.java index 08d570a625a..34c9a269b8b 100644 --- a/src/com/android/settings/fuelgauge/BatterySaverController.java +++ b/src/com/android/settings/fuelgauge/BatterySaverController.java @@ -43,7 +43,7 @@ import static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGING; public class BatterySaverController extends PreferenceController implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnStart, OnStop { - private static final String KEY_BATTERY_SAVER = "battery_saver"; + private static final String KEY_BATTERY_SAVER = "battery_saver_summary"; private static final String TAG = "BatterySaverController"; private static final boolean DEBUG = false; diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index ac041b84802..624a284b8f7 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -86,6 +86,9 @@ public class PowerUsageSummary extends PowerUsageBase { private static final String KEY_SCREEN_USAGE = "screen_usage"; private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge"; + private static final String KEY_AUTO_BRIGHTNESS = "auto_brightness_battery"; + private static final String KEY_SCREEN_TIMEOUT = "screen_timeout_battery"; + private static final String KEY_BATTERY_SAVER_SUMMARY = "battery_saver_summary"; private static final int MENU_STATS_TYPE = Menu.FIRST; @VisibleForTesting @@ -185,8 +188,8 @@ public class PowerUsageSummary extends PowerUsageBase { @Override protected List getPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); - controllers.add(new AutoBrightnessPreferenceController(context)); - controllers.add(new TimeoutPreferenceController(context)); + controllers.add(new AutoBrightnessPreferenceController(context, KEY_AUTO_BRIGHTNESS)); + controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT)); controllers.add(new BatterySaverController(context, getLifecycle())); controllers.add(new BatteryPercentagePreferenceController(context)); return controllers; @@ -712,6 +715,16 @@ public class PowerUsageSummary extends PowerUsageBase { sir.xmlResId = R.xml.power_usage_summary; return Arrays.asList(sir); } + + @Override + public List getNonIndexableKeys(Context context) { + List niks = new ArrayList<>(); + // Duplicates in display + niks.add(KEY_AUTO_BRIGHTNESS); + niks.add(KEY_SCREEN_TIMEOUT); + niks.add(KEY_BATTERY_SAVER_SUMMARY); + return niks; + } }; public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY diff --git a/tests/robotests/assets/whitelist_duplicate_index_key b/tests/robotests/assets/whitelist_duplicate_index_key new file mode 100644 index 00000000000..444640343eb --- /dev/null +++ b/tests/robotests/assets/whitelist_duplicate_index_key @@ -0,0 +1,12 @@ +dashboard_tile_placeholder +gesture_pick_up +add_users_when_locked +additional_system_update_settings +gesture_assist +screen_zoom +gesture_swipe_down_fingerprint +gesture_double_twist +lock_screen_notifications +gesture_double_tap_power +gesture_double_tap_screen +usage_access \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspector.java b/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspector.java index 86c14a56176..2f786f8acf4 100644 --- a/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspector.java +++ b/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspector.java @@ -70,7 +70,7 @@ public abstract class CodeInspector { return true; } - protected void initializeGrandfatherList(List grandfather, String filename) { + public static void initializeGrandfatherList(List grandfather, String filename) { try { final InputStream in = ShadowApplication.getInstance().getApplicationContext() .getAssets() diff --git a/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerTest.java index cc9b6d035c4..6d8696f1541 100644 --- a/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerTest.java @@ -43,12 +43,13 @@ public class AutoBrightnessPreferenceControllerTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; private AutoBrightnessPreferenceController mController; + private final String PREFERENCE_KEY = "auto_brightness"; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mController = new AutoBrightnessPreferenceController(mContext); + mController = new AutoBrightnessPreferenceController(mContext, PREFERENCE_KEY); } @Test @@ -72,7 +73,7 @@ public class AutoBrightnessPreferenceControllerTest { @Test public void testPreferenceController_ProperResultPayloadType() { final Context context = ShadowApplication.getInstance().getApplicationContext(); - mController = new AutoBrightnessPreferenceController(context); + mController = new AutoBrightnessPreferenceController(context, PREFERENCE_KEY); ResultPayload payload = mController.getResultPayload(); assertThat(payload).isInstanceOf(InlineSwitchPayload.class); } @@ -80,7 +81,7 @@ public class AutoBrightnessPreferenceControllerTest { @Test public void testPreferenceController_CorrectPayload() { final Context context = ShadowApplication.getInstance().getApplicationContext(); - mController = new AutoBrightnessPreferenceController(context); + mController = new AutoBrightnessPreferenceController(context, PREFERENCE_KEY); InlineSwitchPayload payload = (InlineSwitchPayload) mController.getResultPayload(); assertThat(payload.settingsUri).isEqualTo("screen_brightness_mode"); assertThat(payload.settingSource).isEqualTo(ResultPayload.SettingsSource.SYSTEM); diff --git a/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java index ec142c251a1..2ebad46c67d 100644 --- a/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java @@ -42,11 +42,13 @@ public class TimeoutPreferenceControllerTest { private TimeoutListPreference mPreference; private TimeoutPreferenceController mController; + private static final String KEY_SCREEN_TIMEOUT = "screen_timeout"; + @Before public void setUp() { MockitoAnnotations.initMocks(this); - mController = new TimeoutPreferenceController(mContext); + mController = new TimeoutPreferenceController(mContext, KEY_SCREEN_TIMEOUT); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index 39c386b290a..5611371045d 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -36,9 +36,11 @@ import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.Utils; import com.android.settings.applications.LayoutPreference; +import com.android.settings.core.PreferenceController; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor; +import com.android.settings.testutils.XmlTestUtils; import com.android.settingslib.BatteryInfo; import org.junit.Before; @@ -423,6 +425,33 @@ public class PowerUsageSummaryTest { TIME_SINCE_LAST_FULL_CHARGE_MS); } + @Test + public void testNonIndexableKeys_MatchPreferenceKeys() { + final Context context = RuntimeEnvironment.application; + final List niks = PowerUsageSummary.SEARCH_INDEX_DATA_PROVIDER + .getNonIndexableKeys(context); + + final List keys = XmlTestUtils.getKeysFromPreferenceXml(context, + R.xml.power_usage_summary); + + assertThat(keys).containsAllIn(niks); + } + + @Test + public void testPreferenceControllers_getPreferenceKeys_existInPreferenceScreen() { + final Context context = RuntimeEnvironment.application; + final PowerUsageSummary fragment = new PowerUsageSummary(); + final List preferenceScreenKeys = XmlTestUtils.getKeysFromPreferenceXml(context, + fragment.getPreferenceScreenResId()); + final List preferenceKeys = new ArrayList<>(); + + for (PreferenceController controller : fragment.getPreferenceControllers(context)) { + preferenceKeys.add(controller.getPreferenceKey()); + } + + assertThat(preferenceScreenKeys).containsAllIn(preferenceKeys); + } + public static class TestFragment extends PowerUsageSummary { private Context mContext; diff --git a/tests/robotests/src/com/android/settings/search/DataIntegrityTest.java b/tests/robotests/src/com/android/settings/search/DataIntegrityTest.java new file mode 100644 index 00000000000..f10752a44fc --- /dev/null +++ b/tests/robotests/src/com/android/settings/search/DataIntegrityTest.java @@ -0,0 +1,123 @@ +package com.android.settings.search; + +import android.content.Context; +import android.provider.SearchIndexableResource; +import android.util.ArraySet; +import com.android.settings.DateTimeSettings; +import com.android.settings.R; +import com.android.settings.SecuritySettings; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.core.codeinspection.CodeInspector; +import com.android.settings.datausage.DataUsageSummary; +import com.android.settings.search2.DatabaseIndexingUtils; +import com.android.settings.testutils.XmlTestUtils; +import com.android.settings.testutils.shadow.SettingsShadowResources; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static com.google.common.truth.Truth.assertThat; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, + assetDir = "/tests/robotests/assets") +public class DataIntegrityTest { + + @Test + @Config(shadows = { + SettingsShadowResources.class, + SettingsShadowResources.SettingsShadowTheme.class, + }) + public void testIndexableResources_uniqueKeys() { + final Context context = RuntimeEnvironment.application; + // Aggregation of all keys + final Set masterKeys = new ArraySet<>(); + // Aggregation of the incorrectly duplicate keys + final Set duplicateKeys = new ArraySet<>(); + // Keys for a specific page + final Set pageKeys = new ArraySet<>(); + // List of all Xml preferences + final Set xmlList = new ArraySet<>(); + // Duplicates we know about. + List grandfatheredKeys = new ArrayList<>(); + CodeInspector.initializeGrandfatherList(grandfatheredKeys, + "whitelist_duplicate_index_key"); + + // Get a list of all Xml. + for (SearchIndexableResource val : SearchIndexableResources.values()) { + final int xmlResId = val.xmlResId; + if (xmlResId != 0) { + xmlList.add(xmlResId); + } else { + // Take class and get all keys + final Class clazz = DatabaseIndexingUtils.getIndexableClass(val.className); + + // Skip classes that are invalid or cannot be mocked. Add them as special Xml below. + if (clazz == null + || clazz == DateTimeSettings.class + || clazz == DataUsageSummary.class + || clazz == SecuritySettings.class) { + continue; + } + + Indexable.SearchIndexProvider provider = DatabaseIndexingUtils + .getSearchIndexProvider(clazz); + + if (provider == null) { + continue; + } + + List subXml = + provider.getXmlResourcesToIndex(context, true); + + if (subXml == null) { + continue; + } + for (SearchIndexableResource resource : subXml) { + final int subXmlResId = resource.xmlResId; + if (subXmlResId != 0) { + xmlList.add(subXmlResId); + } + } + } + } + addSpecialXml(xmlList); + + // Get keys from all Xml and check for duplicates. + for (Integer xmlResId : xmlList) { + // Get all keys to be indexed + final List prefKeys = XmlTestUtils.getKeysFromPreferenceXml(context, xmlResId); + + pageKeys.addAll(prefKeys); + // Remove grandfathered keys. + pageKeys.removeAll(grandfatheredKeys); + // Find all already-existing keys. + pageKeys.retainAll(masterKeys); + // Keep list of offending duplicate keys. + duplicateKeys.addAll(pageKeys); + // Add all keys to master key list. + masterKeys.addAll(prefKeys); + pageKeys.clear(); + } + assertThat(duplicateKeys).isEmpty(); + } + + /** + * Add XML preferences from Fragments which have issues being instantiated in robolectric. + */ + private void addSpecialXml(Set xmlList) { + xmlList.add(R.xml.date_time_prefs); + xmlList.add(R.xml.data_usage); + xmlList.add(R.xml.data_usage_cellular); + xmlList.add(R.xml.data_usage_wifi); + xmlList.add(R.xml.security_settings_misc); + } + + +} diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java b/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java index 012d61629db..89afcaaaafc 100644 --- a/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java +++ b/tests/robotests/src/com/android/settings/search/SearchIndexableResourcesTest.java @@ -19,9 +19,7 @@ package com.android.settings.search; import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE; import static com.android.settings.search.SearchIndexableResources.NO_DATA_RES_ID; -import static com.android.settings.search.SearchIndexableResources.sResMap; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import android.annotation.DrawableRes; @@ -35,12 +33,14 @@ import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.wifi.WifiSettings; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.util.HashMap; +import java.util.Map; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @@ -51,6 +51,21 @@ public class SearchIndexableResourcesTest { @DrawableRes private static final int ICON_RES_ID = R.drawable.ic_settings_language; + Map sResMapCopy; + + @Before + public void setUp() { + sResMapCopy = new HashMap<>(SearchIndexableResources.sResMap); + } + + @After + public void cleanUp() { + SearchIndexableResources.sResMap.clear(); + for (String key : sResMapCopy.keySet()) { + SearchIndexableResources.sResMap.put(key, sResMapCopy.get(key)); + } + } + @Test public void testAddIndex() { // Confirms that String.class isn't contained in SearchIndexableResources. diff --git a/tests/robotests/src/com/android/settings/testutils/XmlTestUtils.java b/tests/robotests/src/com/android/settings/testutils/XmlTestUtils.java new file mode 100644 index 00000000000..20b3f89a058 --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/XmlTestUtils.java @@ -0,0 +1,57 @@ +package com.android.settings.testutils; + +import android.content.res.Resources; +import android.content.res.XmlResourceParser; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Xml; +import com.android.settings.search2.XmlParserUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.util.ArrayList; +import java.util.List; + +/** + * Util class for parsing XML + */ +public class XmlTestUtils { + + /** + * Parses a preference screen's xml, collects and returns all keys used by preferences + * on the screen. + * + * @param context of the preference screen. + * @param xmlId of the Preference Xml to be parsed. + * @return List of all keys in the preference Xml + */ + public static List getKeysFromPreferenceXml(Context context, int xmlId) { + final XmlResourceParser parser = context.getResources().getXml(xmlId); + final AttributeSet attrs = Xml.asAttributeSet(parser); + final List keys = new ArrayList<>(); + String key; + try { + while (parser.next() != XmlPullParser.END_DOCUMENT) { + try { + key = XmlParserUtils.getDataKey(context, attrs); + if (!TextUtils.isEmpty(key)) { + keys.add(key); + } + } catch (NullPointerException e) { + continue; + } catch (Resources.NotFoundException e) { + continue; + } + } + } catch (java.io.IOException e) { + return null; + } catch (XmlPullParserException e) { + return null; + } + + return keys; + } + +}