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 108 @Ignore searchIndex_pageDisabledBySetting_shouldAddAllKeysToNonIndexable()109 public void searchIndex_pageDisabledBySetting_shouldAddAllKeysToNonIndexable() { 110 final Context appContext = RuntimeEnvironment.application; 111 DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, false); 112 113 final List<String> nonIndexableKeys = 114 DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER 115 .getNonIndexableKeys(appContext); 116 117 assertThat(nonIndexableKeys).contains("enable_adb"); 118 } 119 120 @Test 121 @Ignore searchIndex_pageDisabledForNonAdmin_shouldAddAllKeysToNonIndexable()122 public void searchIndex_pageDisabledForNonAdmin_shouldAddAllKeysToNonIndexable() { 123 final Context appContext = RuntimeEnvironment.application; 124 DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, true); 125 mShadowUserManager.setIsAdminUser(false); 126 mShadowUserManager.setIsDemoUser(false); 127 128 final List<String> nonIndexableKeys = 129 DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER 130 .getNonIndexableKeys(appContext); 131 132 assertThat(nonIndexableKeys).contains("enable_adb"); 133 } 134 135 @Test 136 @Ignore 137 @Config(shadows = { 138 ShadowPictureColorModePreferenceController.class, 139 ShadowAdbPreferenceController.class, 140 ShadowClearAdbKeysPreferenceController.class, 141 ShadowWirelessDebuggingPreferenceController.class 142 }) searchIndex_pageEnabled_shouldNotAddKeysToNonIndexable()143 public void searchIndex_pageEnabled_shouldNotAddKeysToNonIndexable() { 144 final Context appContext = RuntimeEnvironment.application; 145 DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, true); 146 147 final List<String> nonIndexableKeys = 148 DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER 149 .getNonIndexableKeys(appContext); 150 151 assertThat(nonIndexableKeys).doesNotContain("development_prefs_screen"); 152 } 153 154 @Test 155 @Ignore 156 @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class) onSwitchChanged_sameState_shouldDoNothing()157 public void onSwitchChanged_sameState_shouldDoNothing() { 158 when(mDashboard.getContext()).thenReturn(mContext); 159 Settings.Global.putInt(mContext.getContentResolver(), 160 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0); 161 162 mDashboard.onSwitchChanged(mSwitch, false /* isChecked */); 163 assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isFalse(); 164 } 165 166 @Test 167 @Ignore 168 @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class) onSwitchChanged_turnOn_shouldShowWarningDialog()169 public void onSwitchChanged_turnOn_shouldShowWarningDialog() { 170 when(mDashboard.getContext()).thenReturn(mContext); 171 Settings.Global.putInt(mContext.getContentResolver(), 172 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0); 173 174 mDashboard.onSwitchChanged(mSwitch, true /* isChecked */); 175 assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isTrue(); 176 } 177 178 @Test 179 @Ignore 180 @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class) onSwitchChanged_turnOff_shouldTurnOff()181 public void onSwitchChanged_turnOff_shouldTurnOff() { 182 when(mDashboard.getContext()).thenReturn(mContext); 183 Settings.Global.putInt(mContext.getContentResolver(), 184 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1); 185 186 mDashboard.onSwitchChanged(mSwitch, false /* isChecked */); 187 188 assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isFalse(); 189 assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse(); 190 } 191 192 @Test 193 @Ignore 194 @Config(shadows = ShadowDisableDevSettingsDialogFragment.class) onSwitchChanged_turnOff_andOffloadIsNotDefaultValue_shouldShowWarningDialog()195 public void onSwitchChanged_turnOff_andOffloadIsNotDefaultValue_shouldShowWarningDialog() { 196 final BluetoothA2dpHwOffloadPreferenceController controller = 197 mock(BluetoothA2dpHwOffloadPreferenceController.class); 198 when(mDashboard.getContext()).thenReturn(mContext); 199 when(mDashboard.getDevelopmentOptionsController( 200 BluetoothA2dpHwOffloadPreferenceController.class)).thenReturn(controller); 201 when(controller.isDefaultValue()).thenReturn(false); 202 Settings.Global.putInt(mContext.getContentResolver(), 203 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1); 204 205 mDashboard.onSwitchChanged(mSwitch, false /* isChecked */); 206 207 AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); 208 assertThat(dialog).isNotNull(); 209 ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog); 210 assertThat(shadowDialog.getTitle()).isEqualTo( 211 mContext.getString(R.string.bluetooth_disable_hw_offload_dialog_title)); 212 assertThat(shadowDialog.getMessage()).isEqualTo( 213 mContext.getString(R.string.bluetooth_disable_hw_offload_dialog_message)); 214 } 215 216 @Test onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed()217 public void onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed() { 218 final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class); 219 doReturn(controller).when(mDashboard) 220 .getDevelopmentOptionsController(OemUnlockPreferenceController.class); 221 mDashboard.onOemUnlockDialogConfirmed(); 222 verify(controller).onOemUnlockConfirmed(); 223 } 224 225 @Test onOemUnlockDialogConfirmed_shouldCallControllerOemDismissed()226 public void onOemUnlockDialogConfirmed_shouldCallControllerOemDismissed() { 227 final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class); 228 doReturn(controller).when(mDashboard) 229 .getDevelopmentOptionsController(OemUnlockPreferenceController.class); 230 mDashboard.onOemUnlockDialogDismissed(); 231 verify(controller).onOemUnlockDismissed(); 232 } 233 234 @Test onAdbDialogConfirmed_shouldCallControllerDialogConfirmed()235 public void onAdbDialogConfirmed_shouldCallControllerDialogConfirmed() { 236 final AdbPreferenceController controller = mock(AdbPreferenceController.class); 237 doReturn(controller).when(mDashboard) 238 .getDevelopmentOptionsController(AdbPreferenceController.class); 239 mDashboard.onEnableAdbDialogConfirmed(); 240 241 verify(controller).onAdbDialogConfirmed(); 242 } 243 244 @Test onAdbDialogDismissed_shouldCallControllerOemDismissed()245 public void onAdbDialogDismissed_shouldCallControllerOemDismissed() { 246 final AdbPreferenceController controller = mock(AdbPreferenceController.class); 247 doReturn(controller).when(mDashboard) 248 .getDevelopmentOptionsController(AdbPreferenceController.class); 249 mDashboard.onEnableAdbDialogDismissed(); 250 251 verify(controller).onAdbDialogDismissed(); 252 } 253 254 @Test onAdbClearKeysDialogConfirmed_shouldCallControllerDialogConfirmed()255 public void onAdbClearKeysDialogConfirmed_shouldCallControllerDialogConfirmed() { 256 final ClearAdbKeysPreferenceController controller = 257 mock(ClearAdbKeysPreferenceController.class); 258 doReturn(controller).when(mDashboard) 259 .getDevelopmentOptionsController(ClearAdbKeysPreferenceController.class); 260 mDashboard.onAdbClearKeysDialogConfirmed(); 261 262 verify(controller).onClearAdbKeysConfirmed(); 263 } 264 265 @Test onDisableLogPersistDialogConfirmed_shouldCallControllerDialogConfirmed()266 public void onDisableLogPersistDialogConfirmed_shouldCallControllerDialogConfirmed() { 267 final LogPersistPreferenceController controller = 268 mock(LogPersistPreferenceController.class); 269 doReturn(controller).when(mDashboard) 270 .getDevelopmentOptionsController(LogPersistPreferenceController.class); 271 mDashboard.onDisableLogPersistDialogConfirmed(); 272 273 verify(controller).onDisableLogPersistDialogConfirmed(); 274 } 275 276 @Test onDisableLogPersistDialogRejected_shouldCallControllerDialogRejected()277 public void onDisableLogPersistDialogRejected_shouldCallControllerDialogRejected() { 278 final LogPersistPreferenceController controller = 279 mock(LogPersistPreferenceController.class); 280 doReturn(controller).when(mDashboard) 281 .getDevelopmentOptionsController(LogPersistPreferenceController.class); 282 mDashboard.onDisableLogPersistDialogRejected(); 283 284 verify(controller).onDisableLogPersistDialogRejected(); 285 } 286 287 @Test shouldSkipForInitialSUW_returnTrue()288 public void shouldSkipForInitialSUW_returnTrue() { 289 assertThat(mDashboard.shouldSkipForInitialSUW()).isTrue(); 290 } 291 292 @Implements(EnableDevelopmentSettingWarningDialog.class) 293 public static class ShadowEnableDevelopmentSettingWarningDialog { 294 295 static boolean mShown; 296 reset()297 public static void reset() { 298 mShown = false; 299 } 300 301 @Implementation show(DevelopmentSettingsDashboardFragment host)302 protected static void show(DevelopmentSettingsDashboardFragment host) { 303 mShown = true; 304 } 305 } 306 307 @Implements(DisableDevSettingsDialogFragment.class) 308 public static class ShadowDisableDevSettingsDialogFragment { 309 310 @Implementation show(DevelopmentSettingsDashboardFragment host)311 public static void show(DevelopmentSettingsDashboardFragment host) { 312 DisableDevSettingsDialogFragment mFragment = 313 spy(DisableDevSettingsDialogFragment.newInstance()); 314 FragmentController.setupFragment(mFragment, FragmentActivity.class, 315 0 /* containerViewId */, null /* bundle */); 316 } 317 } 318 319 @Implements(PictureColorModePreferenceController.class) 320 public static class ShadowPictureColorModePreferenceController { 321 @Implementation isAvailable()322 protected boolean isAvailable() { 323 return true; 324 } 325 } 326 327 @Implements(AbstractEnableAdbPreferenceController.class) 328 public static class ShadowAdbPreferenceController { 329 @Implementation isAvailable()330 protected boolean isAvailable() { 331 return true; 332 } 333 } 334 335 @Implements(ClearAdbKeysPreferenceController.class) 336 public static class ShadowClearAdbKeysPreferenceController { 337 @Implementation isAvailable()338 protected boolean isAvailable() { 339 return true; 340 } 341 } 342 343 @Implements(WirelessDebuggingPreferenceController.class) 344 public static class ShadowWirelessDebuggingPreferenceController { 345 @Implementation isAvailable()346 protected boolean isAvailable() { 347 return true; 348 } 349 } 350 } 351