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