• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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