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