1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settings.dashboard; 17 18 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.DASHBOARD_CONTAINER; 19 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; 20 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.nullable; 26 import static org.mockito.Mockito.mock; 27 import static org.mockito.Mockito.never; 28 import static org.mockito.Mockito.spy; 29 import static org.mockito.Mockito.times; 30 import static org.mockito.Mockito.verify; 31 import static org.mockito.Mockito.when; 32 33 import android.app.settings.SettingsEnums; 34 import android.content.ContentResolver; 35 import android.content.Context; 36 import android.content.pm.ActivityInfo; 37 import android.content.pm.ProviderInfo; 38 import android.net.Uri; 39 import android.os.Bundle; 40 import android.util.FeatureFlagUtils; 41 42 import androidx.preference.Preference; 43 import androidx.preference.PreferenceFragmentCompat; 44 import androidx.preference.PreferenceManager; 45 import androidx.preference.PreferenceScreen; 46 import androidx.preference.SwitchPreference; 47 48 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 49 import com.android.settings.core.FeatureFlags; 50 import com.android.settings.core.PreferenceControllerMixin; 51 import com.android.settings.slices.BlockingSlicePrefController; 52 import com.android.settings.testutils.FakeFeatureFactory; 53 import com.android.settings.widget.MasterSwitchPreference; 54 import com.android.settingslib.core.AbstractPreferenceController; 55 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 56 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; 57 import com.android.settingslib.drawer.ActivityTile; 58 import com.android.settingslib.drawer.DashboardCategory; 59 import com.android.settingslib.drawer.ProviderTile; 60 61 import org.junit.Before; 62 import org.junit.Test; 63 import org.junit.runner.RunWith; 64 import org.mockito.Mock; 65 import org.mockito.MockitoAnnotations; 66 import org.robolectric.RobolectricTestRunner; 67 import org.robolectric.RuntimeEnvironment; 68 import org.robolectric.annotation.Config; 69 import org.robolectric.annotation.Implementation; 70 import org.robolectric.annotation.Implements; 71 import org.robolectric.util.ReflectionHelpers; 72 73 import java.util.ArrayList; 74 import java.util.Arrays; 75 import java.util.HashMap; 76 import java.util.List; 77 import java.util.Map; 78 79 @RunWith(RobolectricTestRunner.class) 80 public class DashboardFragmentTest { 81 82 @Mock 83 private FakeFeatureFactory mFakeFeatureFactory; 84 private DashboardCategory mDashboardCategory; 85 private Context mContext; 86 private TestFragment mTestFragment; 87 private List<AbstractPreferenceController> mControllers; 88 private ActivityTile mActivityTile; 89 private ProviderTile mProviderTile; 90 91 @Before setUp()92 public void setUp() { 93 MockitoAnnotations.initMocks(this); 94 mContext = spy(RuntimeEnvironment.application); 95 final ActivityInfo activityInfo = new ActivityInfo(); 96 activityInfo.packageName = "pkg"; 97 activityInfo.name = "class"; 98 activityInfo.metaData = new Bundle(); 99 activityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key"); 100 mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); 101 mDashboardCategory = new DashboardCategory("key"); 102 mActivityTile = new ActivityTile(activityInfo, mDashboardCategory.key); 103 mDashboardCategory.addTile(mActivityTile); 104 105 final ProviderInfo providerInfo = new ProviderInfo(); 106 providerInfo.packageName = "pkg"; 107 providerInfo.name = "provider"; 108 providerInfo.authority = "authority"; 109 final Bundle metaData = new Bundle(); 110 metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key2"); 111 metaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "uri"); 112 mProviderTile = new ProviderTile(providerInfo, mDashboardCategory.key, metaData); 113 mDashboardCategory.addTile(mProviderTile); 114 115 mTestFragment = new TestFragment(RuntimeEnvironment.application); 116 when(mFakeFeatureFactory.dashboardFeatureProvider 117 .getTilesForCategory(nullable(String.class))) 118 .thenReturn(mDashboardCategory); 119 mTestFragment.onAttach(RuntimeEnvironment.application); 120 when(mContext.getPackageName()).thenReturn("TestPackage"); 121 mControllers = new ArrayList<>(); 122 } 123 124 @Test testPreferenceControllerGetterSetter_shouldAddAndGetProperly()125 public void testPreferenceControllerGetterSetter_shouldAddAndGetProperly() { 126 final TestPreferenceController controller = new TestPreferenceController(mContext); 127 mTestFragment.addPreferenceController(controller); 128 129 final TestPreferenceController retrievedController = mTestFragment.use 130 (TestPreferenceController.class); 131 132 assertThat(controller).isSameAs(retrievedController); 133 } 134 135 @Test testPreferenceControllerSetter_shouldAddAndNotReplace()136 public void testPreferenceControllerSetter_shouldAddAndNotReplace() { 137 final TestPreferenceController controller1 = new TestPreferenceController(mContext); 138 mTestFragment.addPreferenceController(controller1); 139 final TestPreferenceController controller2 = new TestPreferenceController(mContext); 140 mTestFragment.addPreferenceController(controller2); 141 142 final TestPreferenceController retrievedController = mTestFragment.use 143 (TestPreferenceController.class); 144 145 assertThat(controller1).isSameAs(retrievedController); 146 } 147 148 @Test displayTilesAsPreference_shouldAddTilesWithIntent()149 public void displayTilesAsPreference_shouldAddTilesWithIntent() { 150 when(mFakeFeatureFactory.dashboardFeatureProvider 151 .getTilesForCategory(nullable(String.class))) 152 .thenReturn(mDashboardCategory); 153 when(mFakeFeatureFactory.dashboardFeatureProvider 154 .getDashboardKeyForTile(any(ActivityTile.class))) 155 .thenReturn("test_key"); 156 when(mFakeFeatureFactory.dashboardFeatureProvider 157 .getDashboardKeyForTile(any(ProviderTile.class))) 158 .thenReturn("test_key2"); 159 mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); 160 161 verify(mTestFragment.mScreen, times(2)).addPreference(nullable(Preference.class)); 162 } 163 164 @Test displayTilesAsPreference_shouldNotAddTilesWithoutIntent()165 public void displayTilesAsPreference_shouldNotAddTilesWithoutIntent() { 166 mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); 167 168 verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class)); 169 } 170 171 @Test displayTilesAsPreference_withEmptyCategory_shouldNotAddTiles()172 public void displayTilesAsPreference_withEmptyCategory_shouldNotAddTiles() { 173 mDashboardCategory.removeTile(0); 174 mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); 175 176 verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class)); 177 } 178 179 @Test 180 @Config(qualifiers = "mcc999") displayTilesAsPreference_shouldNotAddSuppressedTiles()181 public void displayTilesAsPreference_shouldNotAddSuppressedTiles() { 182 when(mFakeFeatureFactory.dashboardFeatureProvider 183 .getTilesForCategory(nullable(String.class))) 184 .thenReturn(mDashboardCategory); 185 when(mFakeFeatureFactory.dashboardFeatureProvider 186 .getDashboardKeyForTile(any(ActivityTile.class))) 187 .thenReturn("test_key"); 188 when(mFakeFeatureFactory.dashboardFeatureProvider 189 .getDashboardKeyForTile(any(ProviderTile.class))) 190 .thenReturn("test_key2"); 191 mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); 192 193 verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class)); 194 } 195 196 @Test onAttach_shouldCreatePlaceholderPreferenceController()197 public void onAttach_shouldCreatePlaceholderPreferenceController() { 198 final AbstractPreferenceController controller = mTestFragment.use( 199 DashboardTilePlaceholderPreferenceController.class); 200 201 assertThat(controller).isNotNull(); 202 } 203 204 @Test 205 @Config(shadows = ShadowPreferenceFragmentCompat.class) onStart_shouldRegisterDynamicDataObservers()206 public void onStart_shouldRegisterDynamicDataObservers() { 207 final DynamicDataObserver observer = new TestDynamicDataObserver(); 208 mTestFragment.mDashboardTilePrefKeys.put("key", Arrays.asList(observer)); 209 210 mTestFragment.onStart(); 211 212 verify(mTestFragment.getContentResolver()).registerContentObserver(observer.getUri(), false, 213 observer); 214 } 215 216 @Test 217 @Config(shadows = ShadowPreferenceFragmentCompat.class) onStop_shouldUnregisterDynamicDataObservers()218 public void onStop_shouldUnregisterDynamicDataObservers() { 219 final DynamicDataObserver observer = new TestDynamicDataObserver(); 220 mTestFragment.registerDynamicDataObservers(Arrays.asList(observer)); 221 222 mTestFragment.onStop(); 223 224 verify(mTestFragment.getContentResolver()).unregisterContentObserver(observer); 225 } 226 227 @Test updateState_skipUnavailablePrefs()228 public void updateState_skipUnavailablePrefs() { 229 final List<AbstractPreferenceController> preferenceControllers = mTestFragment.mControllers; 230 final AbstractPreferenceController mockController1 = 231 mock(AbstractPreferenceController.class); 232 final AbstractPreferenceController mockController2 = 233 mock(AbstractPreferenceController.class); 234 when(mockController1.getPreferenceKey()).thenReturn("key1"); 235 when(mockController2.getPreferenceKey()).thenReturn("key2"); 236 preferenceControllers.add(mockController1); 237 preferenceControllers.add(mockController2); 238 when(mockController1.isAvailable()).thenReturn(false); 239 when(mockController2.isAvailable()).thenReturn(true); 240 mTestFragment.onAttach(RuntimeEnvironment.application); 241 mTestFragment.onResume(); 242 243 verify(mockController1).getPreferenceKey(); 244 verify(mockController2, times(2)).getPreferenceKey(); 245 } 246 247 @Test updateState_doesNotSkipControllersOfSameClass()248 public void updateState_doesNotSkipControllersOfSameClass() { 249 final AbstractPreferenceController mockController1 = 250 mock(AbstractPreferenceController.class); 251 final AbstractPreferenceController mockController2 = 252 mock(AbstractPreferenceController.class); 253 mTestFragment.addPreferenceController(mockController1); 254 mTestFragment.addPreferenceController(mockController2); 255 when(mockController1.isAvailable()).thenReturn(true); 256 when(mockController2.isAvailable()).thenReturn(true); 257 258 mTestFragment.updatePreferenceStates(); 259 260 verify(mockController1).getPreferenceKey(); 261 verify(mockController2).getPreferenceKey(); 262 } 263 264 @Test onExpandButtonClick_shouldLogAdvancedButtonExpand()265 public void onExpandButtonClick_shouldLogAdvancedButtonExpand() { 266 final MetricsFeatureProvider metricsFeatureProvider 267 = mFakeFeatureFactory.getMetricsFeatureProvider(); 268 mTestFragment.onExpandButtonClick(); 269 270 verify(metricsFeatureProvider).action(SettingsEnums.PAGE_UNKNOWN, 271 MetricsEvent.ACTION_SETTINGS_ADVANCED_BUTTON_EXPAND, 272 DASHBOARD_CONTAINER, null, 0); 273 } 274 275 @Test updatePreferenceVisibility_prefKeyNull_shouldNotCrash()276 public void updatePreferenceVisibility_prefKeyNull_shouldNotCrash() { 277 final Map<Class, List<AbstractPreferenceController>> prefControllers = new HashMap<>(); 278 final List<AbstractPreferenceController> controllerList = new ArrayList<>(); 279 controllerList.add(new TestPreferenceController(mContext)); 280 prefControllers.put(TestPreferenceController.class, controllerList); 281 mTestFragment.mBlockerController = new UiBlockerController(Arrays.asList("pref_key")); 282 283 // Should not crash 284 mTestFragment.updatePreferenceVisibility(prefControllers); 285 } 286 287 @Test checkUiBlocker_noUiBlocker_controllerIsNull()288 public void checkUiBlocker_noUiBlocker_controllerIsNull() { 289 mTestFragment.mBlockerController = null; 290 mControllers.add(new TestPreferenceController(mContext)); 291 292 mTestFragment.checkUiBlocker(mControllers); 293 294 assertThat(mTestFragment.mBlockerController).isNull(); 295 } 296 297 @Test checkUiBlocker_hasUiBlockerAndControllerIsAvailable_controllerNotNull()298 public void checkUiBlocker_hasUiBlockerAndControllerIsAvailable_controllerNotNull() { 299 final BlockingSlicePrefController controller = 300 new BlockingSlicePrefController(mContext, "pref_key"); 301 controller.setSliceUri(Uri.parse("testUri")); 302 mTestFragment.mBlockerController = null; 303 mControllers.add(new TestPreferenceController(mContext)); 304 mControllers.add(controller); 305 306 mTestFragment.checkUiBlocker(mControllers); 307 308 assertThat(mTestFragment.mBlockerController).isNotNull(); 309 } 310 311 @Test checkUiBlocker_hasUiBlockerAndControllerIsNotAvailable_controllerIsNull()312 public void checkUiBlocker_hasUiBlockerAndControllerIsNotAvailable_controllerIsNull() { 313 mTestFragment.mBlockerController = null; 314 mControllers.add(new TestPreferenceController(mContext)); 315 mControllers.add(new BlockingSlicePrefController(mContext, "pref_key")); 316 317 mTestFragment.checkUiBlocker(mControllers); 318 319 assertThat(mTestFragment.mBlockerController).isNull(); 320 } 321 322 @Test createPreference_isProviderTile_returnSwitchPreference()323 public void createPreference_isProviderTile_returnSwitchPreference() { 324 final Preference pref = mTestFragment.createPreference(mProviderTile); 325 326 assertThat(pref).isInstanceOf(SwitchPreference.class); 327 } 328 329 @Test createPreference_isActivityTileAndHasSwitch_returnMasterSwitchPreference()330 public void createPreference_isActivityTileAndHasSwitch_returnMasterSwitchPreference() { 331 mActivityTile.getMetaData().putString(META_DATA_PREFERENCE_SWITCH_URI, "uri"); 332 333 final Preference pref = mTestFragment.createPreference(mActivityTile); 334 335 assertThat(pref).isInstanceOf(MasterSwitchPreference.class); 336 } 337 338 @Test isFeatureFlagAndIsParalleled_runParalleledUpdatePreferenceStates()339 public void isFeatureFlagAndIsParalleled_runParalleledUpdatePreferenceStates() { 340 FeatureFlagUtils.setEnabled(mContext, FeatureFlags.CONTROLLER_ENHANCEMENT, true); 341 final TestFragment testFragment = spy(new TestFragment(RuntimeEnvironment.application)); 342 343 testFragment.updatePreferenceStates(); 344 345 verify(testFragment).updatePreferenceStatesInParallel(); 346 } 347 348 @Test notFeatureFlagAndIsParalleled_notRunParalleledUpdatePreferenceStates()349 public void notFeatureFlagAndIsParalleled_notRunParalleledUpdatePreferenceStates() { 350 FeatureFlagUtils.setEnabled(mContext, FeatureFlags.CONTROLLER_ENHANCEMENT, false); 351 final TestFragment testFragment = spy(new TestFragment(RuntimeEnvironment.application)); 352 353 testFragment.updatePreferenceStates(); 354 355 verify(testFragment, never()).updatePreferenceStatesInParallel(); 356 } 357 358 @Test isFeatureFlagAndNotParalleled_notRunParalleledUpdatePreferenceStates()359 public void isFeatureFlagAndNotParalleled_notRunParalleledUpdatePreferenceStates() { 360 FeatureFlagUtils.setEnabled(mContext, FeatureFlags.CONTROLLER_ENHANCEMENT, true); 361 final TestFragment testFragment = spy(new TestFragment(RuntimeEnvironment.application)); 362 testFragment.setUsingControllerEnhancement(false); 363 364 testFragment.updatePreferenceStates(); 365 366 verify(testFragment, never()).updatePreferenceStatesInParallel(); 367 } 368 369 public static class TestPreferenceController extends AbstractPreferenceController 370 implements PreferenceControllerMixin { 371 TestPreferenceController(Context context)372 private TestPreferenceController(Context context) { 373 super(context); 374 } 375 376 @Override handlePreferenceTreeClick(Preference preference)377 public boolean handlePreferenceTreeClick(Preference preference) { 378 return false; 379 } 380 381 @Override isAvailable()382 public boolean isAvailable() { 383 return false; 384 } 385 386 @Override getPreferenceKey()387 public String getPreferenceKey() { 388 return null; 389 } 390 391 @Override updateNonIndexableKeys(List<String> keys)392 public void updateNonIndexableKeys(List<String> keys) { 393 } 394 } 395 396 private static class TestFragment extends DashboardFragment { 397 398 private final PreferenceManager mPreferenceManager; 399 private final Context mContext; 400 private final List<AbstractPreferenceController> mControllers; 401 private final ContentResolver mContentResolver; 402 403 public final PreferenceScreen mScreen; 404 private boolean mIsParalleled; 405 TestFragment(Context context)406 public TestFragment(Context context) { 407 mContext = context; 408 mPreferenceManager = mock(PreferenceManager.class); 409 mScreen = mock(PreferenceScreen.class); 410 mContentResolver = mock(ContentResolver.class); 411 mControllers = new ArrayList<>(); 412 mIsParalleled = true; 413 414 when(mPreferenceManager.getContext()).thenReturn(mContext); 415 ReflectionHelpers.setField( 416 this, "mVisibilityLoggerMixin", mock(VisibilityLoggerMixin.class)); 417 } 418 419 @Override getContext()420 public Context getContext() { 421 return mContext; 422 } 423 424 @Override getMetricsCategory()425 public int getMetricsCategory() { 426 return DASHBOARD_CONTAINER; 427 } 428 429 @Override getPreferenceScreen()430 public PreferenceScreen getPreferenceScreen() { 431 return mScreen; 432 } 433 434 @Override getLogTag()435 protected String getLogTag() { 436 return "TEST_FRAG"; 437 } 438 439 @Override getPreferenceScreenResId()440 protected int getPreferenceScreenResId() { 441 return 0; 442 } 443 444 @Override createPreferenceControllers(Context context)445 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 446 return mControllers; 447 } 448 449 @Override getPreferenceManager()450 public PreferenceManager getPreferenceManager() { 451 return mPreferenceManager; 452 } 453 454 @Override getContentResolver()455 protected ContentResolver getContentResolver() { 456 return mContentResolver; 457 } 458 isParalleledControllers()459 protected boolean isParalleledControllers() { 460 return mIsParalleled; 461 } 462 setUsingControllerEnhancement(boolean isParalleled)463 public void setUsingControllerEnhancement(boolean isParalleled) { 464 mIsParalleled = isParalleled; 465 } 466 } 467 468 private static class TestDynamicDataObserver extends DynamicDataObserver { 469 470 @Override getUri()471 public Uri getUri() { 472 return Uri.parse("content://abc"); 473 } 474 475 @Override onDataChanged()476 public void onDataChanged() { 477 } 478 } 479 480 @Implements(PreferenceFragmentCompat.class) 481 public static class ShadowPreferenceFragmentCompat { 482 483 @Implementation onStart()484 public void onStart() { 485 // do nothing 486 } 487 488 @Implementation onStop()489 public void onStop() { 490 // do nothing 491 } 492 } 493 } 494