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 41 import androidx.preference.Preference; 42 import androidx.preference.PreferenceFragmentCompat; 43 import androidx.preference.PreferenceManager; 44 import androidx.preference.PreferenceScreen; 45 import androidx.preference.SwitchPreference; 46 47 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 48 import com.android.settings.core.PreferenceControllerMixin; 49 import com.android.settings.slices.BlockingSlicePrefController; 50 import com.android.settings.testutils.FakeFeatureFactory; 51 import com.android.settings.widget.PrimarySwitchPreference; 52 import com.android.settingslib.core.AbstractPreferenceController; 53 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 54 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; 55 import com.android.settingslib.drawer.ActivityTile; 56 import com.android.settingslib.drawer.DashboardCategory; 57 import com.android.settingslib.drawer.ProviderTile; 58 59 import org.junit.Before; 60 import org.junit.Test; 61 import org.junit.runner.RunWith; 62 import org.mockito.Mock; 63 import org.mockito.MockitoAnnotations; 64 import org.robolectric.RobolectricTestRunner; 65 import org.robolectric.RuntimeEnvironment; 66 import org.robolectric.annotation.Config; 67 import org.robolectric.annotation.Implementation; 68 import org.robolectric.annotation.Implements; 69 import org.robolectric.util.ReflectionHelpers; 70 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.HashMap; 74 import java.util.List; 75 import java.util.Map; 76 77 @RunWith(RobolectricTestRunner.class) 78 public class DashboardFragmentTest { 79 80 @Mock 81 private FakeFeatureFactory mFakeFeatureFactory; 82 private DashboardCategory mDashboardCategory; 83 private Context mContext; 84 private TestFragment mTestFragment; 85 private List<AbstractPreferenceController> mControllers; 86 private ActivityTile mActivityTile; 87 private ProviderTile mProviderTile; 88 89 @Before setUp()90 public void setUp() { 91 MockitoAnnotations.initMocks(this); 92 mContext = spy(RuntimeEnvironment.application); 93 final ActivityInfo activityInfo = new ActivityInfo(); 94 activityInfo.packageName = "pkg"; 95 activityInfo.name = "class"; 96 activityInfo.metaData = new Bundle(); 97 activityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key"); 98 mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); 99 mDashboardCategory = new DashboardCategory("key"); 100 mActivityTile = new ActivityTile(activityInfo, mDashboardCategory.key); 101 mDashboardCategory.addTile(mActivityTile); 102 103 final ProviderInfo providerInfo = new ProviderInfo(); 104 providerInfo.packageName = "pkg"; 105 providerInfo.name = "provider"; 106 providerInfo.authority = "authority"; 107 final Bundle metaData = new Bundle(); 108 metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key2"); 109 metaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "uri"); 110 mProviderTile = new ProviderTile(providerInfo, mDashboardCategory.key, metaData); 111 mDashboardCategory.addTile(mProviderTile); 112 113 mTestFragment = new TestFragment(RuntimeEnvironment.application); 114 when(mFakeFeatureFactory.dashboardFeatureProvider 115 .getTilesForCategory(nullable(String.class))) 116 .thenReturn(mDashboardCategory); 117 mTestFragment.onAttach(RuntimeEnvironment.application); 118 when(mContext.getPackageName()).thenReturn("TestPackage"); 119 mControllers = new ArrayList<>(); 120 } 121 122 @Test testPreferenceControllerGetterSetter_shouldAddAndGetProperly()123 public void testPreferenceControllerGetterSetter_shouldAddAndGetProperly() { 124 final TestPreferenceController controller = new TestPreferenceController(mContext); 125 mTestFragment.addPreferenceController(controller); 126 127 final TestPreferenceController retrievedController = mTestFragment.use 128 (TestPreferenceController.class); 129 130 assertThat(controller).isSameInstanceAs(retrievedController); 131 } 132 133 @Test testPreferenceControllerSetter_shouldAddAndNotReplace()134 public void testPreferenceControllerSetter_shouldAddAndNotReplace() { 135 final TestPreferenceController controller1 = new TestPreferenceController(mContext); 136 mTestFragment.addPreferenceController(controller1); 137 final TestPreferenceController controller2 = new TestPreferenceController(mContext); 138 mTestFragment.addPreferenceController(controller2); 139 140 final TestPreferenceController retrievedController = mTestFragment.use 141 (TestPreferenceController.class); 142 143 assertThat(controller1).isSameInstanceAs(retrievedController); 144 } 145 146 @Test displayTilesAsPreference_shouldAddTilesWithIntent()147 public void displayTilesAsPreference_shouldAddTilesWithIntent() { 148 when(mFakeFeatureFactory.dashboardFeatureProvider 149 .getTilesForCategory(nullable(String.class))) 150 .thenReturn(mDashboardCategory); 151 when(mFakeFeatureFactory.dashboardFeatureProvider 152 .getDashboardKeyForTile(any(ActivityTile.class))) 153 .thenReturn("test_key"); 154 when(mFakeFeatureFactory.dashboardFeatureProvider 155 .getDashboardKeyForTile(any(ProviderTile.class))) 156 .thenReturn("test_key2"); 157 mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); 158 159 verify(mTestFragment.mScreen, times(2)).addPreference(nullable(Preference.class)); 160 } 161 162 @Test displayTilesAsPreference_shouldNotAddTilesWithoutIntent()163 public void displayTilesAsPreference_shouldNotAddTilesWithoutIntent() { 164 mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); 165 166 verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class)); 167 } 168 169 @Test displayTilesAsPreference_withEmptyCategory_shouldNotAddTiles()170 public void displayTilesAsPreference_withEmptyCategory_shouldNotAddTiles() { 171 mDashboardCategory.removeTile(0); 172 mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); 173 174 verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class)); 175 } 176 177 @Test 178 @Config(qualifiers = "mcc999") displayTilesAsPreference_shouldNotAddSuppressedTiles()179 public void displayTilesAsPreference_shouldNotAddSuppressedTiles() { 180 when(mFakeFeatureFactory.dashboardFeatureProvider 181 .getTilesForCategory(nullable(String.class))) 182 .thenReturn(mDashboardCategory); 183 when(mFakeFeatureFactory.dashboardFeatureProvider 184 .getDashboardKeyForTile(any(ActivityTile.class))) 185 .thenReturn("test_key"); 186 when(mFakeFeatureFactory.dashboardFeatureProvider 187 .getDashboardKeyForTile(any(ProviderTile.class))) 188 .thenReturn("test_key2"); 189 mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); 190 191 verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class)); 192 } 193 194 @Test onAttach_shouldCreatePlaceholderPreferenceController()195 public void onAttach_shouldCreatePlaceholderPreferenceController() { 196 final AbstractPreferenceController controller = mTestFragment.use( 197 DashboardTilePlaceholderPreferenceController.class); 198 199 assertThat(controller).isNotNull(); 200 } 201 202 @Test 203 @Config(shadows = ShadowPreferenceFragmentCompat.class) onStart_shouldRegisterDynamicDataObservers()204 public void onStart_shouldRegisterDynamicDataObservers() { 205 final DynamicDataObserver observer = new TestDynamicDataObserver(); 206 mTestFragment.mDashboardTilePrefKeys.put("key", Arrays.asList(observer)); 207 208 mTestFragment.onStart(); 209 210 verify(mTestFragment.getContentResolver()).registerContentObserver(observer.getUri(), false, 211 observer); 212 } 213 214 @Test 215 @Config(shadows = ShadowPreferenceFragmentCompat.class) onStop_shouldUnregisterDynamicDataObservers()216 public void onStop_shouldUnregisterDynamicDataObservers() { 217 final DynamicDataObserver observer = new TestDynamicDataObserver(); 218 mTestFragment.registerDynamicDataObservers(Arrays.asList(observer)); 219 220 mTestFragment.onStop(); 221 222 verify(mTestFragment.getContentResolver()).unregisterContentObserver(observer); 223 } 224 225 @Test updateState_skipUnavailablePrefs()226 public void updateState_skipUnavailablePrefs() { 227 final List<AbstractPreferenceController> preferenceControllers = mTestFragment.mControllers; 228 final AbstractPreferenceController mockController1 = 229 mock(AbstractPreferenceController.class); 230 final AbstractPreferenceController mockController2 = 231 mock(AbstractPreferenceController.class); 232 when(mockController1.getPreferenceKey()).thenReturn("key1"); 233 when(mockController2.getPreferenceKey()).thenReturn("key2"); 234 preferenceControllers.add(mockController1); 235 preferenceControllers.add(mockController2); 236 when(mockController1.isAvailable()).thenReturn(false); 237 when(mockController2.isAvailable()).thenReturn(true); 238 mTestFragment.onAttach(RuntimeEnvironment.application); 239 mTestFragment.onResume(); 240 241 verify(mockController1).getPreferenceKey(); 242 verify(mockController2, times(2)).getPreferenceKey(); 243 } 244 245 @Test updateState_doesNotSkipControllersOfSameClass()246 public void updateState_doesNotSkipControllersOfSameClass() { 247 final AbstractPreferenceController mockController1 = 248 mock(AbstractPreferenceController.class); 249 final AbstractPreferenceController mockController2 = 250 mock(AbstractPreferenceController.class); 251 mTestFragment.addPreferenceController(mockController1); 252 mTestFragment.addPreferenceController(mockController2); 253 when(mockController1.isAvailable()).thenReturn(true); 254 when(mockController2.isAvailable()).thenReturn(true); 255 256 mTestFragment.updatePreferenceStates(); 257 258 verify(mockController1).getPreferenceKey(); 259 verify(mockController2).getPreferenceKey(); 260 } 261 262 @Test onExpandButtonClick_shouldLogAdvancedButtonExpand()263 public void onExpandButtonClick_shouldLogAdvancedButtonExpand() { 264 final MetricsFeatureProvider metricsFeatureProvider 265 = mFakeFeatureFactory.getMetricsFeatureProvider(); 266 mTestFragment.onExpandButtonClick(); 267 268 verify(metricsFeatureProvider).action(SettingsEnums.PAGE_UNKNOWN, 269 MetricsEvent.ACTION_SETTINGS_ADVANCED_BUTTON_EXPAND, 270 DASHBOARD_CONTAINER, null, 0); 271 } 272 273 @Test updatePreferenceVisibility_prefKeyNull_shouldNotCrash()274 public void updatePreferenceVisibility_prefKeyNull_shouldNotCrash() { 275 final Map<Class, List<AbstractPreferenceController>> prefControllers = new HashMap<>(); 276 final List<AbstractPreferenceController> controllerList = new ArrayList<>(); 277 controllerList.add(new TestPreferenceController(mContext)); 278 prefControllers.put(TestPreferenceController.class, controllerList); 279 mTestFragment.mBlockerController = new UiBlockerController(Arrays.asList("pref_key")); 280 281 // Should not crash 282 mTestFragment.updatePreferenceVisibility(prefControllers); 283 } 284 285 @Test checkUiBlocker_noUiBlocker_controllerIsNull()286 public void checkUiBlocker_noUiBlocker_controllerIsNull() { 287 mTestFragment.mBlockerController = null; 288 mControllers.add(new TestPreferenceController(mContext)); 289 290 mTestFragment.checkUiBlocker(mControllers); 291 292 assertThat(mTestFragment.mBlockerController).isNull(); 293 } 294 295 @Test checkUiBlocker_hasUiBlockerAndControllerIsAvailable_controllerNotNull()296 public void checkUiBlocker_hasUiBlockerAndControllerIsAvailable_controllerNotNull() { 297 final BlockingSlicePrefController controller = 298 new BlockingSlicePrefController(mContext, "pref_key"); 299 controller.setSliceUri(Uri.parse("testUri")); 300 mTestFragment.mBlockerController = null; 301 mControllers.add(new TestPreferenceController(mContext)); 302 mControllers.add(controller); 303 304 mTestFragment.checkUiBlocker(mControllers); 305 306 assertThat(mTestFragment.mBlockerController).isNotNull(); 307 } 308 309 @Test checkUiBlocker_hasUiBlockerAndControllerIsNotAvailable_controllerIsNull()310 public void checkUiBlocker_hasUiBlockerAndControllerIsNotAvailable_controllerIsNull() { 311 mTestFragment.mBlockerController = null; 312 mControllers.add(new TestPreferenceController(mContext)); 313 mControllers.add(new BlockingSlicePrefController(mContext, "pref_key")); 314 315 mTestFragment.checkUiBlocker(mControllers); 316 317 assertThat(mTestFragment.mBlockerController).isNull(); 318 } 319 320 @Test createPreference_isProviderTile_returnSwitchPreference()321 public void createPreference_isProviderTile_returnSwitchPreference() { 322 final Preference pref = mTestFragment.createPreference(mProviderTile); 323 324 assertThat(pref).isInstanceOf(SwitchPreference.class); 325 } 326 327 @Test createPreference_isActivityTileAndHasSwitch_returnPrimarySwitchPreference()328 public void createPreference_isActivityTileAndHasSwitch_returnPrimarySwitchPreference() { 329 mActivityTile.getMetaData().putString(META_DATA_PREFERENCE_SWITCH_URI, "uri"); 330 331 final Preference pref = mTestFragment.createPreference(mActivityTile); 332 333 assertThat(pref).isInstanceOf(PrimarySwitchPreference.class); 334 } 335 336 public static class TestPreferenceController extends AbstractPreferenceController 337 implements PreferenceControllerMixin { 338 TestPreferenceController(Context context)339 private TestPreferenceController(Context context) { 340 super(context); 341 } 342 343 @Override handlePreferenceTreeClick(Preference preference)344 public boolean handlePreferenceTreeClick(Preference preference) { 345 return false; 346 } 347 348 @Override isAvailable()349 public boolean isAvailable() { 350 return false; 351 } 352 353 @Override getPreferenceKey()354 public String getPreferenceKey() { 355 return null; 356 } 357 358 @Override updateNonIndexableKeys(List<String> keys)359 public void updateNonIndexableKeys(List<String> keys) { 360 } 361 } 362 363 private static class TestFragment extends DashboardFragment { 364 365 private final PreferenceManager mPreferenceManager; 366 private final Context mContext; 367 private final List<AbstractPreferenceController> mControllers; 368 private final ContentResolver mContentResolver; 369 370 public final PreferenceScreen mScreen; 371 private boolean mIsParalleled; 372 TestFragment(Context context)373 public TestFragment(Context context) { 374 mContext = context; 375 mPreferenceManager = mock(PreferenceManager.class); 376 mScreen = mock(PreferenceScreen.class); 377 mContentResolver = mock(ContentResolver.class); 378 mControllers = new ArrayList<>(); 379 mIsParalleled = true; 380 381 when(mPreferenceManager.getContext()).thenReturn(mContext); 382 ReflectionHelpers.setField( 383 this, "mVisibilityLoggerMixin", mock(VisibilityLoggerMixin.class)); 384 } 385 386 @Override getContext()387 public Context getContext() { 388 return mContext; 389 } 390 391 @Override getMetricsCategory()392 public int getMetricsCategory() { 393 return DASHBOARD_CONTAINER; 394 } 395 396 @Override getPreferenceScreen()397 public PreferenceScreen getPreferenceScreen() { 398 return mScreen; 399 } 400 401 @Override getLogTag()402 protected String getLogTag() { 403 return "TEST_FRAG"; 404 } 405 406 @Override getPreferenceScreenResId()407 protected int getPreferenceScreenResId() { 408 return 0; 409 } 410 411 @Override createPreferenceControllers(Context context)412 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 413 return mControllers; 414 } 415 416 @Override getPreferenceManager()417 public PreferenceManager getPreferenceManager() { 418 return mPreferenceManager; 419 } 420 421 @Override getContentResolver()422 protected ContentResolver getContentResolver() { 423 return mContentResolver; 424 } 425 isParalleledControllers()426 protected boolean isParalleledControllers() { 427 return mIsParalleled; 428 } 429 setUsingControllerEnhancement(boolean isParalleled)430 void setUsingControllerEnhancement(boolean isParalleled) { 431 mIsParalleled = isParalleled; 432 } 433 } 434 435 private static class TestDynamicDataObserver extends DynamicDataObserver { 436 437 @Override getUri()438 public Uri getUri() { 439 return Uri.parse("content://abc"); 440 } 441 442 @Override onDataChanged()443 public void onDataChanged() { 444 } 445 } 446 447 @Implements(PreferenceFragmentCompat.class) 448 public static class ShadowPreferenceFragmentCompat { 449 450 @Implementation onStart()451 public void onStart() { 452 // do nothing 453 } 454 455 @Implementation onStop()456 public void onStop() { 457 // do nothing 458 } 459 } 460 } 461