1 /*
2  * Copyright (C) 2017 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 
17 package com.android.settings.development;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.Mockito.doReturn;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.spy;
24 import static org.mockito.Mockito.verify;
25 import static org.mockito.Mockito.when;
26 
27 import android.content.Context;
28 import android.provider.SearchIndexableResource;
29 import android.provider.Settings;
30 
31 import androidx.appcompat.app.AlertDialog;
32 import androidx.fragment.app.FragmentActivity;
33 
34 import com.android.internal.logging.nano.MetricsProto;
35 import com.android.settings.R;
36 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
37 import com.android.settings.testutils.shadow.ShadowUserManager;
38 import com.android.settings.widget.SettingsMainSwitchBar;
39 import com.android.settingslib.development.AbstractEnableAdbPreferenceController;
40 import com.android.settingslib.development.DevelopmentSettingsEnabler;
41 
42 import org.junit.After;
43 import org.junit.Before;
44 import org.junit.Ignore;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.MockitoAnnotations;
48 import org.robolectric.RobolectricTestRunner;
49 import org.robolectric.RuntimeEnvironment;
50 import org.robolectric.annotation.Config;
51 import org.robolectric.annotation.Implementation;
52 import org.robolectric.annotation.Implements;
53 import org.robolectric.shadow.api.Shadow;
54 import org.robolectric.shadows.androidx.fragment.FragmentController;
55 import org.robolectric.util.ReflectionHelpers;
56 
57 import java.util.List;
58 
59 @RunWith(RobolectricTestRunner.class)
60 @Config(shadows = {
61         ShadowAlertDialogCompat.class,
62         ShadowUserManager.class,
63         ShadowUserManager.class,
64 })
65 public class DevelopmentSettingsDashboardFragmentTest {
66 
67     private Context mContext;
68     private ShadowUserManager mShadowUserManager;
69     private DevelopmentSettingsDashboardFragment mDashboard;
70 
71     @Before
setUp()72     public void setUp() {
73         MockitoAnnotations.initMocks(this);
74         mContext = RuntimeEnvironment.application;
75         SettingsMainSwitchBar switchBar = new SettingsMainSwitchBar(mContext);
76         mDashboard = spy(new DevelopmentSettingsDashboardFragment());
77         ReflectionHelpers.setField(mDashboard, "mSwitchBar", switchBar);
78         mShadowUserManager = Shadow.extract(mContext.getSystemService(Context.USER_SERVICE));
79         mShadowUserManager.setIsAdminUser(true);
80     }
81 
82     @After
tearDown()83     public void tearDown() {
84         ShadowEnableDevelopmentSettingWarningDialog.reset();
85     }
86 
87     @Test
shouldNotHaveHelpResource()88     public void shouldNotHaveHelpResource() {
89         assertThat(mDashboard.getHelpResource()).isEqualTo(0);
90     }
91 
92     @Test
shouldLogAsFeatureFlagPage()93     public void shouldLogAsFeatureFlagPage() {
94         assertThat(mDashboard.getMetricsCategory())
95                 .isEqualTo(MetricsProto.MetricsEvent.DEVELOPMENT);
96     }
97 
98     @Test
searchIndex_shouldIndexFromPrefXml()99     public void searchIndex_shouldIndexFromPrefXml() {
100         final List<SearchIndexableResource> index =
101                 DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER
102                         .getXmlResourcesToIndex(RuntimeEnvironment.application, true);
103 
104         assertThat(index.size()).isEqualTo(1);
105         assertThat(index.get(0).xmlResId).isEqualTo(R.xml.development_settings);
106     }
107 
108     @Test
109     @Ignore
searchIndex_pageDisabledBySetting_shouldAddAllKeysToNonIndexable()110     public void searchIndex_pageDisabledBySetting_shouldAddAllKeysToNonIndexable() {
111         final Context appContext = RuntimeEnvironment.application;
112         DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, false);
113 
114         final List<String> nonIndexableKeys =
115                 DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER
116                         .getNonIndexableKeys(appContext);
117 
118         assertThat(nonIndexableKeys).contains("enable_adb");
119     }
120 
121     @Test
122     @Ignore
searchIndex_pageDisabledForNonAdmin_shouldAddAllKeysToNonIndexable()123     public void searchIndex_pageDisabledForNonAdmin_shouldAddAllKeysToNonIndexable() {
124         final Context appContext = RuntimeEnvironment.application;
125         DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, true);
126         mShadowUserManager.setIsAdminUser(false);
127         mShadowUserManager.setIsDemoUser(false);
128 
129         final List<String> nonIndexableKeys =
130                 DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER
131                         .getNonIndexableKeys(appContext);
132 
133         assertThat(nonIndexableKeys).contains("enable_adb");
134     }
135 
136     @Test
137     @Ignore
138     @Config(shadows = {
139             ShadowPictureColorModePreferenceController.class,
140             ShadowAdbPreferenceController.class,
141             ShadowClearAdbKeysPreferenceController.class,
142             ShadowWirelessDebuggingPreferenceController.class
143     })
searchIndex_pageEnabled_shouldNotAddKeysToNonIndexable()144     public void searchIndex_pageEnabled_shouldNotAddKeysToNonIndexable() {
145         final Context appContext = RuntimeEnvironment.application;
146         DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, true);
147 
148         final List<String> nonIndexableKeys =
149                 DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER
150                         .getNonIndexableKeys(appContext);
151 
152         assertThat(nonIndexableKeys).doesNotContain("development_prefs_screen");
153     }
154 
155     @Test
156     @Ignore
157     @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
onSwitchChanged_sameState_shouldDoNothing()158     public void onSwitchChanged_sameState_shouldDoNothing() {
159         when(mDashboard.getContext()).thenReturn(mContext);
160         Settings.Global.putInt(mContext.getContentResolver(),
161                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
162 
163         mDashboard.onCheckedChanged(null, false /* isChecked */);
164         assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isFalse();
165     }
166 
167     @Test
168     @Ignore
169     @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
onSwitchChanged_turnOn_shouldShowWarningDialog()170     public void onSwitchChanged_turnOn_shouldShowWarningDialog() {
171         when(mDashboard.getContext()).thenReturn(mContext);
172         Settings.Global.putInt(mContext.getContentResolver(),
173                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
174 
175         mDashboard.onCheckedChanged(null, true /* isChecked */);
176         assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isTrue();
177     }
178 
179     @Test
180     @Ignore
181     @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class)
onSwitchChanged_turnOff_shouldTurnOff()182     public void onSwitchChanged_turnOff_shouldTurnOff() {
183         when(mDashboard.getContext()).thenReturn(mContext);
184         Settings.Global.putInt(mContext.getContentResolver(),
185                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
186 
187         mDashboard.onCheckedChanged(null, false /* isChecked */);
188 
189         assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isFalse();
190         assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
191     }
192 
193     @Test
194     @Ignore
195     @Config(shadows = ShadowDisableDevSettingsDialogFragment.class)
onSwitchChanged_turnOff_andOffloadIsNotDefaultValue_shouldShowWarningDialog()196     public void onSwitchChanged_turnOff_andOffloadIsNotDefaultValue_shouldShowWarningDialog() {
197         final BluetoothA2dpHwOffloadPreferenceController controller =
198                 mock(BluetoothA2dpHwOffloadPreferenceController.class);
199         when(mDashboard.getContext()).thenReturn(mContext);
200         when(mDashboard.getDevelopmentOptionsController(
201                 BluetoothA2dpHwOffloadPreferenceController.class)).thenReturn(controller);
202         when(controller.isDefaultValue()).thenReturn(false);
203         Settings.Global.putInt(mContext.getContentResolver(),
204                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
205 
206         mDashboard.onCheckedChanged(null, false /* isChecked */);
207 
208         AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
209         assertThat(dialog).isNotNull();
210         ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
211         assertThat(shadowDialog.getTitle()).isEqualTo(
212                 mContext.getString(R.string.bluetooth_disable_hw_offload_dialog_title));
213         assertThat(shadowDialog.getMessage()).isEqualTo(
214                 mContext.getString(R.string.bluetooth_disable_hw_offload_dialog_message));
215     }
216 
217     @Test
onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed()218     public void onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed() {
219         final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class);
220         doReturn(controller).when(mDashboard)
221                 .getDevelopmentOptionsController(OemUnlockPreferenceController.class);
222         mDashboard.onOemUnlockDialogConfirmed();
223         verify(controller).onOemUnlockConfirmed();
224     }
225 
226     @Test
onOemUnlockDialogConfirmed_shouldCallControllerOemDismissed()227     public void onOemUnlockDialogConfirmed_shouldCallControllerOemDismissed() {
228         final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class);
229         doReturn(controller).when(mDashboard)
230                 .getDevelopmentOptionsController(OemUnlockPreferenceController.class);
231         mDashboard.onOemUnlockDialogDismissed();
232         verify(controller).onOemUnlockDismissed();
233     }
234 
235     @Test
onAdbDialogConfirmed_shouldCallControllerDialogConfirmed()236     public void onAdbDialogConfirmed_shouldCallControllerDialogConfirmed() {
237         final AdbPreferenceController controller = mock(AdbPreferenceController.class);
238         doReturn(controller).when(mDashboard)
239                 .getDevelopmentOptionsController(AdbPreferenceController.class);
240         mDashboard.onEnableAdbDialogConfirmed();
241 
242         verify(controller).onAdbDialogConfirmed();
243     }
244 
245     @Test
onAdbDialogDismissed_shouldCallControllerOemDismissed()246     public void onAdbDialogDismissed_shouldCallControllerOemDismissed() {
247         final AdbPreferenceController controller = mock(AdbPreferenceController.class);
248         doReturn(controller).when(mDashboard)
249                 .getDevelopmentOptionsController(AdbPreferenceController.class);
250         mDashboard.onEnableAdbDialogDismissed();
251 
252         verify(controller).onAdbDialogDismissed();
253     }
254 
255     @Test
onAdbClearKeysDialogConfirmed_shouldCallControllerDialogConfirmed()256     public void onAdbClearKeysDialogConfirmed_shouldCallControllerDialogConfirmed() {
257         final ClearAdbKeysPreferenceController controller =
258                 mock(ClearAdbKeysPreferenceController.class);
259         doReturn(controller).when(mDashboard)
260                 .getDevelopmentOptionsController(ClearAdbKeysPreferenceController.class);
261         mDashboard.onAdbClearKeysDialogConfirmed();
262 
263         verify(controller).onClearAdbKeysConfirmed();
264     }
265 
266     @Test
onDisableLogPersistDialogConfirmed_shouldCallControllerDialogConfirmed()267     public void onDisableLogPersistDialogConfirmed_shouldCallControllerDialogConfirmed() {
268         final LogPersistPreferenceController controller =
269                 mock(LogPersistPreferenceController.class);
270         doReturn(controller).when(mDashboard)
271                 .getDevelopmentOptionsController(LogPersistPreferenceController.class);
272         mDashboard.onDisableLogPersistDialogConfirmed();
273 
274         verify(controller).onDisableLogPersistDialogConfirmed();
275     }
276 
277     @Test
onDisableLogPersistDialogRejected_shouldCallControllerDialogRejected()278     public void onDisableLogPersistDialogRejected_shouldCallControllerDialogRejected() {
279         final LogPersistPreferenceController controller =
280                 mock(LogPersistPreferenceController.class);
281         doReturn(controller).when(mDashboard)
282                 .getDevelopmentOptionsController(LogPersistPreferenceController.class);
283         mDashboard.onDisableLogPersistDialogRejected();
284 
285         verify(controller).onDisableLogPersistDialogRejected();
286     }
287 
288     @Test
shouldSkipForInitialSUW_returnTrue()289     public void shouldSkipForInitialSUW_returnTrue() {
290         assertThat(mDashboard.shouldSkipForInitialSUW()).isTrue();
291     }
292 
293     @Implements(EnableDevelopmentSettingWarningDialog.class)
294     public static class ShadowEnableDevelopmentSettingWarningDialog {
295 
296         static boolean mShown;
297 
reset()298         public static void reset() {
299             mShown = false;
300         }
301 
302         @Implementation
show(DevelopmentSettingsDashboardFragment host)303         protected static void show(DevelopmentSettingsDashboardFragment host) {
304             mShown = true;
305         }
306     }
307 
308     @Implements(DisableDevSettingsDialogFragment.class)
309     public static class ShadowDisableDevSettingsDialogFragment {
310 
311         @Implementation
show(DevelopmentSettingsDashboardFragment host)312         public static void show(DevelopmentSettingsDashboardFragment host) {
313             DisableDevSettingsDialogFragment mFragment =
314                     spy(DisableDevSettingsDialogFragment.newInstance());
315             FragmentController.setupFragment(mFragment, FragmentActivity.class,
316                     0 /* containerViewId */, null /* bundle */);
317         }
318     }
319 
320     @Implements(PictureColorModePreferenceController.class)
321     public static class ShadowPictureColorModePreferenceController {
322         @Implementation
isAvailable()323         protected boolean isAvailable() {
324             return true;
325         }
326     }
327 
328     @Implements(AbstractEnableAdbPreferenceController.class)
329     public static class ShadowAdbPreferenceController {
330         @Implementation
isAvailable()331         protected boolean isAvailable() {
332             return true;
333         }
334     }
335 
336     @Implements(ClearAdbKeysPreferenceController.class)
337     public static class ShadowClearAdbKeysPreferenceController {
338         @Implementation
isAvailable()339         protected boolean isAvailable() {
340             return true;
341         }
342     }
343 
344     @Implements(WirelessDebuggingPreferenceController.class)
345     public static class ShadowWirelessDebuggingPreferenceController {
346         @Implementation
isAvailable()347         protected boolean isAvailable() {
348             return true;
349         }
350     }
351 }
352