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