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